comparison src/station_cmd.cpp @ 5584:4b26bd55bd24 draft

(svn r8033) [cpp] - Prepare for merge from branches/cpp (all .c files renamed to .cpp)
author KUDr <KUDr@openttd.org>
date Wed, 10 Jan 2007 18:12:09 +0000
parents
children c44c070c5032
comparison
equal deleted inserted replaced
5583:2d57664077ee 5584:4b26bd55bd24
1 /* $Id$ */
2
3 /** @file station_cmd.c */
4
5 #include "stdafx.h"
6 #include "openttd.h"
7 #include "bridge_map.h"
8 #include "debug.h"
9 #include "functions.h"
10 #include "station_map.h"
11 #include "table/sprites.h"
12 #include "table/strings.h"
13 #include "map.h"
14 #include "tile.h"
15 #include "station.h"
16 #include "gfx.h"
17 #include "window.h"
18 #include "viewport.h"
19 #include "command.h"
20 #include "town.h"
21 #include "vehicle.h"
22 #include "news.h"
23 #include "saveload.h"
24 #include "economy.h"
25 #include "player.h"
26 #include "airport.h"
27 #include "sprite.h"
28 #include "depot.h"
29 #include "train.h"
30 #include "water_map.h"
31 #include "industry_map.h"
32 #include "newgrf_callbacks.h"
33 #include "newgrf_station.h"
34 #include "yapf/yapf.h"
35 #include "date.h"
36
37 typedef enum StationRectModes
38 {
39 RECT_MODE_TEST = 0,
40 RECT_MODE_TRY,
41 RECT_MODE_FORCE
42 } StationRectMode;
43
44 static void StationRect_Init(Station *st);
45 static bool StationRect_IsEmpty(Station *st);
46 static bool StationRect_BeforeAddTile(Station *st, TileIndex tile, StationRectMode mode);
47 static bool StationRect_BeforeAddRect(Station *st, TileIndex tile, int w, int h, StationRectMode mode);
48 static bool StationRect_AfterRemoveTile(Station *st, TileIndex tile);
49 static bool StationRect_AfterRemoveRect(Station *st, TileIndex tile, int w, int h);
50
51
52 /**
53 * Called if a new block is added to the station-pool
54 */
55 static void StationPoolNewBlock(uint start_item)
56 {
57 Station *st;
58
59 /* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
60 * TODO - This is just a temporary stage, this will be removed. */
61 for (st = GetStation(start_item); st != NULL; st = (st->index + 1U < GetStationPoolSize()) ? GetStation(st->index + 1U) : NULL) st->index = start_item++;
62 }
63
64 static void StationPoolCleanBlock(uint start_item, uint end_item)
65 {
66 uint i;
67
68 for (i = start_item; i <= end_item; i++) {
69 Station *st = GetStation(i);
70 free(st->speclist);
71 st->speclist = NULL;
72 }
73 }
74
75 /**
76 * Called if a new block is added to the roadstop-pool
77 */
78 static void RoadStopPoolNewBlock(uint start_item)
79 {
80 RoadStop *rs;
81
82 /* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
83 * TODO - This is just a temporary stage, this will be removed. */
84 for (rs = GetRoadStop(start_item); rs != NULL; rs = (rs->index + 1U < GetRoadStopPoolSize()) ? GetRoadStop(rs->index + 1U) : NULL) rs->index = start_item++;
85 }
86
87 DEFINE_OLD_POOL(Station, Station, StationPoolNewBlock, StationPoolCleanBlock)
88 DEFINE_OLD_POOL(RoadStop, RoadStop, RoadStopPoolNewBlock, NULL)
89
90
91 extern void UpdateAirplanesOnNewStation(Station *st);
92
93 static bool TileBelongsToRailStation(const Station *st, TileIndex tile)
94 {
95 return IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st->index && IsRailwayStation(tile);
96 }
97
98 void MarkStationTilesDirty(const Station *st)
99 {
100 TileIndex tile = st->train_tile;
101 int w, h;
102
103 // XXX No station is recorded as 0, not INVALID_TILE...
104 if (tile == 0) return;
105
106 for (h = 0; h < st->trainst_h; h++) {
107 for (w = 0; w < st->trainst_w; w++) {
108 if (TileBelongsToRailStation(st, tile)) {
109 MarkTileDirtyByTile(tile);
110 }
111 tile += TileDiffXY(1, 0);
112 }
113 tile += TileDiffXY(-w, 1);
114 }
115 }
116
117 static void MarkStationDirty(const Station* st)
118 {
119 if (st->sign.width_1 != 0) {
120 InvalidateWindowWidget(WC_STATION_VIEW, st->index, 1);
121
122 MarkAllViewportsDirty(
123 st->sign.left - 6,
124 st->sign.top,
125 st->sign.left + (st->sign.width_1 << 2) + 12,
126 st->sign.top + 48);
127 }
128 }
129
130 static void InitializeRoadStop(RoadStop *road_stop, RoadStop *previous, TileIndex tile, StationID index)
131 {
132 road_stop->xy = tile;
133 road_stop->used = true;
134 road_stop->status = 3; //stop is free
135 road_stop->next = NULL;
136 road_stop->prev = previous;
137 road_stop->station = index;
138 road_stop->num_vehicles = 0;
139 }
140
141 RoadStop* GetPrimaryRoadStop(const Station* st, RoadStopType type)
142 {
143 switch (type) {
144 case RS_BUS: return st->bus_stops;
145 case RS_TRUCK: return st->truck_stops;
146 default: NOT_REACHED();
147 }
148
149 return NULL;
150 }
151
152 RoadStop* GetRoadStopByTile(TileIndex tile, RoadStopType type)
153 {
154 const Station* st = GetStationByTile(tile);
155 RoadStop* rs;
156
157 for (rs = GetPrimaryRoadStop(st, type); rs->xy != tile; rs = rs->next) {
158 assert(rs->next != NULL);
159 }
160
161 return rs;
162 }
163
164 uint GetNumRoadStopsInStation(const Station* st, RoadStopType type)
165 {
166 uint num = 0;
167 const RoadStop *rs;
168
169 assert(st != NULL);
170 for (rs = GetPrimaryRoadStop(st, type); rs != NULL; rs = rs->next) num++;
171
172 return num;
173 }
174
175 RoadStop *AllocateRoadStop(void)
176 {
177 RoadStop *rs;
178
179 /* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
180 * TODO - This is just a temporary stage, this will be removed. */
181 for (rs = GetRoadStop(0); rs != NULL; rs = (rs->index + 1U < GetRoadStopPoolSize()) ? GetRoadStop(rs->index + 1U) : NULL) {
182 if (!IsValidRoadStop(rs)) {
183 RoadStopID index = rs->index;
184
185 memset(rs, 0, sizeof(*rs));
186 rs->index = index;
187
188 return rs;
189 }
190 }
191
192 /* Check if we can add a block to the pool */
193 if (AddBlockToPool(&_RoadStop_pool)) return AllocateRoadStop();
194
195 return NULL;
196 }
197
198 /* Calculate the radius of the station. Basicly it is the biggest
199 * radius that is available within the station */
200 static uint FindCatchmentRadius(const Station* st)
201 {
202 uint ret = 0;
203
204 if (st->bus_stops != NULL) ret = max(ret, CA_BUS);
205 if (st->truck_stops != NULL) ret = max(ret, CA_TRUCK);
206 if (st->train_tile) ret = max(ret, CA_TRAIN);
207 if (st->dock_tile) ret = max(ret, CA_DOCK);
208
209 if (st->airport_tile) {
210 switch (st->airport_type) {
211 case AT_OILRIG: ret = max(ret, CA_AIR_OILPAD); break;
212 case AT_SMALL: ret = max(ret, CA_AIR_SMALL); break;
213 case AT_HELIPORT: ret = max(ret, CA_AIR_HELIPORT); break;
214 case AT_LARGE: ret = max(ret, CA_AIR_LARGE); break;
215 case AT_METROPOLITAN: ret = max(ret, CA_AIR_METRO); break;
216 case AT_INTERNATIONAL: ret = max(ret, CA_AIR_INTER); break;
217 case AT_COMMUTER: ret = max(ret, CA_AIR_COMMUTER); break;
218 case AT_HELIDEPOT: ret = max(ret, CA_AIR_HELIDEPOT); break;
219 case AT_INTERCON: ret = max(ret, CA_AIR_INTERCON); break;
220 case AT_HELISTATION: ret = max(ret, CA_AIR_HELISTATION); break;
221 }
222 }
223
224 return ret;
225 }
226
227 #define CHECK_STATIONS_ERR ((Station*)-1)
228
229 static Station* GetStationAround(TileIndex tile, int w, int h, StationID closest_station)
230 {
231 // check around to see if there's any stations there
232 BEGIN_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TileDiffXY(1, 1))
233 if (IsTileType(tile_cur, MP_STATION)) {
234 StationID t = GetStationIndex(tile_cur);
235 {
236 Station *st = GetStation(t);
237 // you cannot take control of an oilrig!!
238 if (st->airport_type == AT_OILRIG && st->facilities == (FACIL_AIRPORT|FACIL_DOCK))
239 continue;
240 }
241
242 if (closest_station == INVALID_STATION) {
243 closest_station = t;
244 } else if (closest_station != t) {
245 _error_message = STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING;
246 return CHECK_STATIONS_ERR;
247 }
248 }
249 END_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TileDiffXY(1, 1))
250 return (closest_station == INVALID_STATION) ? NULL : GetStation(closest_station);
251 }
252
253 static Station *AllocateStation(void)
254 {
255 Station *st = NULL;
256
257 /* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
258 * TODO - This is just a temporary stage, this will be removed. */
259 for (st = GetStation(0); st != NULL; st = (st->index + 1U < GetStationPoolSize()) ? GetStation(st->index + 1U) : NULL) {
260 if (!IsValidStation(st)) {
261 StationID index = st->index;
262
263 memset(st, 0, sizeof(Station));
264 st->index = index;
265
266 return st;
267 }
268 }
269
270 /* Check if we can add a block to the pool */
271 if (AddBlockToPool(&_Station_pool)) return AllocateStation();
272
273 _error_message = STR_3008_TOO_MANY_STATIONS_LOADING;
274 return NULL;
275 }
276
277
278 /**
279 * Counts the numbers of tiles matching a specific type in the area around
280 * @param tile the center tile of the 'count area'
281 * @param type the type of tile searched for
282 * @param industry when type == MP_INDUSTRY, the type of the industry,
283 * in all other cases this parameter is ignored
284 * @result the noumber of matching tiles around
285 */
286 static int CountMapSquareAround(TileIndex tile, TileType type, IndustryType industry)
287 {
288 TileIndex cur_tile;
289 int dx, dy;
290 int num = 0;
291
292 for (dx = -3; dx <= 3; dx++) {
293 for (dy = -3; dy <= 3; dy++) {
294 cur_tile = TILE_MASK(tile + TileDiffXY(dx, dy));
295
296 if (IsTileType(cur_tile, type)) {
297 switch (type) {
298 case MP_INDUSTRY:
299 if (GetIndustryType(cur_tile) == industry)
300 num++;
301 break;
302
303 case MP_WATER:
304 if (!IsWater(cur_tile))
305 break;
306 /* FALL THROUGH WHEN WATER TILE */
307 case MP_TREES:
308 num++;
309 break;
310
311 default:
312 break;
313 }
314 }
315 }
316 }
317
318 return num;
319 }
320
321 #define M(x) ((x) - STR_SV_STNAME)
322
323 static bool GenerateStationName(Station *st, TileIndex tile, int flag)
324 {
325 static const uint32 _gen_station_name_bits[] = {
326 0, /* 0 */
327 1 << M(STR_SV_STNAME_AIRPORT), /* 1 */
328 1 << M(STR_SV_STNAME_OILFIELD), /* 2 */
329 1 << M(STR_SV_STNAME_DOCKS), /* 3 */
330 0x1FF << M(STR_SV_STNAME_BUOY_1), /* 4 */
331 1 << M(STR_SV_STNAME_HELIPORT), /* 5 */
332 };
333
334 Town *t = st->town;
335 uint32 free_names = (uint32)-1;
336 int found;
337 uint z,z2;
338 unsigned long tmp;
339
340 {
341 Station *s;
342
343 FOR_ALL_STATIONS(s) {
344 if (s != st && s->town==t) {
345 uint str = M(s->string_id);
346 if (str <= 0x20) {
347 if (str == M(STR_SV_STNAME_FOREST))
348 str = M(STR_SV_STNAME_WOODS);
349 CLRBIT(free_names, str);
350 }
351 }
352 }
353 }
354
355 /* check default names */
356 tmp = free_names & _gen_station_name_bits[flag];
357 if (tmp != 0) {
358 found = FindFirstBit(tmp);
359 goto done;
360 }
361
362 /* check mine? */
363 if (HASBIT(free_names, M(STR_SV_STNAME_MINES))) {
364 if (CountMapSquareAround(tile, MP_INDUSTRY, IT_COAL_MINE) >= 2 ||
365 CountMapSquareAround(tile, MP_INDUSTRY, IT_IRON_MINE) >= 2 ||
366 CountMapSquareAround(tile, MP_INDUSTRY, IT_COPPER_MINE) >= 2 ||
367 CountMapSquareAround(tile, MP_INDUSTRY, IT_GOLD_MINE) >= 2 ||
368 CountMapSquareAround(tile, MP_INDUSTRY, IT_DIAMOND_MINE) >= 2) {
369 found = M(STR_SV_STNAME_MINES);
370 goto done;
371 }
372 }
373
374 /* check close enough to town to get central as name? */
375 if (DistanceMax(tile,t->xy) < 8) {
376 found = M(STR_SV_STNAME);
377 if (HASBIT(free_names, M(STR_SV_STNAME))) goto done;
378
379 found = M(STR_SV_STNAME_CENTRAL);
380 if (HASBIT(free_names, M(STR_SV_STNAME_CENTRAL))) goto done;
381 }
382
383 /* Check lakeside */
384 if (HASBIT(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
385 DistanceFromEdge(tile) < 20 &&
386 CountMapSquareAround(tile, MP_WATER, 0) >= 5) {
387 found = M(STR_SV_STNAME_LAKESIDE);
388 goto done;
389 }
390
391 /* Check woods */
392 if (HASBIT(free_names, M(STR_SV_STNAME_WOODS)) && (
393 CountMapSquareAround(tile, MP_TREES, 0) >= 8 ||
394 CountMapSquareAround(tile, MP_INDUSTRY, IT_FOREST) >= 2)
395 ) {
396 found = _opt.landscape == LT_DESERT ?
397 M(STR_SV_STNAME_FOREST) : M(STR_SV_STNAME_WOODS);
398 goto done;
399 }
400
401 /* check elevation compared to town */
402 z = GetTileZ(tile);
403 z2 = GetTileZ(t->xy);
404 if (z < z2) {
405 found = M(STR_SV_STNAME_VALLEY);
406 if (HASBIT(free_names, M(STR_SV_STNAME_VALLEY))) goto done;
407 } else if (z > z2) {
408 found = M(STR_SV_STNAME_HEIGHTS);
409 if (HASBIT(free_names, M(STR_SV_STNAME_HEIGHTS))) goto done;
410 }
411
412 /* check direction compared to town */
413 {
414 static const int8 _direction_and_table[] = {
415 ~( (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_EAST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
416 ~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
417 ~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_EAST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
418 ~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_EAST)) ),
419 };
420
421 free_names &= _direction_and_table[
422 (TileX(tile) < TileX(t->xy)) +
423 (TileY(tile) < TileY(t->xy)) * 2];
424 }
425
426 tmp = free_names & ((1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<6)|(1<<7)|(1<<12)|(1<<26)|(1<<27)|(1<<28)|(1<<29)|(1<<30));
427 if (tmp == 0) {
428 _error_message = STR_3007_TOO_MANY_STATIONS_LOADING;
429 return false;
430 }
431 found = FindFirstBit(tmp);
432
433 done:
434 st->string_id = found + STR_SV_STNAME;
435 return true;
436 }
437 #undef M
438
439 static Station* GetClosestStationFromTile(TileIndex tile, uint threshold, PlayerID owner)
440 {
441 Station* best_station = NULL;
442 Station* st;
443
444 FOR_ALL_STATIONS(st) {
445 if ((owner == PLAYER_SPECTATOR || st->owner == owner)) {
446 uint cur_dist = DistanceManhattan(tile, st->xy);
447
448 if (cur_dist < threshold) {
449 threshold = cur_dist;
450 best_station = st;
451 }
452 }
453 }
454
455 return best_station;
456 }
457
458 static void StationInitialize(Station *st, TileIndex tile)
459 {
460 GoodsEntry *ge;
461
462 st->xy = tile;
463 st->airport_tile = st->dock_tile = st->train_tile = 0;
464 st->bus_stops = st->truck_stops = NULL;
465 st->had_vehicle_of_type = 0;
466 st->time_since_load = 255;
467 st->time_since_unload = 255;
468 st->delete_ctr = 0;
469 st->facilities = 0;
470
471 st->last_vehicle_type = VEH_Invalid;
472
473 for (ge = st->goods; ge != endof(st->goods); ge++) {
474 ge->waiting_acceptance = 0;
475 ge->days_since_pickup = 0;
476 ge->enroute_from = INVALID_STATION;
477 ge->rating = 175;
478 ge->last_speed = 0;
479 ge->last_age = 0xFF;
480 ge->feeder_profit = 0;
481 }
482
483 st->random_bits = Random();
484 st->waiting_triggers = 0;
485
486 StationRect_Init(st);
487 }
488
489 // Update the virtual coords needed to draw the station sign.
490 // st = Station to update for.
491 static void UpdateStationVirtCoord(Station *st)
492 {
493 Point pt = RemapCoords2(TileX(st->xy) * TILE_SIZE, TileY(st->xy) * TILE_SIZE);
494
495 pt.y -= 32;
496 if (st->facilities & FACIL_AIRPORT && st->airport_type == AT_OILRIG) pt.y -= 16;
497
498 SetDParam(0, st->index);
499 SetDParam(1, st->facilities);
500 UpdateViewportSignPos(&st->sign, pt.x, pt.y, STR_305C_0);
501 }
502
503 // Update the virtual coords needed to draw the station sign for all stations.
504 void UpdateAllStationVirtCoord(void)
505 {
506 Station* st;
507
508 FOR_ALL_STATIONS(st) {
509 UpdateStationVirtCoord(st);
510 }
511 }
512
513 // Update the station virt coords while making the modified parts dirty.
514 static void UpdateStationVirtCoordDirty(Station *st)
515 {
516 MarkStationDirty(st);
517 UpdateStationVirtCoord(st);
518 MarkStationDirty(st);
519 }
520
521 // Get a mask of the cargo types that the station accepts.
522 static uint GetAcceptanceMask(const Station *st)
523 {
524 uint mask = 0;
525 uint i;
526
527 for (i = 0; i != NUM_CARGO; i++) {
528 if (st->goods[i].waiting_acceptance & 0x8000) mask |= 1 << i;
529 }
530 return mask;
531 }
532
533 // Items contains the two cargo names that are to be accepted or rejected.
534 // msg is the string id of the message to display.
535 static void ShowRejectOrAcceptNews(const Station *st, uint32 items, StringID msg)
536 {
537 if (items) {
538 SetDParam(2, GB(items, 16, 16));
539 SetDParam(1, GB(items, 0, 16));
540 SetDParam(0, st->index);
541 AddNewsItem(msg + (GB(items, 16, 16) ? 1 : 0), NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_TILE, NT_ACCEPTANCE, 0), st->xy, 0);
542 }
543 }
544
545 // Get a list of the cargo types being produced around the tile.
546 void GetProductionAroundTiles(AcceptedCargo produced, TileIndex tile,
547 int w, int h, int rad)
548 {
549 int x,y;
550 int x1,y1,x2,y2;
551 int xc,yc;
552
553 memset(produced, 0, sizeof(AcceptedCargo));
554
555 x = TileX(tile);
556 y = TileY(tile);
557
558 // expand the region by rad tiles on each side
559 // while making sure that we remain inside the board.
560 x2 = min(x + w + rad, MapSizeX());
561 x1 = max(x - rad, 0);
562
563 y2 = min(y + h + rad, MapSizeY());
564 y1 = max(y - rad, 0);
565
566 assert(x1 < x2);
567 assert(y1 < y2);
568 assert(w > 0);
569 assert(h > 0);
570
571 for (yc = y1; yc != y2; yc++) {
572 for (xc = x1; xc != x2; xc++) {
573 if (!(IS_INSIDE_1D(xc, x, w) && IS_INSIDE_1D(yc, y, h))) {
574 GetProducedCargoProc *gpc;
575 TileIndex tile = TileXY(xc, yc);
576
577 gpc = _tile_type_procs[GetTileType(tile)]->get_produced_cargo_proc;
578 if (gpc != NULL) {
579 CargoID cargos[2] = { CT_INVALID, CT_INVALID };
580
581 gpc(tile, cargos);
582 if (cargos[0] != CT_INVALID) {
583 produced[cargos[0]]++;
584 if (cargos[1] != CT_INVALID) {
585 produced[cargos[1]]++;
586 }
587 }
588 }
589 }
590 }
591 }
592 }
593
594 // Get a list of the cargo types that are accepted around the tile.
595 void GetAcceptanceAroundTiles(AcceptedCargo accepts, TileIndex tile,
596 int w, int h, int rad)
597 {
598 int x,y;
599 int x1,y1,x2,y2;
600 int xc,yc;
601
602 memset(accepts, 0, sizeof(AcceptedCargo));
603
604 x = TileX(tile);
605 y = TileY(tile);
606
607 // expand the region by rad tiles on each side
608 // while making sure that we remain inside the board.
609 x2 = min(x + w + rad, MapSizeX());
610 y2 = min(y + h + rad, MapSizeY());
611 x1 = max(x - rad, 0);
612 y1 = max(y - rad, 0);
613
614 assert(x1 < x2);
615 assert(y1 < y2);
616 assert(w > 0);
617 assert(h > 0);
618
619 for (yc = y1; yc != y2; yc++) {
620 for (xc = x1; xc != x2; xc++) {
621 TileIndex tile = TileXY(xc, yc);
622
623 if (!IsTileType(tile, MP_STATION)) {
624 AcceptedCargo ac;
625 uint i;
626
627 GetAcceptedCargo(tile, ac);
628 for (i = 0; i < lengthof(ac); ++i) accepts[i] += ac[i];
629 }
630 }
631 }
632 }
633
634 typedef struct ottd_Rectangle {
635 uint min_x;
636 uint min_y;
637 uint max_x;
638 uint max_y;
639 } ottd_Rectangle;
640
641 static inline void MergePoint(ottd_Rectangle* rect, TileIndex tile)
642 {
643 uint x = TileX(tile);
644 uint y = TileY(tile);
645
646 if (rect->min_x > x) rect->min_x = x;
647 if (rect->min_y > y) rect->min_y = y;
648 if (rect->max_x < x) rect->max_x = x;
649 if (rect->max_y < y) rect->max_y = y;
650 }
651
652 // Update the acceptance for a station.
653 // show_msg controls whether to display a message that acceptance was changed.
654 static void UpdateStationAcceptance(Station *st, bool show_msg)
655 {
656 uint old_acc, new_acc;
657 const RoadStop *cur_rs;
658 int i;
659 ottd_Rectangle rect;
660 int rad;
661 AcceptedCargo accepts;
662
663 rect.min_x = MapSizeX();
664 rect.min_y = MapSizeY();
665 rect.max_x = rect.max_y = 0;
666 // Don't update acceptance for a buoy
667 if (IsBuoy(st)) return;
668
669 /* old accepted goods types */
670 old_acc = GetAcceptanceMask(st);
671
672 // Put all the tiles that span an area in the table.
673 if (st->train_tile != 0) {
674 MergePoint(&rect, st->train_tile);
675 MergePoint(&rect,
676 st->train_tile + TileDiffXY(st->trainst_w - 1, st->trainst_h - 1)
677 );
678 }
679
680 if (st->airport_tile != 0) {
681 const AirportFTAClass* afc = GetAirport(st->airport_type);
682
683 MergePoint(&rect, st->airport_tile);
684 MergePoint(&rect,
685 st->airport_tile + TileDiffXY(afc->size_x - 1, afc->size_y - 1)
686 );
687 }
688
689 if (st->dock_tile != 0) MergePoint(&rect, st->dock_tile);
690
691 for (cur_rs = st->bus_stops; cur_rs != NULL; cur_rs = cur_rs->next) {
692 MergePoint(&rect, cur_rs->xy);
693 }
694
695 for (cur_rs = st->truck_stops; cur_rs != NULL; cur_rs = cur_rs->next) {
696 MergePoint(&rect, cur_rs->xy);
697 }
698
699 rad = (_patches.modified_catchment) ? FindCatchmentRadius(st) : 4;
700
701 // And retrieve the acceptance.
702 if (rect.max_x >= rect.min_x) {
703 GetAcceptanceAroundTiles(
704 accepts,
705 TileXY(rect.min_x, rect.min_y),
706 rect.max_x - rect.min_x + 1,
707 rect.max_y - rect.min_y + 1,
708 rad
709 );
710 } else {
711 memset(accepts, 0, sizeof(accepts));
712 }
713
714 // Adjust in case our station only accepts fewer kinds of goods
715 for (i = 0; i != NUM_CARGO; i++) {
716 uint amt = min(accepts[i], 15);
717
718 // Make sure the station can accept the goods type.
719 if ((i != CT_PASSENGERS && !(st->facilities & (byte)~FACIL_BUS_STOP)) ||
720 (i == CT_PASSENGERS && !(st->facilities & (byte)~FACIL_TRUCK_STOP)))
721 amt = 0;
722
723 SB(st->goods[i].waiting_acceptance, 12, 4, amt);
724 }
725
726 // Only show a message in case the acceptance was actually changed.
727 new_acc = GetAcceptanceMask(st);
728 if (old_acc == new_acc)
729 return;
730
731 // show a message to report that the acceptance was changed?
732 if (show_msg && st->owner == _local_player && st->facilities) {
733 uint32 accept=0, reject=0; /* these contain two string ids each */
734 const StringID *str = _cargoc.names_s;
735
736 do {
737 if (new_acc & 1) {
738 if (!(old_acc & 1)) accept = (accept << 16) | *str;
739 } else {
740 if (old_acc & 1) reject = (reject << 16) | *str;
741 }
742 } while (str++,(new_acc>>=1) != (old_acc>>=1));
743
744 ShowRejectOrAcceptNews(st, accept, STR_3040_NOW_ACCEPTS);
745 ShowRejectOrAcceptNews(st, reject, STR_303E_NO_LONGER_ACCEPTS);
746 }
747
748 // redraw the station view since acceptance changed
749 InvalidateWindowWidget(WC_STATION_VIEW, st->index, 4);
750 }
751
752 static void UpdateStationSignCoord(Station *st)
753 {
754 Rect *r = &st->rect;
755
756 if (StationRect_IsEmpty(st)) return; // no tiles belong to this station
757
758 // clamp sign coord to be inside the station rect
759 st->xy = TileXY(clampu(TileX(st->xy), r->left, r->right), clampu(TileY(st->xy), r->top, r->bottom));
760 UpdateStationVirtCoordDirty(st);
761 }
762
763 // This is called right after a station was deleted.
764 // It checks if the whole station is free of substations, and if so, the station will be
765 // deleted after a little while.
766 static void DeleteStationIfEmpty(Station* st)
767 {
768 if (st->facilities == 0) {
769 st->delete_ctr = 0;
770 RebuildStationLists();
771 InvalidateWindow(WC_STATION_LIST, st->owner);
772 }
773 /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
774 UpdateStationSignCoord(st);
775 }
776
777 static int32 ClearTile_Station(TileIndex tile, byte flags);
778
779 // Tries to clear the given area. Returns the cost in case of success.
780 // Or an error code if it failed.
781 int32 CheckFlatLandBelow(TileIndex tile, uint w, uint h, uint flags, uint invalid_dirs, StationID* station)
782 {
783 int32 cost = 0, ret;
784
785 Slope tileh;
786 uint z;
787 int allowed_z = -1;
788 int flat_z;
789
790 BEGIN_TILE_LOOP(tile_cur, w, h, tile)
791 if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) {
792 return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
793 }
794
795 if (!EnsureNoVehicle(tile_cur)) return CMD_ERROR;
796
797 tileh = GetTileSlope(tile_cur, &z);
798
799 /* Prohibit building if
800 * 1) The tile is "steep" (i.e. stretches two height levels)
801 * -OR-
802 * 2) The tile is non-flat if
803 * a) the player building is an "old-school" AI
804 * -OR-
805 * b) the build_on_slopes switch is disabled
806 */
807 if (IsSteepSlope(tileh) ||
808 ((_is_old_ai_player || !_patches.build_on_slopes) && tileh != SLOPE_FLAT)) {
809 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
810 }
811
812 flat_z = z;
813 if (tileh != SLOPE_FLAT) {
814 // need to check so the entrance to the station is not pointing at a slope.
815 if ((invalid_dirs&1 && !(tileh & SLOPE_NE) && (uint)w_cur == w) ||
816 (invalid_dirs&2 && !(tileh & SLOPE_SE) && h_cur == 1) ||
817 (invalid_dirs&4 && !(tileh & SLOPE_SW) && w_cur == 1) ||
818 (invalid_dirs&8 && !(tileh & SLOPE_NW) && (uint)h_cur == h)) {
819 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
820 }
821 cost += _price.terraform;
822 flat_z += TILE_HEIGHT;
823 }
824
825 // get corresponding flat level and make sure that all parts of the station have the same level.
826 if (allowed_z == -1) {
827 // first tile
828 allowed_z = flat_z;
829 } else if (allowed_z != flat_z) {
830 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
831 }
832
833 // if station is set, then we have special handling to allow building on top of already existing stations.
834 // so station points to INVALID_STATION if we can build on any station. or it points to a station if we're only allowed to build
835 // on exactly that station.
836 if (station != NULL && IsTileType(tile_cur, MP_STATION)) {
837 if (!IsRailwayStation(tile_cur)) {
838 return ClearTile_Station(tile_cur, DC_AUTO); // get error message
839 } else {
840 StationID st = GetStationIndex(tile_cur);
841 if (*station == INVALID_STATION) {
842 *station = st;
843 } else if (*station != st) {
844 return_cmd_error(STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING);
845 }
846 }
847 } else {
848 ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
849 if (CmdFailed(ret)) return ret;
850 cost += ret;
851 }
852 END_TILE_LOOP(tile_cur, w, h, tile)
853
854 return cost;
855 }
856
857 static bool CanExpandRailroadStation(Station* st, uint* fin, Axis axis)
858 {
859 uint curw = st->trainst_w, curh = st->trainst_h;
860 TileIndex tile = fin[0];
861 uint w = fin[1];
862 uint h = fin[2];
863
864 if (_patches.nonuniform_stations) {
865 // determine new size of train station region..
866 int x = min(TileX(st->train_tile), TileX(tile));
867 int y = min(TileY(st->train_tile), TileY(tile));
868 curw = max(TileX(st->train_tile) + curw, TileX(tile) + w) - x;
869 curh = max(TileY(st->train_tile) + curh, TileY(tile) + h) - y;
870 tile = TileXY(x, y);
871 } else {
872 // check so the orientation is the same
873 if (GetRailStationAxis(st->train_tile) != axis) {
874 _error_message = STR_306D_NONUNIFORM_STATIONS_DISALLOWED;
875 return false;
876 }
877
878 // check if the new station adjoins the old station in either direction
879 if (curw == w && st->train_tile == tile + TileDiffXY(0, h)) {
880 // above
881 curh += h;
882 } else if (curw == w && st->train_tile == tile - TileDiffXY(0, curh)) {
883 // below
884 tile -= TileDiffXY(0, curh);
885 curh += h;
886 } else if (curh == h && st->train_tile == tile + TileDiffXY(w, 0)) {
887 // to the left
888 curw += w;
889 } else if (curh == h && st->train_tile == tile - TileDiffXY(curw, 0)) {
890 // to the right
891 tile -= TileDiffXY(curw, 0);
892 curw += w;
893 } else {
894 _error_message = STR_306D_NONUNIFORM_STATIONS_DISALLOWED;
895 return false;
896 }
897 }
898 // make sure the final size is not too big.
899 if (curw > _patches.station_spread || curh > _patches.station_spread) {
900 _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
901 return false;
902 }
903
904 // now tile contains the new value for st->train_tile
905 // curw, curh contain the new value for width and height
906 fin[0] = tile;
907 fin[1] = curw;
908 fin[2] = curh;
909 return true;
910 }
911
912 static inline byte *CreateSingle(byte *layout, int n)
913 {
914 int i = n;
915 do *layout++ = 0; while (--i);
916 layout[((n-1) >> 1)-n] = 2;
917 return layout;
918 }
919
920 static inline byte *CreateMulti(byte *layout, int n, byte b)
921 {
922 int i = n;
923 do *layout++ = b; while (--i);
924 if (n > 4) {
925 layout[0-n] = 0;
926 layout[n-1-n] = 0;
927 }
928 return layout;
929 }
930
931 static void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec)
932 {
933 if (statspec != NULL && statspec->lengths >= plat_len &&
934 statspec->platforms[plat_len - 1] >= numtracks &&
935 statspec->layouts[plat_len - 1][numtracks - 1]) {
936 /* Custom layout defined, follow it. */
937 memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1],
938 plat_len * numtracks);
939 return;
940 }
941
942 if (plat_len == 1) {
943 CreateSingle(layout, numtracks);
944 } else {
945 if (numtracks & 1) layout = CreateSingle(layout, plat_len);
946 numtracks >>= 1;
947
948 while (--numtracks >= 0) {
949 layout = CreateMulti(layout, plat_len, 4);
950 layout = CreateMulti(layout, plat_len, 6);
951 }
952 }
953 }
954
955 /** Build railroad station
956 * @param tile_org starting position of station dragging/placement
957 * @param p1 various bitstuffed elements
958 * - p1 = (bit 0) - orientation (p1 & 1)
959 * - p1 = (bit 8-15) - number of tracks
960 * - p1 = (bit 16-23) - platform length
961 * @param p2 various bitstuffed elements
962 * - p2 = (bit 0- 3) - railtype (p2 & 0xF)
963 * - p2 = (bit 8-15) - custom station class
964 * - p2 = (bit 16-23) - custom station id
965 */
966 int32 CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, uint32 p2)
967 {
968 Station *st;
969 int w_org, h_org;
970 int32 cost, ret;
971 StationID est;
972 int plat_len, numtracks;
973 Axis axis;
974 uint finalvalues[3];
975 const StationSpec *statspec;
976 int specindex;
977
978 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
979
980 /* Does the authority allow this? */
981 if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile_org)) return CMD_ERROR;
982 if (!ValParamRailtype(p2 & 0xF)) return CMD_ERROR;
983
984 /* unpack parameters */
985 axis = p1 & 1;
986 numtracks = GB(p1, 8, 8);
987 plat_len = GB(p1, 16, 8);
988 /* w = length, h = num_tracks */
989 if (axis == AXIS_X) {
990 w_org = plat_len;
991 h_org = numtracks;
992 } else {
993 h_org = plat_len;
994 w_org = numtracks;
995 }
996
997 if (h_org > _patches.station_spread || w_org > _patches.station_spread) return CMD_ERROR;
998
999 // these values are those that will be stored in train_tile and station_platforms
1000 finalvalues[0] = tile_org;
1001 finalvalues[1] = w_org;
1002 finalvalues[2] = h_org;
1003
1004 // Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station)
1005 est = INVALID_STATION;
1006 // If DC_EXEC is in flag, do not want to pass it to CheckFlatLandBelow, because of a nice bug
1007 // for detail info, see: https://sourceforge.net/tracker/index.php?func=detail&aid=1029064&group_id=103924&atid=636365
1008 ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags & ~DC_EXEC, 5 << axis, _patches.nonuniform_stations ? &est : NULL);
1009 if (CmdFailed(ret)) return ret;
1010 cost = ret + (numtracks * _price.train_station_track + _price.train_station_length) * plat_len;
1011
1012 // Make sure there are no similar stations around us.
1013 st = GetStationAround(tile_org, w_org, h_org, est);
1014 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
1015
1016 // See if there is a deleted station close to us.
1017 if (st == NULL) {
1018 st = GetClosestStationFromTile(tile_org, 8, _current_player);
1019 if (st != NULL && st->facilities) st = NULL;
1020 }
1021
1022 if (st != NULL) {
1023 // Reuse an existing station.
1024 if (st->owner != OWNER_NONE && st->owner != _current_player)
1025 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
1026
1027 if (st->train_tile != 0) {
1028 // check if we want to expanding an already existing station?
1029 if (_is_old_ai_player || !_patches.join_stations)
1030 return_cmd_error(STR_3005_TOO_CLOSE_TO_ANOTHER_RAILROAD);
1031 if (!CanExpandRailroadStation(st, finalvalues, axis))
1032 return CMD_ERROR;
1033 }
1034
1035 //XXX can't we pack this in the "else" part of the if above?
1036 if (!StationRect_BeforeAddRect(st, tile_org, w_org, h_org, RECT_MODE_TEST)) return CMD_ERROR;
1037 } else {
1038 // Create a new station
1039 st = AllocateStation();
1040 if (st == NULL) return CMD_ERROR;
1041
1042 st->town = ClosestTownFromTile(tile_org, (uint)-1);
1043 if (IsValidPlayer(_current_player) && (flags & DC_EXEC))
1044 SETBIT(st->town->have_ratings, _current_player);
1045
1046 if (!GenerateStationName(st, tile_org, 0)) return CMD_ERROR;
1047
1048 if (flags & DC_EXEC) StationInitialize(st, tile_org);
1049 }
1050
1051 /* Check if the given station class is valid */
1052 if (GB(p2, 8, 8) >= STAT_CLASS_MAX) return CMD_ERROR;
1053
1054 /* Check if we can allocate a custom stationspec to this station */
1055 statspec = GetCustomStationSpec(GB(p2, 8, 8), GB(p2, 16, 8));
1056 specindex = AllocateSpecToStation(statspec, st, flags & DC_EXEC);
1057 if (specindex == -1) return CMD_ERROR;
1058
1059 if (statspec != NULL) {
1060 /* Perform NewStation checks */
1061
1062 /* Check if the station size is permitted */
1063 if (HASBIT(statspec->disallowed_platforms, numtracks - 1) || HASBIT(statspec->disallowed_lengths, plat_len - 1)) {
1064 return CMD_ERROR;
1065 }
1066
1067 /* Check if the station is buildable */
1068 if (HASBIT(statspec->callbackmask, CBM_STATION_AVAIL) && GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE) == 0) {
1069 return CMD_ERROR;
1070 }
1071 }
1072
1073 if (flags & DC_EXEC) {
1074 TileIndexDiff tile_delta;
1075 byte *layout_ptr;
1076 byte numtracks_orig;
1077 Track track;
1078
1079 // Now really clear the land below the station
1080 // It should never return CMD_ERROR.. but you never know ;)
1081 // (a bit strange function name for it, but it really does clear the land, when DC_EXEC is in flags)
1082 ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags, 5 << axis, _patches.nonuniform_stations ? &est : NULL);
1083 if (CmdFailed(ret)) return ret;
1084
1085 st->train_tile = finalvalues[0];
1086 if (!st->facilities) st->xy = finalvalues[0];
1087 st->facilities |= FACIL_TRAIN;
1088 st->owner = _current_player;
1089
1090 st->trainst_w = finalvalues[1];
1091 st->trainst_h = finalvalues[2];
1092
1093 st->build_date = _date;
1094
1095 StationRect_BeforeAddRect(st, tile_org, w_org, h_org, RECT_MODE_TRY);
1096
1097 tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
1098 track = AxisToTrack(axis);
1099
1100 layout_ptr = alloca(numtracks * plat_len);
1101 GetStationLayout(layout_ptr, numtracks, plat_len, statspec);
1102
1103 numtracks_orig = numtracks;
1104
1105 do {
1106 TileIndex tile = tile_org;
1107 int w = plat_len;
1108 do {
1109 byte layout = *layout_ptr++;
1110 MakeRailStation(tile, st->owner, st->index, axis, layout, GB(p2, 0, 4));
1111 SetCustomStationSpecIndex(tile, specindex);
1112 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1113
1114 if (statspec != NULL) {
1115 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1116 uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
1117 uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, st, tile);
1118 if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, callback + axis);
1119 }
1120
1121 tile += tile_delta;
1122 } while (--w);
1123 SetSignalsOnBothDir(tile_org, track);
1124 YapfNotifyTrackLayoutChange(tile_org, track);
1125 tile_org += tile_delta ^ TileDiffXY(1, 1); // perpendicular to tile_delta
1126 } while (--numtracks);
1127
1128 MarkStationTilesDirty(st);
1129 UpdateStationVirtCoordDirty(st);
1130 UpdateStationAcceptance(st, false);
1131 RebuildStationLists();
1132 InvalidateWindow(WC_STATION_LIST, st->owner);
1133 }
1134
1135 return cost;
1136 }
1137
1138 static void MakeRailwayStationAreaSmaller(Station *st)
1139 {
1140 uint w = st->trainst_w;
1141 uint h = st->trainst_h;
1142 TileIndex tile = st->train_tile;
1143 uint i;
1144
1145 restart:
1146
1147 // too small?
1148 if (w != 0 && h != 0) {
1149 // check the left side, x = constant, y changes
1150 for (i = 0; !TileBelongsToRailStation(st, tile + TileDiffXY(0, i));) {
1151 // the left side is unused?
1152 if (++i == h) {
1153 tile += TileDiffXY(1, 0);
1154 w--;
1155 goto restart;
1156 }
1157 }
1158
1159 // check the right side, x = constant, y changes
1160 for (i = 0; !TileBelongsToRailStation(st, tile + TileDiffXY(w - 1, i));) {
1161 // the right side is unused?
1162 if (++i == h) {
1163 w--;
1164 goto restart;
1165 }
1166 }
1167
1168 // check the upper side, y = constant, x changes
1169 for (i = 0; !TileBelongsToRailStation(st, tile + TileDiffXY(i, 0));) {
1170 // the left side is unused?
1171 if (++i == w) {
1172 tile += TileDiffXY(0, 1);
1173 h--;
1174 goto restart;
1175 }
1176 }
1177
1178 // check the lower side, y = constant, x changes
1179 for (i = 0; !TileBelongsToRailStation(st, tile + TileDiffXY(i, h - 1));) {
1180 // the left side is unused?
1181 if (++i == w) {
1182 h--;
1183 goto restart;
1184 }
1185 }
1186 } else {
1187 tile = 0;
1188 }
1189
1190 st->trainst_w = w;
1191 st->trainst_h = h;
1192 st->train_tile = tile;
1193 }
1194
1195 /** Remove a single tile from a railroad station.
1196 * This allows for custom-built station with holes and weird layouts
1197 * @param tile tile of station piece to remove
1198 * @param p1 unused
1199 * @param p2 unused
1200 */
1201 int32 CmdRemoveFromRailroadStation(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
1202 {
1203 Station *st;
1204
1205 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
1206
1207 // make sure the specified tile belongs to the current player, and that it is a railroad station.
1208 if (!IsTileType(tile, MP_STATION) || !IsRailwayStation(tile) || !_patches.nonuniform_stations) return CMD_ERROR;
1209 st = GetStationByTile(tile);
1210 if (_current_player != OWNER_WATER && (!CheckOwnership(st->owner) || !EnsureNoVehicle(tile))) return CMD_ERROR;
1211
1212 // if we reached here, it means we can actually delete it. do that.
1213 if (flags & DC_EXEC) {
1214 uint specindex = GetCustomStationSpecIndex(tile);
1215 Track track = GetRailStationTrack(tile);
1216 DoClearSquare(tile);
1217 StationRect_AfterRemoveTile(st, tile);
1218 SetSignalsOnBothDir(tile, track);
1219 YapfNotifyTrackLayoutChange(tile, track);
1220
1221 DeallocateSpecFromStation(st, specindex);
1222
1223 // now we need to make the "spanned" area of the railway station smaller if we deleted something at the edges.
1224 // we also need to adjust train_tile.
1225 MakeRailwayStationAreaSmaller(st);
1226 MarkStationTilesDirty(st);
1227 UpdateStationSignCoord(st);
1228
1229 // if we deleted the whole station, delete the train facility.
1230 if (st->train_tile == 0) {
1231 st->facilities &= ~FACIL_TRAIN;
1232 UpdateStationVirtCoordDirty(st);
1233 DeleteStationIfEmpty(st);
1234 }
1235 }
1236 return _price.remove_rail_station;
1237 }
1238
1239 // determine the number of platforms for the station
1240 uint GetStationPlatforms(const Station *st, TileIndex tile)
1241 {
1242 TileIndex t;
1243 TileIndexDiff delta;
1244 Axis axis;
1245 uint len;
1246 assert(TileBelongsToRailStation(st, tile));
1247
1248 len = 0;
1249 axis = GetRailStationAxis(tile);
1250 delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
1251
1252 // find starting tile..
1253 t = tile;
1254 do {
1255 t -= delta;
1256 len++;
1257 } while (TileBelongsToRailStation(st, t) && GetRailStationAxis(t) == axis);
1258
1259 // find ending tile
1260 t = tile;
1261 do {
1262 t += delta;
1263 len++;
1264 } while (TileBelongsToRailStation(st, t) && GetRailStationAxis(t) == axis);
1265
1266 return len - 1;
1267 }
1268
1269 /** Determines the REMAINING length of a platform, starting at (and including)
1270 * the given tile.
1271 * @param tile the tile from which to start searching. Must be a railway station tile
1272 * @param dir The direction in which to search.
1273 * @return The platform length
1274 */
1275 uint GetPlatformLength(TileIndex tile, DiagDirection dir)
1276 {
1277 TileIndex start_tile = tile;
1278 uint length = 0;
1279 assert(IsRailwayStationTile(tile));
1280 assert(dir < DIAGDIR_END);
1281
1282 do {
1283 length ++;
1284 tile += TileOffsByDiagDir(dir);
1285 } while (IsCompatibleTrainStationTile(tile, start_tile));
1286
1287 return length;
1288 }
1289
1290
1291 static int32 RemoveRailroadStation(Station *st, TileIndex tile, uint32 flags)
1292 {
1293 int w,h;
1294 int32 cost = 0;
1295
1296 /* if there is flooding and non-uniform stations are enabled, remove platforms tile by tile */
1297 if (_current_player == OWNER_WATER && _patches.nonuniform_stations)
1298 return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAILROAD_STATION);
1299
1300 /* Current player owns the station? */
1301 if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
1302 return CMD_ERROR;
1303
1304 /* determine width and height of platforms */
1305 tile = st->train_tile;
1306 w = st->trainst_w;
1307 h = st->trainst_h;
1308
1309 assert(w != 0 && h != 0);
1310
1311 /* clear all areas of the station */
1312 do {
1313 int w_bak = w;
1314 do {
1315 // for nonuniform stations, only remove tiles that are actually train station tiles
1316 if (TileBelongsToRailStation(st, tile)) {
1317 if (!EnsureNoVehicle(tile))
1318 return CMD_ERROR;
1319 cost += _price.remove_rail_station;
1320 if (flags & DC_EXEC) {
1321 Track track = GetRailStationTrack(tile);
1322 DoClearSquare(tile);
1323 SetSignalsOnBothDir(tile, track);
1324 YapfNotifyTrackLayoutChange(tile, track);
1325 }
1326 }
1327 tile += TileDiffXY(1, 0);
1328 } while (--w);
1329 w = w_bak;
1330 tile += TileDiffXY(-w, 1);
1331 } while (--h);
1332
1333 if (flags & DC_EXEC) {
1334 StationRect_AfterRemoveRect(st, st->train_tile, st->trainst_w, st->trainst_h);
1335
1336 st->train_tile = 0;
1337 st->trainst_w = st->trainst_h = 0;
1338 st->facilities &= ~FACIL_TRAIN;
1339
1340 free(st->speclist);
1341 st->num_specs = 0;
1342 st->speclist = NULL;
1343
1344 UpdateStationVirtCoordDirty(st);
1345 DeleteStationIfEmpty(st);
1346 }
1347
1348 return cost;
1349 }
1350
1351 int32 DoConvertStationRail(TileIndex tile, RailType totype, bool exec)
1352 {
1353 const Station* st = GetStationByTile(tile);
1354
1355 if (!CheckOwnership(st->owner) || !EnsureNoVehicle(tile)) return CMD_ERROR;
1356
1357 // tile is not a railroad station?
1358 if (!IsRailwayStation(tile)) return CMD_ERROR;
1359
1360 if (GetRailType(tile) == totype) return CMD_ERROR;
1361
1362 // 'hidden' elrails can't be downgraded to normal rail when elrails are disabled
1363 if (_patches.disable_elrails && totype == RAILTYPE_RAIL && GetRailType(tile) == RAILTYPE_ELECTRIC) return CMD_ERROR;
1364
1365 if (exec) {
1366 SetRailType(tile, totype);
1367 MarkTileDirtyByTile(tile);
1368 YapfNotifyTrackLayoutChange(tile, GetRailStationTrack(tile));
1369 }
1370
1371 return _price.build_rail >> 1;
1372 }
1373
1374 /** Heavy wizardry used to add a roadstop to a station.
1375 * To understand the function, lets first look at what is passed around,
1376 * especially the last two parameters. CmdBuildRoadStop allocates a road
1377 * stop and needs to put that stop into the linked list of road stops.
1378 * It (CmdBuildRoadStop) has a **currstop pointer which points to element
1379 * in the linked list of stops (each element in this list being a pointer
1380 * in itself, hence the double pointer). We (FindRoadStopSpot) need to
1381 * modify this pointer (**currstop) thus we need to pass by reference,
1382 * obtaining a triple pointer (***currstop). When finished, **currstop
1383 * in CmdBuildRoadStop will contain the address of the pointer which will
1384 * then point into the global roadstop array. *prev (in CmdBuildRoadStop)
1385 * is the pointer tino the global roadstop array which has *currstop in
1386 * its ->next element.
1387 * @param[in] truck_station Determines whether a stop is RS_BUS or RS_TRUCK
1388 * @param[in] station The station to do the whole procedure for
1389 * @param[out] currstop See the detailed function description
1390 * @param prev See the detailed function description
1391 */
1392 static void FindRoadStopSpot(bool truck_station, Station* st, RoadStop*** currstop, RoadStop** prev)
1393 {
1394 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1395 assert(*prev == NULL);
1396
1397 if (*primary_stop == NULL) {
1398 //we have no roadstop of the type yet, so write a "primary stop"
1399 *currstop = primary_stop;
1400 } else {
1401 //there are stops already, so append to the end of the list
1402 *prev = *primary_stop;
1403 *currstop = &(*primary_stop)->next;
1404 while (**currstop != NULL) {
1405 *prev = (*prev)->next;
1406 *currstop = &(**currstop)->next;
1407 }
1408 }
1409 }
1410
1411 /** Build a bus or truck stop
1412 * @param tile tile to build the stop at
1413 * @param p1 entrance direction (DiagDirection)
1414 * @param p2 0 for Bus stops, 1 for truck stops
1415 */
1416 int32 CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
1417 {
1418 Station *st;
1419 RoadStop *road_stop;
1420 RoadStop **currstop;
1421 RoadStop *prev = NULL;
1422 int32 cost;
1423 int32 ret;
1424 bool type = !!p2;
1425
1426 /* Saveguard the parameters */
1427 if (!IsValidDiagDirection(p1)) return CMD_ERROR;
1428
1429 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
1430
1431 if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
1432 return CMD_ERROR;
1433
1434 ret = CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL);
1435 if (CmdFailed(ret)) return ret;
1436 cost = ret;
1437
1438 st = GetStationAround(tile, 1, 1, -1);
1439 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
1440
1441 /* Find a station close to us */
1442 if (st == NULL) {
1443 st = GetClosestStationFromTile(tile, 8, _current_player);
1444 if (st != NULL && st->facilities != 0) st = NULL;
1445 }
1446
1447 //give us a road stop in the list, and check if something went wrong
1448 road_stop = AllocateRoadStop();
1449 if (road_stop == NULL) {
1450 return_cmd_error(type ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS);
1451 }
1452
1453 if (st != NULL &&
1454 GetNumRoadStopsInStation(st, RS_BUS) + GetNumRoadStopsInStation(st, RS_TRUCK) >= ROAD_STOP_LIMIT) {
1455 return_cmd_error(type ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS);
1456 }
1457
1458 if (st != NULL) {
1459 if (st->owner != OWNER_NONE && st->owner != _current_player) {
1460 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
1461 }
1462
1463 if (!StationRect_BeforeAddTile(st, tile, RECT_MODE_TEST)) return CMD_ERROR;
1464
1465 FindRoadStopSpot(type, st, &currstop, &prev);
1466 } else {
1467 Town *t;
1468
1469 st = AllocateStation();
1470 if (st == NULL) return CMD_ERROR;
1471
1472 st->town = t = ClosestTownFromTile(tile, (uint)-1);
1473
1474 FindRoadStopSpot(type, st, &currstop, &prev);
1475
1476 if (IsValidPlayer(_current_player) && (flags & DC_EXEC)) {
1477 SETBIT(t->have_ratings, _current_player);
1478 }
1479
1480 st->sign.width_1 = 0;
1481
1482 if (!GenerateStationName(st, tile, 0)) return CMD_ERROR;
1483
1484 if (flags & DC_EXEC) StationInitialize(st, tile);
1485 }
1486
1487 cost += (type) ? _price.build_truck_station : _price.build_bus_station;
1488
1489 if (flags & DC_EXEC) {
1490 //point to the correct item in the _busstops or _truckstops array
1491 *currstop = road_stop;
1492
1493 //initialize an empty station
1494 InitializeRoadStop(road_stop, prev, tile, st->index);
1495 if (!st->facilities) st->xy = tile;
1496 st->facilities |= (type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP;
1497 st->owner = _current_player;
1498
1499 st->build_date = _date;
1500
1501 StationRect_BeforeAddTile(st, tile, RECT_MODE_TRY);
1502
1503 MakeRoadStop(tile, st->owner, st->index, type, p1);
1504
1505 UpdateStationVirtCoordDirty(st);
1506 UpdateStationAcceptance(st, false);
1507 RebuildStationLists();
1508 InvalidateWindow(WC_STATION_LIST, st->owner);
1509 }
1510 return cost;
1511 }
1512
1513 // Remove a bus station
1514 static int32 RemoveRoadStop(Station *st, uint32 flags, TileIndex tile)
1515 {
1516 RoadStop **primary_stop;
1517 RoadStop *cur_stop;
1518 bool is_truck = IsTruckStop(tile);
1519
1520 if (_current_player != OWNER_WATER && !CheckOwnership(st->owner)) {
1521 return CMD_ERROR;
1522 }
1523
1524 if (is_truck) { // truck stop
1525 primary_stop = &st->truck_stops;
1526 cur_stop = GetRoadStopByTile(tile, RS_TRUCK);
1527 } else {
1528 primary_stop = &st->bus_stops;
1529 cur_stop = GetRoadStopByTile(tile, RS_BUS);
1530 }
1531
1532 assert(cur_stop != NULL);
1533
1534 if (!EnsureNoVehicle(tile)) return CMD_ERROR;
1535
1536 if (flags & DC_EXEC) {
1537 //we only had one stop left
1538 if (cur_stop->next == NULL && cur_stop->prev == NULL) {
1539 //so we remove ALL stops
1540 *primary_stop = NULL;
1541 st->facilities &= (is_truck) ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP;
1542 } else if (cur_stop == *primary_stop) {
1543 //removed the first stop in the list
1544 //need to set the primary element to the next stop
1545 *primary_stop = (*primary_stop)->next;
1546 }
1547
1548 DeleteRoadStop(cur_stop);
1549 DoClearSquare(tile);
1550 StationRect_AfterRemoveTile(st, tile);
1551
1552 UpdateStationVirtCoordDirty(st);
1553 DeleteStationIfEmpty(st);
1554 }
1555
1556 return (is_truck) ? _price.remove_truck_station : _price.remove_bus_station;
1557 }
1558
1559
1560
1561 // FIXME -- need to move to its corresponding Airport variable
1562 // Country Airfield (small)
1563 static const byte _airport_sections_country[] = {
1564 54, 53, 52, 65,
1565 58, 57, 56, 55,
1566 64, 63, 63, 62
1567 };
1568
1569 // City Airport (large)
1570 static const byte _airport_sections_town[] = {
1571 31, 9, 33, 9, 9, 32,
1572 27, 36, 29, 34, 8, 10,
1573 30, 11, 35, 13, 20, 21,
1574 51, 12, 14, 17, 19, 28,
1575 38, 13, 15, 16, 18, 39,
1576 26, 22, 23, 24, 25, 26
1577 };
1578
1579 // Metropolitain Airport (large) - 2 runways
1580 static const byte _airport_sections_metropolitan[] = {
1581 31, 9, 33, 9, 9, 32,
1582 27, 36, 29, 34, 8, 10,
1583 30, 11, 35, 13, 20, 21,
1584 102, 8, 8, 8, 8, 28,
1585 83, 84, 84, 84, 84, 83,
1586 26, 23, 23, 23, 23, 26
1587 };
1588
1589 // International Airport (large) - 2 runways
1590 static const byte _airport_sections_international[] = {
1591 88, 89, 89, 89, 89, 89, 88,
1592 51, 8, 8, 8, 8, 8, 32,
1593 30, 8, 11, 27, 11, 8, 10,
1594 32, 8, 11, 27, 11, 8, 114,
1595 87, 8, 11, 85, 11, 8, 114,
1596 87, 8, 8, 8, 8, 8, 90,
1597 26, 23, 23, 23, 23, 23, 26
1598 };
1599
1600 // Intercontinental Airport (vlarge) - 4 runways
1601 static const byte _airport_sections_intercontinental[] = {
1602 102, 120, 89, 89, 89, 89, 89, 89, 118,
1603 120, 22, 22, 22, 22, 22, 22, 119, 117,
1604 87, 54, 87, 8, 8, 8, 8, 51, 117,
1605 87, 162, 87, 85, 116, 116, 8, 9, 10,
1606 87, 8, 8, 11, 31, 11, 8, 160, 32,
1607 32, 160, 8, 11, 27, 11, 8, 8, 10,
1608 87, 8, 8, 11, 30, 11, 8, 8, 10,
1609 87, 142, 8, 11, 29, 11, 10, 163, 10,
1610 87, 164, 87, 8, 8, 8, 10, 37, 117,
1611 87, 120, 89, 89, 89, 89, 89, 89, 119,
1612 121, 22, 22, 22, 22, 22, 22, 119, 37
1613 };
1614
1615
1616 // Commuter Airfield (small)
1617 static const byte _airport_sections_commuter[] = {
1618 85, 30, 115, 115, 32,
1619 87, 8, 8, 8, 10,
1620 87, 11, 11, 11, 10,
1621 26, 23, 23, 23, 26
1622 };
1623
1624 // Heliport
1625 static const byte _airport_sections_heliport[] = {
1626 66,
1627 };
1628
1629 // Helidepot
1630 static const byte _airport_sections_helidepot[] = {
1631 124, 32,
1632 122, 123
1633 };
1634
1635 // Helistation
1636 static const byte _airport_sections_helistation[] = {
1637 32, 134, 159, 158,
1638 161, 142, 142, 157
1639 };
1640
1641 static const byte * const _airport_sections[] = {
1642 _airport_sections_country, // Country Airfield (small)
1643 _airport_sections_town, // City Airport (large)
1644 _airport_sections_heliport, // Heliport
1645 _airport_sections_metropolitan, // Metropolitain Airport (large)
1646 _airport_sections_international, // International Airport (xlarge)
1647 _airport_sections_commuter, // Commuter Airport (small)
1648 _airport_sections_helidepot, // Helidepot
1649 _airport_sections_intercontinental, // Intercontinental Airport (xxlarge)
1650 _airport_sections_helistation // Helistation
1651 };
1652
1653 /** Place an Airport.
1654 * @param tile tile where airport will be built
1655 * @param p1 airport type, @see airport.h
1656 * @param p2 unused
1657 */
1658 int32 CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
1659 {
1660 Town *t;
1661 Station *st;
1662 int32 cost;
1663 int32 ret;
1664 int w, h;
1665 bool airport_upgrade = true;
1666 const AirportFTAClass* afc;
1667
1668 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
1669
1670 /* Check if a valid, buildable airport was chosen for construction */
1671 if (p1 > lengthof(_airport_sections) || !HASBIT(GetValidAirports(), p1)) return CMD_ERROR;
1672
1673 if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
1674 return CMD_ERROR;
1675
1676 t = ClosestTownFromTile(tile, (uint)-1);
1677
1678 /* Check if local auth refuses a new airport */
1679 {
1680 uint num = 0;
1681 FOR_ALL_STATIONS(st) {
1682 if (st->town == t && st->facilities&FACIL_AIRPORT && st->airport_type != AT_OILRIG)
1683 num++;
1684 }
1685 if (num >= 2) {
1686 SetDParam(0, t->index);
1687 return_cmd_error(STR_2035_LOCAL_AUTHORITY_REFUSES);
1688 }
1689 }
1690
1691 afc = GetAirport(p1);
1692 w = afc->size_x;
1693 h = afc->size_y;
1694
1695 ret = CheckFlatLandBelow(tile, w, h, flags, 0, NULL);
1696 if (CmdFailed(ret)) return ret;
1697 cost = ret;
1698
1699 st = GetStationAround(tile, w, h, -1);
1700 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
1701
1702 /* Find a station close to us */
1703 if (st == NULL) {
1704 st = GetClosestStationFromTile(tile, 8, _current_player);
1705 if (st != NULL && st->facilities) st = NULL;
1706 }
1707
1708 if (w > _patches.station_spread || h > _patches.station_spread) {
1709 _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
1710 return CMD_ERROR;
1711 }
1712
1713 if (st != NULL) {
1714 if (st->owner != OWNER_NONE && st->owner != _current_player)
1715 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
1716
1717 if (!StationRect_BeforeAddRect(st, tile, w, h, RECT_MODE_TEST)) return CMD_ERROR;
1718
1719 if (st->airport_tile != 0)
1720 return_cmd_error(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT);
1721 } else {
1722 airport_upgrade = false;
1723
1724 st = AllocateStation();
1725 if (st == NULL) return CMD_ERROR;
1726
1727 st->town = t;
1728
1729 if (IsValidPlayer(_current_player) && (flags & DC_EXEC))
1730 SETBIT(t->have_ratings, _current_player);
1731
1732 st->sign.width_1 = 0;
1733
1734 // if airport type equals Heliport then generate
1735 // type 5 name, which is heliport, otherwise airport names (1)
1736 if (!GenerateStationName(st, tile, (p1 == AT_HELIPORT)||(p1 == AT_HELIDEPOT)||(p1 == AT_HELISTATION) ? 5 : 1))
1737 return CMD_ERROR;
1738
1739 if (flags & DC_EXEC) StationInitialize(st, tile);
1740 }
1741
1742 cost += _price.build_airport * w * h;
1743
1744 if (flags & DC_EXEC) {
1745 st->owner = _current_player;
1746 st->airport_tile = tile;
1747 if (!st->facilities) st->xy = tile;
1748 st->facilities |= FACIL_AIRPORT;
1749 st->airport_type = (byte)p1;
1750 st->airport_flags = 0;
1751
1752 st->build_date = _date;
1753
1754 StationRect_BeforeAddRect(st, tile, w, h, RECT_MODE_TRY);
1755
1756 /* if airport was demolished while planes were en-route to it, the
1757 * positions can no longer be the same (v->u.air.pos), since different
1758 * airports have different indexes. So update all planes en-route to this
1759 * airport. Only update if
1760 * 1. airport is upgraded
1761 * 2. airport is added to existing station (unfortunately unavoideable)
1762 */
1763 if (airport_upgrade) UpdateAirplanesOnNewStation(st);
1764
1765 {
1766 const byte *b = _airport_sections[p1];
1767
1768 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
1769 MakeAirport(tile_cur, st->owner, st->index, *b++);
1770 } END_TILE_LOOP(tile_cur, w, h, tile)
1771 }
1772
1773 UpdateStationVirtCoordDirty(st);
1774 UpdateStationAcceptance(st, false);
1775 RebuildStationLists();
1776 InvalidateWindow(WC_STATION_LIST, st->owner);
1777 }
1778
1779 return cost;
1780 }
1781
1782 static int32 RemoveAirport(Station *st, uint32 flags)
1783 {
1784 TileIndex tile;
1785 int w,h;
1786 int32 cost;
1787 const AirportFTAClass* afc;
1788
1789 if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
1790 return CMD_ERROR;
1791
1792 tile = st->airport_tile;
1793
1794 afc = GetAirport(st->airport_type);
1795 w = afc->size_x;
1796 h = afc->size_y;
1797
1798 cost = w * h * _price.remove_airport;
1799
1800 BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
1801 if (!EnsureNoVehicle(tile_cur)) return CMD_ERROR;
1802
1803 if (flags & DC_EXEC) {
1804 DeleteAnimatedTile(tile_cur);
1805 DoClearSquare(tile_cur);
1806 }
1807 } END_TILE_LOOP(tile_cur, w,h,tile)
1808
1809 if (flags & DC_EXEC) {
1810 uint i;
1811
1812 for (i = 0; i < afc->nof_depots; ++i) {
1813 DeleteWindowById(
1814 WC_VEHICLE_DEPOT, tile + ToTileIndexDiff(afc->airport_depots[i])
1815 );
1816 }
1817
1818 StationRect_AfterRemoveRect(st, tile, w, h);
1819
1820 st->airport_tile = 0;
1821 st->facilities &= ~FACIL_AIRPORT;
1822
1823 UpdateStationVirtCoordDirty(st);
1824 DeleteStationIfEmpty(st);
1825 }
1826
1827 return cost;
1828 }
1829
1830 /** Build a buoy.
1831 * @param tile tile where to place the bouy
1832 * @param p1 unused
1833 * @param p2 unused
1834 */
1835 int32 CmdBuildBuoy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
1836 {
1837 Station *st;
1838
1839 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
1840
1841 if (!IsClearWaterTile(tile) || tile == 0) return_cmd_error(STR_304B_SITE_UNSUITABLE);
1842
1843 st = AllocateStation();
1844 if (st == NULL) return CMD_ERROR;
1845
1846 st->town = ClosestTownFromTile(tile, (uint)-1);
1847 st->sign.width_1 = 0;
1848
1849 if (!GenerateStationName(st, tile, 4)) return CMD_ERROR;
1850
1851 if (flags & DC_EXEC) {
1852 StationInitialize(st, tile);
1853 st->dock_tile = tile;
1854 st->facilities |= FACIL_DOCK;
1855 /* Buoys are marked in the Station struct by this flag. Yes, it is this
1856 * braindead.. */
1857 st->had_vehicle_of_type |= HVOT_BUOY;
1858 st->owner = OWNER_NONE;
1859
1860 st->build_date = _date;
1861
1862 MakeBuoy(tile, st->index);
1863
1864 UpdateStationVirtCoordDirty(st);
1865 UpdateStationAcceptance(st, false);
1866 RebuildStationLists();
1867 InvalidateWindow(WC_STATION_LIST, st->owner);
1868 }
1869
1870 return _price.build_dock;
1871 }
1872
1873 /* Checks if any ship is servicing the buoy specified. Returns yes or no */
1874 static bool CheckShipsOnBuoy(Station *st)
1875 {
1876 const Vehicle *v;
1877 FOR_ALL_VEHICLES(v) {
1878 if (v->type == VEH_Ship) {
1879 const Order *order;
1880 FOR_VEHICLE_ORDERS(v, order) {
1881 if (order->type == OT_GOTO_STATION && order->dest == st->index) {
1882 return true;
1883 }
1884 }
1885 }
1886 }
1887 return false;
1888 }
1889
1890 static int32 RemoveBuoy(Station *st, uint32 flags)
1891 {
1892 TileIndex tile;
1893
1894 /* XXX: strange stuff */
1895 if (!IsValidPlayer(_current_player)) return_cmd_error(INVALID_STRING_ID);
1896
1897 tile = st->dock_tile;
1898
1899 if (CheckShipsOnBuoy(st)) return_cmd_error(STR_BUOY_IS_IN_USE);
1900 if (!EnsureNoVehicle(tile)) return CMD_ERROR;
1901
1902 if (flags & DC_EXEC) {
1903 st->dock_tile = 0;
1904 /* Buoys are marked in the Station struct by this flag. Yes, it is this
1905 * braindead.. */
1906 st->facilities &= ~FACIL_DOCK;
1907 st->had_vehicle_of_type &= ~HVOT_BUOY;
1908
1909 MakeWater(tile);
1910 MarkTileDirtyByTile(tile);
1911
1912 UpdateStationVirtCoordDirty(st);
1913 DeleteStationIfEmpty(st);
1914 }
1915
1916 return _price.remove_truck_station;
1917 }
1918
1919 static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
1920 {-1, 0},
1921 { 0, 0},
1922 { 0, 0},
1923 { 0, -1}
1924 };
1925 static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
1926 static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
1927
1928 /** Build a dock/haven.
1929 * @param tile tile where dock will be built
1930 * @param p1 unused
1931 * @param p2 unused
1932 */
1933 int32 CmdBuildDock(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
1934 {
1935 TileIndex tile_cur;
1936 DiagDirection direction;
1937 int32 cost;
1938 Station *st;
1939
1940 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
1941
1942 switch (GetTileSlope(tile, NULL)) {
1943 case SLOPE_SW: direction = DIAGDIR_NE; break;
1944 case SLOPE_SE: direction = DIAGDIR_NW; break;
1945 case SLOPE_NW: direction = DIAGDIR_SE; break;
1946 case SLOPE_NE: direction = DIAGDIR_SW; break;
1947 default: return_cmd_error(STR_304B_SITE_UNSUITABLE);
1948 }
1949
1950 if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile)) return CMD_ERROR;
1951
1952 if (!EnsureNoVehicle(tile)) return CMD_ERROR;
1953
1954 cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
1955 if (CmdFailed(cost)) return CMD_ERROR;
1956
1957 tile_cur = tile + TileOffsByDiagDir(direction);
1958
1959 if (!EnsureNoVehicle(tile_cur)) return CMD_ERROR;
1960
1961 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
1962 return_cmd_error(STR_304B_SITE_UNSUITABLE);
1963 }
1964
1965 cost = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
1966 if (CmdFailed(cost)) return CMD_ERROR;
1967
1968 tile_cur += TileOffsByDiagDir(direction);
1969 if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
1970 return_cmd_error(STR_304B_SITE_UNSUITABLE);
1971 }
1972
1973 /* middle */
1974 st = GetStationAround(
1975 tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
1976 _dock_w_chk[direction], _dock_h_chk[direction], -1);
1977 if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
1978
1979 /* Find a station close to us */
1980 if (st == NULL) {
1981 st = GetClosestStationFromTile(tile, 8, _current_player);
1982 if (st!=NULL && st->facilities) st = NULL;
1983 }
1984
1985 if (st != NULL) {
1986 if (st->owner != OWNER_NONE && st->owner != _current_player)
1987 return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
1988
1989 if (!StationRect_BeforeAddRect(st, tile, _dock_w_chk[direction], _dock_h_chk[direction], RECT_MODE_TEST)) return CMD_ERROR;
1990
1991 if (st->dock_tile != 0) return_cmd_error(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK);
1992 } else {
1993 Town *t;
1994
1995 st = AllocateStation();
1996 if (st == NULL) return CMD_ERROR;
1997
1998 st->town = t = ClosestTownFromTile(tile, (uint)-1);
1999
2000 if (IsValidPlayer(_current_player) && (flags & DC_EXEC))
2001 SETBIT(t->have_ratings, _current_player);
2002
2003 st->sign.width_1 = 0;
2004
2005 if (!GenerateStationName(st, tile, 3)) return CMD_ERROR;
2006
2007 if (flags & DC_EXEC) StationInitialize(st, tile);
2008 }
2009
2010 if (flags & DC_EXEC) {
2011 st->dock_tile = tile;
2012 if (!st->facilities) st->xy = tile;
2013 st->facilities |= FACIL_DOCK;
2014 st->owner = _current_player;
2015
2016 st->build_date = _date;
2017
2018 StationRect_BeforeAddRect(st, tile, _dock_w_chk[direction], _dock_h_chk[direction], RECT_MODE_TRY);
2019
2020 MakeDock(tile, st->owner, st->index, direction);
2021
2022 UpdateStationVirtCoordDirty(st);
2023 UpdateStationAcceptance(st, false);
2024 RebuildStationLists();
2025 InvalidateWindow(WC_STATION_LIST, st->owner);
2026 }
2027 return _price.build_dock;
2028 }
2029
2030 static int32 RemoveDock(Station *st, uint32 flags)
2031 {
2032 TileIndex tile1;
2033 TileIndex tile2;
2034
2035 if (!CheckOwnership(st->owner)) return CMD_ERROR;
2036
2037 tile1 = st->dock_tile;
2038 tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
2039
2040 if (!EnsureNoVehicle(tile1)) return CMD_ERROR;
2041 if (!EnsureNoVehicle(tile2)) return CMD_ERROR;
2042
2043 if (flags & DC_EXEC) {
2044 DoClearSquare(tile1);
2045 MakeWater(tile2);
2046
2047 StationRect_AfterRemoveTile(st, tile1);
2048 StationRect_AfterRemoveTile(st, tile2);
2049
2050 MarkTileDirtyByTile(tile2);
2051
2052 st->dock_tile = 0;
2053 st->facilities &= ~FACIL_DOCK;
2054
2055 UpdateStationVirtCoordDirty(st);
2056 DeleteStationIfEmpty(st);
2057 }
2058
2059 return _price.remove_dock;
2060 }
2061
2062 #include "table/station_land.h"
2063
2064 const DrawTileSprites *GetStationTileLayout(byte gfx)
2065 {
2066 return &_station_display_datas[gfx];
2067 }
2068
2069 static void DrawTile_Station(TileInfo *ti)
2070 {
2071 uint32 image;
2072 const DrawTileSeqStruct *dtss;
2073 const DrawTileSprites *t = NULL;
2074 RailType railtype = GetRailType(ti->tile);
2075 const RailtypeInfo *rti = GetRailTypeInfo(railtype);
2076 uint32 relocation = 0;
2077 const Station *st = NULL;
2078 const StationSpec *statspec = NULL;
2079 PlayerID owner = GetTileOwner(ti->tile);
2080 uint32 palette;
2081
2082 if (IsValidPlayer(owner)) {
2083 palette = PLAYER_SPRITE_COLOR(owner);
2084 } else {
2085 // Some stations are not owner by a player, namely oil rigs
2086 palette = PALETTE_TO_GREY;
2087 }
2088
2089 // don't show foundation for docks
2090 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile))
2091 DrawFoundation(ti, ti->tileh);
2092
2093 if (IsCustomStationSpecIndex(ti->tile)) {
2094 // look for customization
2095 st = GetStationByTile(ti->tile);
2096 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
2097
2098 //debug("Cust-o-mized %p", statspec);
2099
2100 if (statspec != NULL) {
2101 uint tile = GetStationGfx(ti->tile);
2102
2103 relocation = GetCustomStationRelocation(statspec, st, ti->tile);
2104
2105 if (HASBIT(statspec->callbackmask, CBM_CUSTOM_LAYOUT)) {
2106 uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
2107 if (callback != CALLBACK_FAILED) tile = (callback & ~1) + GetRailStationAxis(ti->tile);
2108 }
2109
2110 /* Ensure the chosen tile layout is valid for this custom station */
2111 if (statspec->renderdata != NULL) {
2112 t = &statspec->renderdata[tile < statspec->tiles ? tile : GetRailStationAxis(ti->tile)];
2113 }
2114 }
2115 }
2116
2117 if (t == NULL || t->seq == NULL) t = &_station_display_datas[GetStationGfx(ti->tile)];
2118
2119 image = t->ground_sprite;
2120 if (HASBIT(image, 31)) {
2121 CLRBIT(image, 31);
2122 image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
2123 image += rti->custom_ground_offset;
2124 } else {
2125 image += rti->total_offset;
2126 }
2127 if (image & PALETTE_MODIFIER_COLOR) image |= palette;
2128
2129 // station_land array has been increased from 82 elements to 114
2130 // but this is something else. If AI builds station with 114 it looks all weird
2131 DrawGroundSprite(image);
2132
2133 if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
2134
2135 foreach_draw_tile_seq(dtss, t->seq) {
2136 image = dtss->image;
2137 if (HASBIT(image, 30)) {
2138 CLRBIT(image, 30);
2139 image += rti->total_offset;
2140 } else {
2141 image += relocation;
2142 }
2143
2144 if (_display_opt & DO_TRANS_BUILDINGS) {
2145 MAKE_TRANSPARENT(image);
2146 } else if (image & PALETTE_MODIFIER_COLOR) {
2147 image |= palette;
2148 }
2149
2150 if ((byte)dtss->delta_z != 0x80) {
2151 AddSortableSpriteToDraw(
2152 image,
2153 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
2154 dtss->size_x, dtss->size_y,
2155 dtss->size_z, ti->z + dtss->delta_z
2156 );
2157 } else {
2158 AddChildSpriteScreen(image, dtss->delta_x, dtss->delta_y);
2159 }
2160 }
2161 }
2162
2163 void StationPickerDrawSprite(int x, int y, RailType railtype, int image)
2164 {
2165 uint32 ormod, img;
2166 const DrawTileSeqStruct *dtss;
2167 const DrawTileSprites *t;
2168 const RailtypeInfo *rti = GetRailTypeInfo(railtype);
2169
2170 ormod = PLAYER_SPRITE_COLOR(_local_player);
2171
2172 t = &_station_display_datas[image];
2173
2174 img = t->ground_sprite;
2175 if (img & PALETTE_MODIFIER_COLOR) img |= ormod;
2176 DrawSprite(img + rti->total_offset, x, y);
2177
2178 foreach_draw_tile_seq(dtss, t->seq) {
2179 Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
2180 DrawSprite((dtss->image | ormod) + rti->total_offset, x + pt.x, y + pt.y);
2181 }
2182 }
2183
2184 static uint GetSlopeZ_Station(TileIndex tile, uint x, uint y)
2185 {
2186 return GetTileMaxZ(tile);
2187 }
2188
2189 static Slope GetSlopeTileh_Station(TileIndex tile, Slope tileh)
2190 {
2191 return SLOPE_FLAT;
2192 }
2193
2194 static void GetAcceptedCargo_Station(TileIndex tile, AcceptedCargo ac)
2195 {
2196 /* not used */
2197 }
2198
2199 static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
2200 {
2201 StringID str;
2202
2203 td->owner = GetTileOwner(tile);
2204 td->build_date = GetStationByTile(tile)->build_date;
2205
2206 switch (GetStationType(tile)) {
2207 default: NOT_REACHED();
2208 case STATION_RAIL: str = STR_305E_RAILROAD_STATION; break;
2209 case STATION_AIRPORT:
2210 str = (IsHangar(tile) ? STR_305F_AIRCRAFT_HANGAR : STR_3060_AIRPORT);
2211 break;
2212 case STATION_TRUCK: str = STR_3061_TRUCK_LOADING_AREA; break;
2213 case STATION_BUS: str = STR_3062_BUS_STATION; break;
2214 case STATION_OILRIG: str = STR_4807_OIL_RIG; break;
2215 case STATION_DOCK: str = STR_3063_SHIP_DOCK; break;
2216 case STATION_BUOY: str = STR_3069_BUOY; break;
2217 }
2218 td->str = str;
2219 }
2220
2221
2222 static uint32 GetTileTrackStatus_Station(TileIndex tile, TransportType mode)
2223 {
2224 switch (mode) {
2225 case TRANSPORT_RAIL:
2226 if (IsRailwayStation(tile) && !IsStationTileBlocked(tile)) {
2227 return TrackToTrackBits(GetRailStationTrack(tile)) * 0x101;
2228 }
2229 break;
2230
2231 case TRANSPORT_WATER:
2232 // buoy is coded as a station, it is always on open water
2233 if (IsBuoy_(tile)) return TRACK_BIT_ALL * 0x101;
2234 break;
2235
2236 case TRANSPORT_ROAD:
2237 if (IsRoadStopTile(tile)) {
2238 return AxisToTrackBits(DiagDirToAxis(GetRoadStopDir(tile))) * 0x101;
2239 }
2240 break;
2241
2242 default:
2243 break;
2244 }
2245
2246 return 0;
2247 }
2248
2249
2250 static void TileLoop_Station(TileIndex tile)
2251 {
2252 // FIXME -- GetTileTrackStatus_Station -> animated stationtiles
2253 // hardcoded.....not good
2254 switch (GetStationGfx(tile)) {
2255 case GFX_RADAR_LARGE_FIRST:
2256 case GFX_WINDSACK_FIRST : // for small airport
2257 case GFX_RADAR_INTERNATIONAL_FIRST:
2258 case GFX_RADAR_METROPOLITAN_FIRST:
2259 case GFX_RADAR_DISTRICTWE_FIRST: // radar district W-E airport
2260 case GFX_WINDSACK_INTERCON_FIRST : // for intercontinental airport
2261 AddAnimatedTile(tile);
2262 break;
2263
2264 case GFX_OILRIG_BASE: //(station part)
2265 case GFX_BUOY_BASE:
2266 TileLoop_Water(tile);
2267 break;
2268
2269 default: break;
2270 }
2271 }
2272
2273
2274 static void AnimateTile_Station(TileIndex tile)
2275 {
2276 typedef struct AnimData {
2277 StationGfx from; // first sprite
2278 StationGfx to; // last sprite
2279 byte delay;
2280 } AnimData;
2281
2282 static const AnimData data[] = {
2283 { GFX_RADAR_LARGE_FIRST, GFX_RADAR_LARGE_LAST, 3 },
2284 { GFX_WINDSACK_FIRST, GFX_WINDSACK_LAST, 1 },
2285 { GFX_RADAR_INTERNATIONAL_FIRST, GFX_RADAR_INTERNATIONAL_LAST, 3 },
2286 { GFX_RADAR_METROPOLITAN_FIRST, GFX_RADAR_METROPOLITAN_LAST, 3 },
2287 { GFX_RADAR_DISTRICTWE_FIRST, GFX_RADAR_DISTRICTWE_LAST, 3 },
2288 { GFX_WINDSACK_INTERCON_FIRST, GFX_WINDSACK_INTERCON_LAST, 1 }
2289 };
2290
2291 StationGfx gfx = GetStationGfx(tile);
2292 const AnimData* i;
2293
2294 for (i = data; i != endof(data); i++) {
2295 if (i->from <= gfx && gfx <= i->to) {
2296 if ((_tick_counter & i->delay) == 0) {
2297 SetStationGfx(tile, gfx < i->to ? gfx + 1 : i->from);
2298 MarkTileDirtyByTile(tile);
2299 }
2300 break;
2301 }
2302 }
2303 }
2304
2305
2306 static void ClickTile_Station(TileIndex tile)
2307 {
2308 if (IsHangar(tile)) {
2309 ShowDepotWindow(tile, VEH_Aircraft);
2310 } else {
2311 ShowStationViewWindow(GetStationIndex(tile));
2312 }
2313 }
2314
2315 static const byte _enter_station_speedtable[12] = {
2316 215, 195, 175, 155, 135, 115, 95, 75, 55, 35, 15, 0
2317 };
2318
2319 static uint32 VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
2320 {
2321 if (v->type == VEH_Train) {
2322 if (IsRailwayStation(tile) && IsFrontEngine(v) &&
2323 !IsCompatibleTrainStationTile(tile + TileOffsByDiagDir(DirToDiagDir(v->direction)), tile)) {
2324 StationID station_id = GetStationIndex(tile);
2325
2326 if ((!(v->current_order.flags & OF_NON_STOP) && !_patches.new_nonstop) ||
2327 (v->current_order.type == OT_GOTO_STATION && v->current_order.dest == station_id)) {
2328 if (!(_patches.new_nonstop && v->current_order.flags & OF_NON_STOP) &&
2329 v->current_order.type != OT_LEAVESTATION &&
2330 v->last_station_visited != station_id) {
2331 DiagDirection dir = DirToDiagDir(v->direction);
2332
2333 x &= 0xF;
2334 y &= 0xF;
2335
2336 if (DiagDirToAxis(dir) != AXIS_X) intswap(x, y);
2337 if (y == TILE_SIZE / 2) {
2338 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
2339 if (x == 12) return 2 | (station_id << 8); /* enter station */
2340 if (x < 12) {
2341 uint16 spd;
2342
2343 v->vehstatus |= VS_TRAIN_SLOWING;
2344 spd = _enter_station_speedtable[x];
2345 if (spd < v->cur_speed) v->cur_speed = spd;
2346 }
2347 }
2348 }
2349 }
2350 }
2351 } else if (v->type == VEH_Road) {
2352 if (v->u.road.state < 16 && !HASBIT(v->u.road.state, 2) && v->u.road.frame == 0) {
2353 if (IsRoadStop(tile)) {
2354 /* Attempt to allocate a parking bay in a road stop */
2355 RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
2356
2357 /* rs->status bits 0 and 1 describe current the two parking spots.
2358 * 0 means occupied, 1 means free. */
2359
2360 // Check if station is busy or if there are no free bays.
2361 if (HASBIT(rs->status, 7) || GB(rs->status, 0, 2) == 0)
2362 return 8;
2363
2364 v->u.road.state += 32;
2365
2366 // if the first bay is free, allocate that, else the second bay must be free.
2367 if (HASBIT(rs->status, 0)) {
2368 CLRBIT(rs->status, 0);
2369 } else {
2370 CLRBIT(rs->status, 1);
2371 v->u.road.state += 2;
2372 }
2373
2374 // mark the station as busy
2375 SETBIT(rs->status, 7);
2376 }
2377 }
2378 }
2379
2380 return 0;
2381 }
2382
2383 /**
2384 * Cleanup a RoadStop. Make sure no vehicles try to go to this roadstop.
2385 */
2386 void DestroyRoadStop(RoadStop* rs)
2387 {
2388 Vehicle *v;
2389
2390 /* Clear the slot assignment of all vehicles heading for this road stop */
2391 if (rs->num_vehicles != 0) {
2392 FOR_ALL_VEHICLES(v) {
2393 if (v->type == VEH_Road && v->u.road.slot == rs) {
2394 ClearSlot(v);
2395 }
2396 }
2397 }
2398 assert(rs->num_vehicles == 0);
2399
2400 if (rs->prev != NULL) rs->prev->next = rs->next;
2401 if (rs->next != NULL) rs->next->prev = rs->prev;
2402 }
2403
2404 /**
2405 * Clean up a station by clearing vehicle orders and invalidating windows.
2406 * Aircraft-Hangar orders need special treatment here, as the hangars are
2407 * actually part of a station (tiletype is STATION), but the order type
2408 * is OT_GOTO_DEPOT.
2409 * @param st Station to be deleted
2410 */
2411 void DestroyStation(Station *st)
2412 {
2413 StationID index;
2414
2415 index = st->index;
2416
2417 DeleteName(st->string_id);
2418 MarkStationDirty(st);
2419 RebuildStationLists();
2420 InvalidateWindowClasses(WC_STATION_LIST);
2421
2422 DeleteWindowById(WC_STATION_VIEW, index);
2423
2424 /* Now delete all orders that go to the station */
2425 RemoveOrderFromAllVehicles(OT_GOTO_STATION, index);
2426
2427 //Subsidies need removal as well
2428 DeleteSubsidyWithStation(index);
2429
2430 free(st->speclist);
2431 }
2432
2433 void DeleteAllPlayerStations(void)
2434 {
2435 Station *st;
2436
2437 FOR_ALL_STATIONS(st) {
2438 if (IsValidPlayer(st->owner)) DeleteStation(st);
2439 }
2440 }
2441
2442 /* this function is called for one station each tick */
2443 static void StationHandleBigTick(Station *st)
2444 {
2445 UpdateStationAcceptance(st, true);
2446
2447 if (st->facilities == 0 && ++st->delete_ctr >= 8) DeleteStation(st);
2448
2449 }
2450
2451 static inline void byte_inc_sat(byte *p) { byte b = *p + 1; if (b != 0) *p = b; }
2452
2453 static void UpdateStationRating(Station *st)
2454 {
2455 GoodsEntry *ge;
2456 int rating;
2457 StationID index;
2458 int waiting;
2459 bool waiting_changed = false;
2460
2461 byte_inc_sat(&st->time_since_load);
2462 byte_inc_sat(&st->time_since_unload);
2463
2464 ge = st->goods;
2465 do {
2466 if (ge->enroute_from != INVALID_STATION) {
2467 byte_inc_sat(&ge->enroute_time);
2468 byte_inc_sat(&ge->days_since_pickup);
2469
2470 rating = 0;
2471
2472 {
2473 int b = ge->last_speed;
2474 if ((b-=85) >= 0)
2475 rating += b >> 2;
2476 }
2477
2478 {
2479 byte age = ge->last_age;
2480 (age >= 3) ||
2481 (rating += 10, age >= 2) ||
2482 (rating += 10, age >= 1) ||
2483 (rating += 13, true);
2484 }
2485
2486 if (IsValidPlayer(st->owner) && HASBIT(st->town->statues, st->owner)) rating += 26;
2487
2488 {
2489 byte days = ge->days_since_pickup;
2490 if (st->last_vehicle_type == VEH_Ship)
2491 days >>= 2;
2492 (days > 21) ||
2493 (rating += 25, days > 12) ||
2494 (rating += 25, days > 6) ||
2495 (rating += 45, days > 3) ||
2496 (rating += 35, true);
2497 }
2498
2499 {
2500 waiting = GB(ge->waiting_acceptance, 0, 12);
2501 (rating -= 90, waiting > 1500) ||
2502 (rating += 55, waiting > 1000) ||
2503 (rating += 35, waiting > 600) ||
2504 (rating += 10, waiting > 300) ||
2505 (rating += 20, waiting > 100) ||
2506 (rating += 10, true);
2507 }
2508
2509 {
2510 int or = ge->rating; // old rating
2511
2512 // only modify rating in steps of -2, -1, 0, 1 or 2
2513 ge->rating = rating = or + clamp(clamp(rating, 0, 255) - or, -2, 2);
2514
2515 // if rating is <= 64 and more than 200 items waiting, remove some random amount of goods from the station
2516 if (rating <= 64 && waiting >= 200) {
2517 int dec = Random() & 0x1F;
2518 if (waiting < 400) dec &= 7;
2519 waiting -= dec + 1;
2520 waiting_changed = true;
2521 }
2522
2523 // if rating is <= 127 and there are any items waiting, maybe remove some goods.
2524 if (rating <= 127 && waiting != 0) {
2525 uint32 r = Random();
2526 if ( (uint)rating <= (r & 0x7F) ) {
2527 waiting = max(waiting - ((r >> 8)&3) - 1, 0);
2528 waiting_changed = true;
2529 }
2530 }
2531
2532 if (waiting_changed) SB(ge->waiting_acceptance, 0, 12, waiting);
2533 }
2534 }
2535 } while (++ge != endof(st->goods));
2536
2537 index = st->index;
2538
2539 if (waiting_changed) {
2540 InvalidateWindow(WC_STATION_VIEW, index);
2541 } else {
2542 InvalidateWindowWidget(WC_STATION_VIEW, index, 5);
2543 }
2544 }
2545
2546 /* called for every station each tick */
2547 static void StationHandleSmallTick(Station *st)
2548 {
2549 byte b;
2550
2551 if (st->facilities == 0) return;
2552
2553 b = st->delete_ctr + 1;
2554 if (b >= 185) b = 0;
2555 st->delete_ctr = b;
2556
2557 if (b == 0) UpdateStationRating(st);
2558 }
2559
2560 void OnTick_Station(void)
2561 {
2562 uint i;
2563 Station *st;
2564
2565 if (_game_mode == GM_EDITOR) return;
2566
2567 i = _station_tick_ctr;
2568 if (++_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0;
2569
2570 if (IsValidStationID(i)) StationHandleBigTick(GetStation(i));
2571
2572 FOR_ALL_STATIONS(st) {
2573 StationHandleSmallTick(st);
2574 }
2575 }
2576
2577 void StationMonthlyLoop(void)
2578 {
2579 }
2580
2581
2582 void ModifyStationRatingAround(TileIndex tile, PlayerID owner, int amount, uint radius)
2583 {
2584 Station *st;
2585
2586 FOR_ALL_STATIONS(st) {
2587 if (st->owner == owner &&
2588 DistanceManhattan(tile, st->xy) <= radius) {
2589 uint i;
2590
2591 for (i = 0; i != NUM_CARGO; i++) {
2592 GoodsEntry* ge = &st->goods[i];
2593
2594 if (ge->enroute_from != INVALID_STATION) {
2595 ge->rating = clamp(ge->rating + amount, 0, 255);
2596 }
2597 }
2598 }
2599 }
2600 }
2601
2602 static void UpdateStationWaiting(Station *st, int type, uint amount)
2603 {
2604 SB(st->goods[type].waiting_acceptance, 0, 12,
2605 min(0xFFF, GB(st->goods[type].waiting_acceptance, 0, 12) + amount)
2606 );
2607
2608 st->goods[type].enroute_time = 0;
2609 st->goods[type].enroute_from = st->index;
2610 InvalidateWindow(WC_STATION_VIEW, st->index);
2611 MarkStationTilesDirty(st);
2612 }
2613
2614 /** Rename a station
2615 * @param tile unused
2616 * @param p1 station ID that is to be renamed
2617 * @param p2 unused
2618 */
2619 int32 CmdRenameStation(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
2620 {
2621 StringID str;
2622 Station *st;
2623
2624 if (!IsValidStationID(p1) || _cmd_text[0] == '\0') return CMD_ERROR;
2625 st = GetStation(p1);
2626
2627 if (!CheckOwnership(st->owner)) return CMD_ERROR;
2628
2629 str = AllocateNameUnique(_cmd_text, 6);
2630 if (str == 0) return CMD_ERROR;
2631
2632 if (flags & DC_EXEC) {
2633 StringID old_str = st->string_id;
2634
2635 st->string_id = str;
2636 UpdateStationVirtCoord(st);
2637 DeleteName(old_str);
2638 ResortStationLists();
2639 MarkWholeScreenDirty();
2640 } else {
2641 DeleteName(str);
2642 }
2643
2644 return 0;
2645 }
2646
2647
2648 uint MoveGoodsToStation(TileIndex tile, int w, int h, int type, uint amount)
2649 {
2650 Station* around[8];
2651 uint i;
2652 uint moved;
2653 uint best_rating, best_rating2;
2654 Station *st1, *st2;
2655 uint t;
2656 int rad = 0;
2657 int w_prod; //width and height of the "producer" of the cargo
2658 int h_prod;
2659 int max_rad;
2660
2661 for (i = 0; i < lengthof(around); i++) around[i] = NULL;
2662
2663 if (_patches.modified_catchment) {
2664 w_prod = w;
2665 h_prod = h;
2666 w += 16;
2667 h += 16;
2668 max_rad = 8;
2669 } else {
2670 w_prod = 0;
2671 h_prod = 0;
2672 w += 8;
2673 h += 8;
2674 max_rad = 4;
2675 }
2676
2677 BEGIN_TILE_LOOP(cur_tile, w, h, tile - TileDiffXY(max_rad, max_rad))
2678 Station* st;
2679
2680 cur_tile = TILE_MASK(cur_tile);
2681 if (!IsTileType(cur_tile, MP_STATION)) continue;
2682
2683 st = GetStationByTile(cur_tile);
2684
2685 for (i = 0; i != lengthof(around); i++) {
2686 if (around[i] == NULL) {
2687 if (!IsBuoy(st) &&
2688 (st->town->exclusive_counter == 0 || st->town->exclusivity == st->owner) && // check exclusive transport rights
2689 st->goods[type].rating != 0 &&
2690 (!_patches.selectgoods || st->goods[type].last_speed > 0) && // if last_speed is 0, no vehicle has been there.
2691 ((st->facilities & ~FACIL_BUS_STOP) != 0 || type == CT_PASSENGERS) && // if we have other fac. than a bus stop, or the cargo is passengers
2692 ((st->facilities & ~FACIL_TRUCK_STOP) != 0 || type != CT_PASSENGERS)) { // if we have other fac. than a cargo bay or the cargo is not passengers
2693 int x_dist;
2694 int y_dist;
2695
2696 if (_patches.modified_catchment) {
2697 // min and max coordinates of the producer relative
2698 const int x_min_prod = 9;
2699 const int x_max_prod = 8 + w_prod;
2700 const int y_min_prod = 9;
2701 const int y_max_prod = 8 + h_prod;
2702
2703 rad = FindCatchmentRadius(st);
2704
2705 x_dist = min(w_cur - x_min_prod, x_max_prod - w_cur);
2706 if (w_cur < x_min_prod) {
2707 x_dist = x_min_prod - w_cur;
2708 } else if (w_cur > x_max_prod) {
2709 x_dist = w_cur - x_max_prod;
2710 }
2711
2712 y_dist = min(h_cur - y_min_prod, y_max_prod - h_cur);
2713 if (h_cur < y_min_prod) {
2714 y_dist = y_min_prod - h_cur;
2715 } else if (h_cur > y_max_prod) {
2716 y_dist = h_cur - y_max_prod;
2717 }
2718 } else {
2719 x_dist = 0;
2720 y_dist = 0;
2721 }
2722
2723 if (x_dist <= rad && y_dist <= rad) around[i] = st;
2724 }
2725 break;
2726 } else if (around[i] == st) {
2727 break;
2728 }
2729 }
2730 END_TILE_LOOP(cur_tile, w, h, tile - TileDiffXY(max_rad, max_rad))
2731
2732 /* no stations around at all? */
2733 if (around[0] == NULL) return 0;
2734
2735 if (around[1] == NULL) {
2736 /* only one station around */
2737 moved = (amount * around[0]->goods[type].rating >> 8) + 1;
2738 UpdateStationWaiting(around[0], type, moved);
2739 return moved;
2740 }
2741
2742 /* several stations around, find the two with the highest rating */
2743 st2 = st1 = NULL;
2744 best_rating = best_rating2 = 0;
2745
2746 for (i = 0; i != lengthof(around) && around[i] != NULL; i++) {
2747 if (around[i]->goods[type].rating >= best_rating) {
2748 best_rating2 = best_rating;
2749 st2 = st1;
2750
2751 best_rating = around[i]->goods[type].rating;
2752 st1 = around[i];
2753 } else if (around[i]->goods[type].rating >= best_rating2) {
2754 best_rating2 = around[i]->goods[type].rating;
2755 st2 = around[i];
2756 }
2757 }
2758
2759 assert(st1 != NULL);
2760 assert(st2 != NULL);
2761 assert(best_rating != 0 || best_rating2 != 0);
2762
2763 /* the 2nd highest one gets a penalty */
2764 best_rating2 >>= 1;
2765
2766 /* amount given to station 1 */
2767 t = (best_rating * (amount + 1)) / (best_rating + best_rating2);
2768
2769 moved = 0;
2770 if (t != 0) {
2771 moved = t * best_rating / 256 + 1;
2772 amount -= t;
2773 UpdateStationWaiting(st1, type, moved);
2774 }
2775
2776 if (amount != 0) {
2777 amount = amount * best_rating2 / 256 + 1;
2778 moved += amount;
2779 UpdateStationWaiting(st2, type, amount);
2780 }
2781
2782 return moved;
2783 }
2784
2785 void BuildOilRig(TileIndex tile)
2786 {
2787 uint j;
2788 Station *st = AllocateStation();
2789
2790 if (st == NULL) {
2791 DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
2792 return;
2793 }
2794 if (!GenerateStationName(st, tile, 2)) {
2795 DEBUG(misc, 0, "Can't allocate station-name for oilrig at 0x%X, reverting to oilrig only", tile);
2796 return;
2797 }
2798
2799 st->town = ClosestTownFromTile(tile, (uint)-1);
2800 st->sign.width_1 = 0;
2801
2802 MakeOilrig(tile, st->index);
2803
2804 st->owner = OWNER_NONE;
2805 st->airport_flags = 0;
2806 st->airport_type = AT_OILRIG;
2807 st->xy = tile;
2808 st->bus_stops = NULL;
2809 st->truck_stops = NULL;
2810 st->airport_tile = tile;
2811 st->dock_tile = tile;
2812 st->train_tile = 0;
2813 st->had_vehicle_of_type = 0;
2814 st->time_since_load = 255;
2815 st->time_since_unload = 255;
2816 st->delete_ctr = 0;
2817 st->last_vehicle_type = VEH_Invalid;
2818 st->facilities = FACIL_AIRPORT | FACIL_DOCK;
2819 st->build_date = _date;
2820
2821 for (j = 0; j != NUM_CARGO; j++) {
2822 st->goods[j].waiting_acceptance = 0;
2823 st->goods[j].days_since_pickup = 0;
2824 st->goods[j].enroute_from = INVALID_STATION;
2825 st->goods[j].rating = 175;
2826 st->goods[j].last_speed = 0;
2827 st->goods[j].last_age = 255;
2828 }
2829
2830 UpdateStationVirtCoordDirty(st);
2831 UpdateStationAcceptance(st, false);
2832 }
2833
2834 void DeleteOilRig(TileIndex tile)
2835 {
2836 Station* st = GetStationByTile(tile);
2837
2838 DoClearSquare(tile);
2839
2840 st->dock_tile = 0;
2841 st->airport_tile = 0;
2842 st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
2843 st->airport_flags = 0;
2844 UpdateStationVirtCoordDirty(st);
2845 DeleteStation(st);
2846 }
2847
2848 static void ChangeTileOwner_Station(TileIndex tile, PlayerID old_player, PlayerID new_player)
2849 {
2850 if (!IsTileOwner(tile, old_player)) return;
2851
2852 if (new_player != PLAYER_SPECTATOR) {
2853 Station* st = GetStationByTile(tile);
2854
2855 SetTileOwner(tile, new_player);
2856 st->owner = new_player;
2857 RebuildStationLists();
2858 InvalidateWindowClasses(WC_STATION_LIST);
2859 } else {
2860 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
2861 }
2862 }
2863
2864 static int32 ClearTile_Station(TileIndex tile, byte flags)
2865 {
2866 Station *st;
2867
2868 if (flags & DC_AUTO) {
2869 switch (GetStationType(tile)) {
2870 case STATION_RAIL: return_cmd_error(STR_300B_MUST_DEMOLISH_RAILROAD);
2871 case STATION_AIRPORT: return_cmd_error(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST);
2872 case STATION_TRUCK: return_cmd_error(STR_3047_MUST_DEMOLISH_TRUCK_STATION);
2873 case STATION_BUS: return_cmd_error(STR_3046_MUST_DEMOLISH_BUS_STATION);
2874 case STATION_BUOY: return_cmd_error(STR_306A_BUOY_IN_THE_WAY);
2875 case STATION_DOCK: return_cmd_error(STR_304D_MUST_DEMOLISH_DOCK_FIRST);
2876 case STATION_OILRIG:
2877 SetDParam(0, STR_4807_OIL_RIG);
2878 return_cmd_error(STR_4800_IN_THE_WAY);
2879 }
2880 }
2881
2882 st = GetStationByTile(tile);
2883
2884 switch (GetStationType(tile)) {
2885 case STATION_RAIL: return RemoveRailroadStation(st, tile, flags);
2886 case STATION_AIRPORT: return RemoveAirport(st, flags);
2887 case STATION_TRUCK:
2888 case STATION_BUS: return RemoveRoadStop(st, flags, tile);
2889 case STATION_BUOY: return RemoveBuoy(st, flags);
2890 case STATION_DOCK: return RemoveDock(st, flags);
2891 default: break;
2892 }
2893
2894 return CMD_ERROR;
2895 }
2896
2897 void InitializeStations(void)
2898 {
2899 /* Clean the station pool and create 1 block in it */
2900 CleanPool(&_Station_pool);
2901 AddBlockToPool(&_Station_pool);
2902
2903 /* Clean the roadstop pool and create 1 block in it */
2904 CleanPool(&_RoadStop_pool);
2905 AddBlockToPool(&_RoadStop_pool);
2906
2907 _station_tick_ctr = 0;
2908
2909 }
2910
2911
2912 void AfterLoadStations(void)
2913 {
2914 Station *st;
2915 uint i;
2916 TileIndex tile;
2917
2918 /* Update the speclists of all stations to point to the currently loaded custom stations. */
2919 FOR_ALL_STATIONS(st) {
2920 for (i = 0; i < st->num_specs; i++) {
2921 if (st->speclist[i].grfid == 0) continue;
2922
2923 st->speclist[i].spec = GetCustomStationSpecByGrf(st->speclist[i].grfid, st->speclist[i].localidx);
2924 }
2925 }
2926
2927 for (tile = 0; tile < MapSize(); tile++) {
2928 if (GetTileType(tile) != MP_STATION) continue;
2929 st = GetStationByTile(tile);
2930 StationRect_BeforeAddTile(st, tile, RECT_MODE_FORCE);
2931 }
2932 }
2933
2934
2935 const TileTypeProcs _tile_type_station_procs = {
2936 DrawTile_Station, /* draw_tile_proc */
2937 GetSlopeZ_Station, /* get_slope_z_proc */
2938 ClearTile_Station, /* clear_tile_proc */
2939 GetAcceptedCargo_Station, /* get_accepted_cargo_proc */
2940 GetTileDesc_Station, /* get_tile_desc_proc */
2941 GetTileTrackStatus_Station, /* get_tile_track_status_proc */
2942 ClickTile_Station, /* click_tile_proc */
2943 AnimateTile_Station, /* animate_tile_proc */
2944 TileLoop_Station, /* tile_loop_clear */
2945 ChangeTileOwner_Station, /* change_tile_owner_clear */
2946 NULL, /* get_produced_cargo_proc */
2947 VehicleEnter_Station, /* vehicle_enter_tile_proc */
2948 GetSlopeTileh_Station, /* get_slope_tileh_proc */
2949 };
2950
2951 static const SaveLoad _roadstop_desc[] = {
2952 SLE_VAR(RoadStop,xy, SLE_UINT32),
2953 SLE_VAR(RoadStop,used, SLE_BOOL),
2954 SLE_VAR(RoadStop,status, SLE_UINT8),
2955 /* Index was saved in some versions, but this is not needed */
2956 SLE_CONDNULL(4, 0, 8),
2957 SLE_VAR(RoadStop,station, SLE_UINT16),
2958 SLE_CONDNULL(1, 0, 25),
2959
2960 SLE_REF(RoadStop,next, REF_ROADSTOPS),
2961 SLE_REF(RoadStop,prev, REF_ROADSTOPS),
2962
2963 SLE_CONDNULL(4, 0, 24),
2964 SLE_CONDNULL(1, 25, 25),
2965
2966 SLE_END()
2967 };
2968
2969 static const SaveLoad _station_desc[] = {
2970 SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
2971 SLE_CONDVAR(Station, xy, SLE_UINT32, 6, SL_MAX_VERSION),
2972 SLE_CONDVAR(Station, bus_tile_obsolete, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
2973 SLE_CONDVAR(Station, lorry_tile_obsolete, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
2974 SLE_CONDVAR(Station, train_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
2975 SLE_CONDVAR(Station, train_tile, SLE_UINT32, 6, SL_MAX_VERSION),
2976 SLE_CONDVAR(Station, airport_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
2977 SLE_CONDVAR(Station, airport_tile, SLE_UINT32, 6, SL_MAX_VERSION),
2978 SLE_CONDVAR(Station, dock_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
2979 SLE_CONDVAR(Station, dock_tile, SLE_UINT32, 6, SL_MAX_VERSION),
2980 SLE_REF(Station, town, REF_TOWN),
2981 SLE_VAR(Station, trainst_w, SLE_UINT8),
2982 SLE_CONDVAR(Station, trainst_h, SLE_UINT8, 2, SL_MAX_VERSION),
2983
2984 // alpha_order was stored here in savegame format 0 - 3
2985 SLE_CONDNULL(1, 0, 3),
2986
2987 SLE_VAR(Station, string_id, SLE_STRINGID),
2988 SLE_VAR(Station, had_vehicle_of_type, SLE_UINT16),
2989
2990 SLE_VAR(Station, time_since_load, SLE_UINT8),
2991 SLE_VAR(Station, time_since_unload, SLE_UINT8),
2992 SLE_VAR(Station, delete_ctr, SLE_UINT8),
2993 SLE_VAR(Station, owner, SLE_UINT8),
2994 SLE_VAR(Station, facilities, SLE_UINT8),
2995 SLE_VAR(Station, airport_type, SLE_UINT8),
2996
2997 // truck/bus_stop_status was stored here in savegame format 0 - 6
2998 SLE_CONDVAR(Station, truck_stop_status_obsolete, SLE_UINT8, 0, 5),
2999 SLE_CONDVAR(Station, bus_stop_status_obsolete, SLE_UINT8, 0, 5),
3000
3001 // blocked_months was stored here in savegame format 0 - 4.0
3002 SLE_CONDVAR(Station, blocked_months_obsolete, SLE_UINT8, 0, 4),
3003
3004 SLE_CONDVAR(Station, airport_flags, SLE_VAR_U32 | SLE_FILE_U16, 0, 2),
3005 SLE_CONDVAR(Station, airport_flags, SLE_UINT32, 3, SL_MAX_VERSION),
3006
3007 SLE_CONDNULL(2, 0, 25), /* Ex last-vehicle */
3008 SLE_CONDVAR(Station, last_vehicle_type, SLE_UINT8, 26, SL_MAX_VERSION),
3009
3010 // Was custom station class and id
3011 SLE_CONDNULL(2, 3, 25),
3012 SLE_CONDVAR(Station, build_date, SLE_FILE_U16 | SLE_VAR_I32, 3, 30),
3013 SLE_CONDVAR(Station, build_date, SLE_INT32, 31, SL_MAX_VERSION),
3014
3015 SLE_CONDREF(Station, bus_stops, REF_ROADSTOPS, 6, SL_MAX_VERSION),
3016 SLE_CONDREF(Station, truck_stops, REF_ROADSTOPS, 6, SL_MAX_VERSION),
3017
3018 /* Used by newstations for graphic variations */
3019 SLE_CONDVAR(Station, random_bits, SLE_UINT16, 27, SL_MAX_VERSION),
3020 SLE_CONDVAR(Station, waiting_triggers, SLE_UINT8, 27, SL_MAX_VERSION),
3021 SLE_CONDVAR(Station, num_specs, SLE_UINT8, 27, SL_MAX_VERSION),
3022
3023 // reserve extra space in savegame here. (currently 32 bytes)
3024 SLE_CONDNULL(32, 2, SL_MAX_VERSION),
3025
3026 SLE_END()
3027 };
3028
3029 static const SaveLoad _goods_desc[] = {
3030 SLE_VAR(GoodsEntry, waiting_acceptance, SLE_UINT16),
3031 SLE_VAR(GoodsEntry, days_since_pickup, SLE_UINT8),
3032 SLE_VAR(GoodsEntry, rating, SLE_UINT8),
3033 SLE_CONDVAR(GoodsEntry, enroute_from, SLE_FILE_U8 | SLE_VAR_U16, 0, 6),
3034 SLE_CONDVAR(GoodsEntry, enroute_from, SLE_UINT16, 7, SL_MAX_VERSION),
3035 SLE_VAR(GoodsEntry, enroute_time, SLE_UINT8),
3036 SLE_VAR(GoodsEntry, last_speed, SLE_UINT8),
3037 SLE_VAR(GoodsEntry, last_age, SLE_UINT8),
3038 SLE_CONDVAR(GoodsEntry, feeder_profit, SLE_INT32, 14, SL_MAX_VERSION),
3039
3040 SLE_END()
3041 };
3042
3043 static const SaveLoad _station_speclist_desc[] = {
3044 SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, 27, SL_MAX_VERSION),
3045 SLE_CONDVAR(StationSpecList, localidx, SLE_UINT8, 27, SL_MAX_VERSION),
3046
3047 SLE_END()
3048 };
3049
3050
3051 static void SaveLoad_STNS(Station *st)
3052 {
3053 uint i;
3054
3055 SlObject(st, _station_desc);
3056 for (i = 0; i != NUM_CARGO; i++) {
3057 SlObject(&st->goods[i], _goods_desc);
3058
3059 /* In older versions, enroute_from had 0xFF as INVALID_STATION, is now 0xFFFF */
3060 if (CheckSavegameVersion(7) && st->goods[i].enroute_from == 0xFF) {
3061 st->goods[i].enroute_from = INVALID_STATION;
3062 }
3063 }
3064
3065 if (st->num_specs != 0) {
3066 /* Allocate speclist memory when loading a game */
3067 if (st->speclist == NULL) st->speclist = calloc(st->num_specs, sizeof(*st->speclist));
3068 for (i = 0; i < st->num_specs; i++) SlObject(&st->speclist[i], _station_speclist_desc);
3069 }
3070 }
3071
3072 static void Save_STNS(void)
3073 {
3074 Station *st;
3075 // Write the stations
3076 FOR_ALL_STATIONS(st) {
3077 SlSetArrayIndex(st->index);
3078 SlAutolength((AutolengthProc*)SaveLoad_STNS, st);
3079 }
3080 }
3081
3082 static void Load_STNS(void)
3083 {
3084 int index;
3085 while ((index = SlIterateArray()) != -1) {
3086 Station *st;
3087
3088 if (!AddBlockIfNeeded(&_Station_pool, index))
3089 error("Stations: failed loading savegame: too many stations");
3090
3091 st = GetStation(index);
3092 SaveLoad_STNS(st);
3093
3094 // this means it's an oldstyle savegame without support for nonuniform stations
3095 if (st->train_tile != 0 && st->trainst_h == 0) {
3096 uint w = GB(st->trainst_w, 4, 4);
3097 uint h = GB(st->trainst_w, 0, 4);
3098
3099 if (GetRailStationAxis(st->train_tile) == AXIS_Y) uintswap(w, h);
3100 st->trainst_w = w;
3101 st->trainst_h = h;
3102 }
3103
3104 /* In older versions, we had just 1 tile for a bus/lorry, now we have more..
3105 * convert, if needed */
3106 if (CheckSavegameVersion(6)) {
3107 if (st->bus_tile_obsolete != 0) {
3108 st->bus_stops = AllocateRoadStop();
3109 if (st->bus_stops == NULL)
3110 error("Station: too many busstations in savegame");
3111
3112 InitializeRoadStop(st->bus_stops, NULL, st->bus_tile_obsolete, st->index);
3113 }
3114 if (st->lorry_tile_obsolete != 0) {
3115 st->truck_stops = AllocateRoadStop();
3116 if (st->truck_stops == NULL)
3117 error("Station: too many truckstations in savegame");
3118
3119 InitializeRoadStop(st->truck_stops, NULL, st->lorry_tile_obsolete, st->index);
3120 }
3121 }
3122 }
3123
3124 /* This is to ensure all pointers are within the limits of _stations_size */
3125 if (_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0;
3126 }
3127
3128 static void Save_ROADSTOP(void)
3129 {
3130 RoadStop *rs;
3131
3132 FOR_ALL_ROADSTOPS(rs) {
3133 SlSetArrayIndex(rs->index);
3134 SlObject(rs, _roadstop_desc);
3135 }
3136 }
3137
3138 static void Load_ROADSTOP(void)
3139 {
3140 int index;
3141 Vehicle *v;
3142
3143 while ((index = SlIterateArray()) != -1) {
3144 RoadStop *rs;
3145
3146 if (!AddBlockIfNeeded(&_RoadStop_pool, index))
3147 error("RoadStops: failed loading savegame: too many RoadStops");
3148
3149 rs = GetRoadStop(index);
3150 SlObject(rs, _roadstop_desc);
3151 }
3152
3153 FOR_ALL_VEHICLES(v) {
3154 if (v->type == VEH_Road && v->u.road.slot != NULL) v->u.road.slot->num_vehicles++;
3155 }
3156 }
3157
3158 const ChunkHandler _station_chunk_handlers[] = {
3159 { 'STNS', Save_STNS, Load_STNS, CH_ARRAY },
3160 { 'ROAD', Save_ROADSTOP, Load_ROADSTOP, CH_ARRAY | CH_LAST},
3161 };
3162
3163
3164 static inline bool PtInRectXY(Rect *r, int x, int y)
3165 {
3166 return (r->left <= x && x <= r->right && r->top <= y && y <= r->bottom);
3167 }
3168
3169 static void StationRect_Init(Station *st)
3170 {
3171 Rect *r = &st->rect;
3172 r->left = r->top = r->right = r->bottom = 0;
3173 }
3174
3175 static bool StationRect_IsEmpty(Station *st)
3176 {
3177 return (st->rect.left == 0 || st->rect.left > st->rect.right || st->rect.top > st->rect.bottom);
3178 }
3179
3180 static bool StationRect_BeforeAddTile(Station *st, TileIndex tile, StationRectMode mode)
3181 {
3182 Rect *r = &st->rect;
3183 int x = TileX(tile);
3184 int y = TileY(tile);
3185 if (StationRect_IsEmpty(st)) {
3186 // we are adding the first station tile
3187 r->left = r->right = x;
3188 r->top = r->bottom = y;
3189 } else if (!PtInRectXY(r, x, y)) {
3190 // current rect is not empty and new point is outside this rect
3191 // make new spread-out rectangle
3192 Rect new_rect = {min(x, r->left), min(y, r->top), max(x, r->right), max(y, r->bottom)};
3193 // check new rect dimensions against preset max
3194 int w = new_rect.right - new_rect.left + 1;
3195 int h = new_rect.bottom - new_rect.top + 1;
3196 if (mode != RECT_MODE_FORCE && (w > _patches.station_spread || h > _patches.station_spread)) {
3197 assert(mode != RECT_MODE_TRY);
3198 _error_message = STR_306C_STATION_TOO_SPREAD_OUT;
3199 return false;
3200 }
3201 // spread-out ok, return true
3202 if (mode != RECT_MODE_TEST) {
3203 // we should update the station rect
3204 *r = new_rect;
3205 }
3206 } else {
3207 ; // new point is inside the rect, we don't need to do anything
3208 }
3209 return true;
3210 }
3211
3212 static bool StationRect_BeforeAddRect(Station *st, TileIndex tile, int w, int h, StationRectMode mode)
3213 {
3214 return StationRect_BeforeAddTile(st, tile, mode) && StationRect_BeforeAddTile(st, TILE_ADDXY(tile, w - 1, h - 1), mode);
3215 }
3216
3217 static inline bool ScanRectForStationTiles(StationID st_id, int left, int top, int right, int bottom)
3218 {
3219 TileIndex top_left = TileXY(left, top);
3220 int width = right - left + 1;
3221 int height = bottom - top + 1;
3222 BEGIN_TILE_LOOP(tile, width, height, top_left)
3223 if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
3224 END_TILE_LOOP(tile, width, height, top_left);
3225 return false;
3226 }
3227
3228 static bool StationRect_AfterRemoveTile(Station *st, TileIndex tile)
3229 {
3230 Rect *r = &st->rect;
3231 int x = TileX(tile);
3232 int y = TileY(tile);
3233 bool reduce_x, reduce_y;
3234
3235 // look if removed tile was on the bounding rect edge
3236 // and try to reduce the rect by this edge
3237 // do it until we have empty rect or nothing to do
3238 for (;;) {
3239 // check if removed tile is on rect edge
3240 bool left_edge = (x == r->left);
3241 bool right_edge = (x == r->right);
3242 bool top_edge = (y == r->top);
3243 bool bottom_edge = (y == r->bottom);
3244 // can we reduce the rect in either direction?
3245 reduce_x = ((left_edge || right_edge) && !ScanRectForStationTiles(st->index, x, r->top, x, r->bottom));
3246 reduce_y = ((top_edge || bottom_edge) && !ScanRectForStationTiles(st->index, r->left, y, r->right, y));
3247 if (!(reduce_x || reduce_y)) break; // nothing to do (can't reduce)
3248 if (reduce_x) {
3249 // reduce horizontally
3250 if (left_edge) {
3251 // move left edge right
3252 r->left = x = x + 1;
3253 } else {
3254 // move right edge left
3255 r->right = x = x - 1;
3256 }
3257 }
3258 if (reduce_y) {
3259 // reduce vertically
3260 if (top_edge) {
3261 // move top edge down
3262 r->top = y = y + 1;
3263 } else {
3264 // move bottom edge up
3265 r->bottom = y = y - 1;
3266 }
3267 }
3268 if (r->left > r->right || r->top > r->bottom) {
3269 // can't continue, if the remaining rectangle is empty
3270 StationRect_Init(st);
3271 return true; // empty remaining rect
3272 }
3273 }
3274 return false; // non-empty remaining rect
3275 }
3276
3277 static bool StationRect_AfterRemoveRect(Station *st, TileIndex tile, int w, int h)
3278 {
3279 bool empty;
3280 assert(PtInRectXY(&st->rect, TileX(tile), TileY(tile)));
3281 assert(PtInRectXY(&st->rect, TileX(tile) + w - 1, TileY(tile) + h - 1));
3282 empty = StationRect_AfterRemoveTile(st, tile);
3283 if (w != 1 || h != 1) empty = empty || StationRect_AfterRemoveTile(st, TILE_ADDXY(tile, w - 1, h - 1));
3284 return empty;
3285 }