4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file station_cmd.cpp Handling of station tiles. */
17 #include "cmd_helper.h"
18 #include "viewport_func.h"
19 #include "command_func.h"
21 #include "news_func.h"
26 #include "newgrf_cargo.h"
27 #include "newgrf_debug.h"
28 #include "newgrf_station.h"
29 #include "newgrf_canal.h" /* For the buoy */
30 #include "pathfinder/yapf/yapf.h"
31 #include "road_internal.h" /* For drawing catenary/checking road removal */
32 #include "autoslope.h"
34 #include "strings_func.h"
35 #include "clear_func.h"
36 #include "date_func.h"
37 #include "vehicle_func.h"
39 #include "animated_tile_func.h"
40 #include "elrail_func.h"
41 #include "station_base.h"
42 #include "roadstop_base.h"
43 #include "newgrf_railtype.h"
44 #include "waypoint_base.h"
45 #include "waypoint_func.h"
48 #include "core/random_func.hpp"
49 #include "company_base.h"
50 #include "table/airporttile_ids.h"
51 #include "newgrf_airporttiles.h"
52 #include "order_backup.h"
53 #include "newgrf_house.h"
54 #include "company_gui.h"
55 #include "linkgraph/linkgraph.h"
56 #include "linkgraph/linkgraphschedule.h"
57 #include "linkgraph/refresh.h"
58 #include "widgets/station_widget.h"
59 #include "signalbuffer.h"
60 #include "map/zoneheight.h"
63 #include "table/strings.h"
66 * Static instance of FlowStat::SharesMap.
67 * Note: This instance is created on task start.
68 * Lazy creation on first usage results in a data race between the CDist threads.
70 /* static */ const FlowStat::SharesMap
FlowStat::empty_sharesmap
;
73 * Retrieve hangar information of a hangar at a given tile.
74 * @param tile %Tile containing the hangar.
75 * @return The requested hangar information, or NULL if the tile is not a hangar.
77 const AirportFTA::Hangar
*Airport::GetHangarDataByTile (TileIndex tile
) const
79 assert (this->Contains (tile
));
80 TileIndexDiff diff
= tile
- this->tile
;
81 const AirportFTA
*fta
= this->GetFTA();
82 for (uint i
= 0; i
< fta
->num_hangars
; i
++) {
83 if (this->GetRotatedHangarDiff (&fta
->hangars
[i
]) == diff
) {
84 return &fta
->hangars
[i
];
91 * Check whether the given tile is a hangar.
92 * @param t the tile to of whether it is a hangar.
93 * @pre IsStationTile(t)
94 * @return true if and only if the tile is a hangar.
96 bool IsHangar(TileIndex t
)
98 assert(IsStationTile(t
));
100 /* If the tile isn't an airport there's no chance it's a hangar. */
101 if (!IsAirport(t
)) return false;
103 const Station
*st
= Station::GetByTile(t
);
104 return st
->airport
.GetHangarDataByTile(t
) != NULL
;
108 * Check whether the tile is a mine.
109 * @param tile the tile to investigate.
110 * @return true if and only if the tile is a mine
112 static bool CMSAMine(TileIndex tile
)
115 if (!IsIndustryTile(tile
)) return false;
117 const Industry
*ind
= Industry::GetByTile(tile
);
119 /* No extractive industry */
120 if ((GetIndustrySpec(ind
->type
)->life_type
& INDUSTRYLIFE_EXTRACTIVE
) == 0) return false;
122 for (uint i
= 0; i
< lengthof(ind
->produced_cargo
); i
++) {
123 /* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine.
124 * Also the production of passengers and mail is ignored. */
125 if (ind
->produced_cargo
[i
] != CT_INVALID
&&
126 (CargoSpec::Get(ind
->produced_cargo
[i
])->classes
& (CC_LIQUID
| CC_PASSENGERS
| CC_MAIL
)) == 0) {
134 #define M(x) ((x) - STR_SV_STNAME)
139 STATIONNAMING_AIRPORT
,
140 STATIONNAMING_OILRIG
,
142 STATIONNAMING_HELIPORT
,
145 static StringID
GenerateStationName(Station
*st
, TileIndex tile
, StationNaming name_class
)
147 static const uint32 _gen_station_name_bits
[] = {
148 0, // STATIONNAMING_RAIL
149 0, // STATIONNAMING_ROAD
150 1U << M(STR_SV_STNAME_AIRPORT
), // STATIONNAMING_AIRPORT
151 1U << M(STR_SV_STNAME_OILFIELD
), // STATIONNAMING_OILRIG
152 1U << M(STR_SV_STNAME_DOCKS
), // STATIONNAMING_DOCK
153 1U << M(STR_SV_STNAME_HELIPORT
), // STATIONNAMING_HELIPORT
156 const Town
*t
= st
->town
;
157 uint32 free_names
= UINT32_MAX
;
159 bool indtypes
[NUM_INDUSTRYTYPES
];
160 memset(indtypes
, 0, sizeof(indtypes
));
163 FOR_ALL_STATIONS(s
) {
164 if (s
!= st
&& s
->town
== t
) {
165 if (s
->indtype
!= IT_INVALID
) {
166 indtypes
[s
->indtype
] = true;
167 StringID name
= GetIndustrySpec(s
->indtype
)->station_name
;
168 if (name
!= STR_UNDEFINED
) {
169 /* Filter for other industrytypes with the same name */
170 for (IndustryType it
= 0; it
< NUM_INDUSTRYTYPES
; it
++) {
171 const IndustrySpec
*indsp
= GetIndustrySpec(it
);
172 if (indsp
->enabled
&& indsp
->station_name
== name
) indtypes
[it
] = true;
177 uint str
= M(s
->string_id
);
179 if (str
== M(STR_SV_STNAME_FOREST
)) {
180 str
= M(STR_SV_STNAME_WOODS
);
182 ClrBit(free_names
, str
);
187 CircularTileIterator
iter (tile
, 7);
188 for (TileIndex indtile
= iter
; indtile
!= INVALID_TILE
; indtile
= ++iter
) {
189 if (!IsIndustryTile(indtile
)) continue;
191 /* If the station name is undefined it means that it doesn't name a station */
192 const IndustryType indtype
= GetIndustryType(indtile
);
193 const IndustrySpec
*indsp
= GetIndustrySpec(indtype
);
194 if (indsp
->station_name
== STR_UNDEFINED
) continue;
196 /* In all cases if an industry that provides a name is found
197 * two of the standard names will be disabled. */
198 free_names
&= ~(1 << M(STR_SV_STNAME_OILFIELD
) | 1 << M(STR_SV_STNAME_MINES
));
200 if (!indtypes
[indtype
]) {
201 /* An industry has been found nearby */
202 /* STR_NULL means it only disables oil rig/mines */
203 if (indsp
->station_name
!= STR_NULL
) {
204 st
->indtype
= indtype
;
205 return STR_SV_STNAME_FALLBACK
;
211 /* check default names */
212 uint32 tmp
= free_names
& _gen_station_name_bits
[name_class
];
213 if (tmp
!= 0) return STR_SV_STNAME
+ FindFirstBit(tmp
);
215 TileArea
around (tile
);
219 if (HasBit(free_names
, M(STR_SV_STNAME_MINES
))) {
221 TILE_AREA_LOOP(t
, around
) {
222 if (CMSAMine(t
) && ++num
>= 2) {
223 return STR_SV_STNAME_MINES
;
228 /* check close enough to town to get central as name? */
229 if (DistanceMax(tile
, t
->xy
) < 8) {
230 if (HasBit(free_names
, M(STR_SV_STNAME
))) return STR_SV_STNAME
;
232 if (HasBit(free_names
, M(STR_SV_STNAME_CENTRAL
))) return STR_SV_STNAME_CENTRAL
;
236 if (HasBit(free_names
, M(STR_SV_STNAME_LAKESIDE
)) &&
237 DistanceFromEdge(tile
) < 20) {
239 TILE_AREA_LOOP(t
, around
) {
240 if (IsPlainWaterTile(t
) && ++num
>= 5) {
241 return STR_SV_STNAME_LAKESIDE
;
247 if (HasBit(free_names
, M(STR_SV_STNAME_WOODS
))) {
250 TILE_AREA_LOOP(t
, around
) {
251 if ((IsTreeTile(t
) && ++trees
>= 8) || (IsTileForestIndustry(t
) && ++forest
>= 2)) {
252 return _settings_game
.game_creation
.landscape
== LT_TROPIC
? STR_SV_STNAME_FOREST
: STR_SV_STNAME_WOODS
;
257 /* check elevation compared to town */
258 int z
= GetTileZ(tile
);
259 int z2
= GetTileZ(t
->xy
);
261 if (HasBit(free_names
, M(STR_SV_STNAME_VALLEY
))) return STR_SV_STNAME_VALLEY
;
263 if (HasBit(free_names
, M(STR_SV_STNAME_HEIGHTS
))) return STR_SV_STNAME_HEIGHTS
;
266 /* check direction compared to town */
267 static const int8 _direction_and_table
[] = {
268 ~( (1 << M(STR_SV_STNAME_WEST
)) | (1 << M(STR_SV_STNAME_EAST
)) | (1 << M(STR_SV_STNAME_NORTH
)) ),
269 ~( (1 << M(STR_SV_STNAME_SOUTH
)) | (1 << M(STR_SV_STNAME_WEST
)) | (1 << M(STR_SV_STNAME_NORTH
)) ),
270 ~( (1 << M(STR_SV_STNAME_SOUTH
)) | (1 << M(STR_SV_STNAME_EAST
)) | (1 << M(STR_SV_STNAME_NORTH
)) ),
271 ~( (1 << M(STR_SV_STNAME_SOUTH
)) | (1 << M(STR_SV_STNAME_WEST
)) | (1 << M(STR_SV_STNAME_EAST
)) ),
274 free_names
&= _direction_and_table
[
275 (TileX(tile
) < TileX(t
->xy
)) +
276 (TileY(tile
) < TileY(t
->xy
)) * 2];
278 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));
279 return (tmp
== 0) ? STR_SV_STNAME_FALLBACK
: (STR_SV_STNAME
+ FindFirstBit(tmp
));
284 * Find the closest deleted station of the current company
285 * @param tile the tile to search from.
286 * @return the closest station or NULL if too far.
288 static Station
*GetClosestDeletedStation(TileIndex tile
)
291 Station
*best_station
= NULL
;
294 FOR_ALL_STATIONS(st
) {
295 if (!st
->IsInUse() && st
->owner
== _current_company
) {
296 uint cur_dist
= DistanceManhattan(tile
, st
->xy
);
298 if (cur_dist
< threshold
) {
299 threshold
= cur_dist
;
310 * Update the virtual coords needed to draw the station sign.
312 void Station::UpdateVirtCoord()
314 Point pt
= RemapCoords2(TileX(this->xy
) * TILE_SIZE
, TileY(this->xy
) * TILE_SIZE
);
316 pt
.y
-= 32 * ZOOM_LVL_BASE
;
317 if ((this->facilities
& FACIL_AIRPORT
) && this->airport
.type
== AT_OILRIG
) pt
.y
-= 16 * ZOOM_LVL_BASE
;
319 SetDParam(0, this->index
);
320 SetDParam(1, this->facilities
);
321 this->sign
.UpdatePosition(pt
.x
, pt
.y
, STR_VIEWPORT_STATION
);
323 SetWindowDirty(WC_STATION_VIEW
, this->index
);
326 /** Update the virtual coords needed to draw the station sign for all stations. */
327 void UpdateAllStationVirtCoords()
331 FOR_ALL_BASE_STATIONS(st
) {
332 st
->UpdateVirtCoord();
337 * Get a mask of the cargo types that the station accepts.
338 * @param st Station to query
339 * @return the expected mask
341 static uint
GetAcceptanceMask(const Station
*st
)
345 for (CargoID i
= 0; i
< NUM_CARGO
; i
++) {
346 if (HasBit(st
->goods
[i
].status
, GoodsEntry::GES_ACCEPTANCE
)) mask
|= 1 << i
;
352 * Get the cargo types being produced around a tile area.
353 * @param area Tile area
354 * @param rad Search radius in addition to the given area
356 CargoArray
GetAreaProduction (const TileArea
&area
, int rad
)
363 /* Loop over all tiles to get the produced cargo of
364 * everything except industries */
365 TILE_AREA_LOOP(tile
, ta
) AddProducedCargo(tile
, produced
);
367 /* Loop over the industries. They produce cargo for
368 * anything that is within 'rad' from their bounding
369 * box. As such if you have e.g. a oil well the tile
370 * area loop might not hit an industry tile while
371 * the industry would produce cargo for the station.
374 FOR_ALL_INDUSTRIES(i
) {
375 if (!ta
.Intersects(i
->location
)) continue;
377 for (uint j
= 0; j
< lengthof(i
->produced_cargo
); j
++) {
378 CargoID cargo
= i
->produced_cargo
[j
];
379 if (cargo
!= CT_INVALID
) produced
[cargo
]++;
387 * Get the acceptance of cargoes around a tile area in 1/8.
388 * @param area Tile area
389 * @param rad Search radius in addition to given area
390 * @param always_accepted bitmask of cargo accepted by houses and headquarters; can be NULL
392 CargoArray
GetAreaAcceptance (const TileArea
&area
, int rad
, uint32
*always_accepted
)
394 CargoArray acceptance
;
395 if (always_accepted
!= NULL
) *always_accepted
= 0;
400 TILE_AREA_LOOP(tile
, ta
) AddAcceptedCargo(tile
, acceptance
, always_accepted
);
406 * Update the acceptance for a station.
407 * @param st Station to update
408 * @param show_msg controls whether to display a message that acceptance was changed.
410 void UpdateStationAcceptance(Station
*st
, bool show_msg
)
412 /* old accepted goods types */
413 uint old_acc
= GetAcceptanceMask(st
);
415 /* And retrieve the acceptance. */
416 CargoArray acceptance
;
417 if (!st
->rect
.empty()) {
418 acceptance
= GetAreaAcceptance (st
->rect
,
419 st
->GetCatchmentRadius(), &st
->always_accepted
);
422 /* Adjust in case our station only accepts fewer kinds of goods */
423 for (CargoID i
= 0; i
< NUM_CARGO
; i
++) {
424 /* Make sure the station can accept the goods type. */
425 uint amt
= st
->CanHandleCargo(i
) ? acceptance
[i
] : 0;
427 GoodsEntry
&ge
= st
->goods
[i
];
428 SB(ge
.status
, GoodsEntry::GES_ACCEPTANCE
, 1, amt
>= 8);
429 if (LinkGraph::IsValidID(ge
.link_graph
)) {
430 (*LinkGraph::Get(ge
.link_graph
))[ge
.node
]->SetDemand(amt
/ 8);
434 /* Only show a message in case the acceptance was actually changed. */
435 uint new_acc
= GetAcceptanceMask(st
);
436 uint diff_acc
= old_acc
^ new_acc
;
437 if (diff_acc
== 0) return;
439 /* show a message to report that the acceptance was changed? */
440 if (show_msg
&& st
->owner
== _local_company
&& st
->IsInUse()) {
441 /* List of accept and reject strings for different number of
443 static const StringID accept_msg
[] = {
444 STR_NEWS_STATION_NOW_ACCEPTS_CARGO
,
445 STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO
,
447 static const StringID reject_msg
[] = {
448 STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO
,
449 STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_OR_CARGO
,
452 /* Array of accepted and rejected cargo types */
453 CargoID accepts
[2] = { CT_INVALID
, CT_INVALID
};
454 CargoID rejects
[2] = { CT_INVALID
, CT_INVALID
};
458 /* Test each cargo type to see if its acceptance has changed */
459 for (CargoID i
= 0; i
< NUM_CARGO
; i
++) {
460 if (!HasBit (diff_acc
, i
)) continue;
462 if (HasBit(new_acc
, i
)) {
463 if (num_acc
< lengthof(accepts
)) {
464 /* New cargo is accepted */
465 accepts
[num_acc
++] = i
;
468 if (num_rej
< lengthof(rejects
)) {
469 /* Old cargo is no longer accepted */
470 rejects
[num_rej
++] = i
;
475 /* Show news message if there are any changes */
476 if (num_acc
> 0) AddNewsItem
<AcceptanceNewsItem
> (st
, num_acc
, accepts
, accept_msg
[num_acc
- 1]);
477 if (num_rej
> 0) AddNewsItem
<AcceptanceNewsItem
> (st
, num_rej
, rejects
, reject_msg
[num_rej
- 1]);
480 /* redraw the station view since acceptance changed */
481 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_ACCEPT_RATING_LIST
);
484 /** Update the station sign tile and virtual position. */
485 static void UpdateStationSign (BaseStation
*st
)
487 if (st
->rect
.empty()) { // no tiles belong to this station
488 st
->UpdateVirtCoord();
492 /* clamp sign coord to be inside the station rect */
493 st
->xy
= st
->rect
.get_closest_tile(st
->xy
);
494 st
->UpdateVirtCoord();
496 if (st
->IsWaypoint()) return;
497 Station
*full_station
= Station::From(st
);
498 for (CargoID c
= 0; c
< NUM_CARGO
; ++c
) {
499 LinkGraphID lg
= full_station
->goods
[c
].link_graph
;
500 if (!LinkGraph::IsValidID(lg
)) continue;
505 * This is called right after a station was deleted.
506 * It checks if the whole station is free of substations, and if so, the station will be
507 * deleted after a little while.
510 static void DeleteStationIfEmpty(BaseStation
*st
)
512 if (!st
->IsInUse()) {
514 InvalidateWindowData(WC_STATION_LIST
, st
->owner
, 0);
518 CommandCost
ClearTile_Station(TileIndex tile
, DoCommandFlag flags
);
521 * Checks if the given tile is buildable, flat and has a certain height.
522 * @param tile TileIndex to check.
523 * @param invalid_dirs Prohibited directions for slopes (set of #DiagDirection).
524 * @param allowed_z Height allowed for the tile. If allowed_z is negative, it will be set to the height of this tile.
525 * @param allow_steep Whether steep slopes are allowed.
526 * @param check_bridge Minimum allowed height for a bridge, 0 for none.
527 * @return The cost in case of success, or an error code if it failed.
529 CommandCost
CheckBuildableTile (TileIndex tile
, uint invalid_dirs
,
530 int &allowed_z
, bool allow_steep
, int check_bridge
= 0)
533 Slope tileh
= GetTileSlope (tile
, &z
);
534 z
+= GetSlopeMaxZ (tileh
);
536 if (HasBridgeAbove (tile
) && ((check_bridge
== 0)
537 || (GetBridgeHeight (GetSouthernBridgeEnd (tile
)) < z
+ check_bridge
))) {
538 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
541 StringID str
= CheckVehicleOnGround (tile
);
542 if (str
!= STR_NULL
) return_cmd_error(str
);
544 /* Prohibit building if
545 * 1) The tile is "steep" (i.e. stretches two height levels).
546 * 2) The tile is non-flat and the build_on_slopes switch is disabled.
548 if ((!allow_steep
&& IsSteepSlope(tileh
)) ||
549 ((!_settings_game
.construction
.build_on_slopes
) && tileh
!= SLOPE_FLAT
)) {
550 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED
);
553 CommandCost
cost(EXPENSES_CONSTRUCTION
);
554 if (tileh
!= SLOPE_FLAT
) {
555 /* Forbid building if the tile faces a slope in a invalid direction. */
556 for (DiagDirection dir
= DIAGDIR_BEGIN
; dir
!= DIAGDIR_END
; dir
++) {
557 if (HasBit(invalid_dirs
, dir
) && !CanBuildDepotByTileh(dir
, tileh
)) {
558 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED
);
561 cost
.AddCost(_price
[PR_BUILD_FOUNDATION
]);
564 /* The level of this tile must be equal to allowed_z. */
568 } else if (allowed_z
!= z
) {
569 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED
);
576 * Checks if a rail station can be built at the given area.
577 * @param tile_area Area to check.
578 * @param flags Operation to perform.
579 * @param axis Rail station axis.
580 * @param station StationID to be queried and returned if available.
581 * @param rt The rail type to check for (overbuilding rail stations over rail).
582 * @param affected_vehicles List of trains with PBS reservations on the tiles
583 * @param statspec Station spec.
584 * @param plat_len Platform length.
585 * @param numtracks Number of platforms.
586 * @param layout Station layout.
587 * @return The cost in case of success, or an error code if it failed.
589 static CommandCost
CheckFlatLandRailStation (TileArea tile_area
,
590 DoCommandFlag flags
, Axis axis
, StationID
*station
, RailType rt
,
591 SmallVector
<Train
*, 4> &affected_vehicles
,
592 const StationSpec
*statspec
, byte plat_len
, byte numtracks
,
595 CommandCost
cost(EXPENSES_CONSTRUCTION
);
597 uint invalid_dirs
= 5 << axis
;
599 bool slope_cb
= statspec
!= NULL
&& HasBit(statspec
->callback_mask
, CBM_STATION_SLOPE_CHECK
);
601 TILE_AREA_LOOP(tile_cur
, tile_area
) {
603 if (statspec
!= NULL
) {
604 /* Disallow bridges over custom station tiles for now. */
607 uint dx
= TileX (tile_cur
) - TileX (tile_area
.tile
);
608 uint dy
= TileY (tile_cur
) - TileY (tile_area
.tile
);
609 uint platform
, offset
;
610 if (axis
== AXIS_X
) {
617 uint gfx
= layout
[platform
* plat_len
+ offset
];
618 check_bridge
= (gfx
< 2 ? 1 : gfx
< 4 ? 2 : 4);
620 CommandCost ret
= CheckBuildableTile (tile_cur
, invalid_dirs
, allowed_z
, false, check_bridge
);
621 if (ret
.Failed()) return ret
;
625 /* Do slope check if requested. */
626 ret
= PerformStationTileSlopeCheck (tile_area
.tile
, tile_cur
, statspec
, rt
, axis
, plat_len
, numtracks
);
627 if (ret
.Failed()) return ret
;
630 /* if station is set, then we have special handling to allow building on top of already existing stations.
631 * so station points to INVALID_STATION if we can build on any station.
632 * Or it points to a station if we're only allowed to build on exactly that station. */
633 if (station
!= NULL
&& IsStationTile(tile_cur
)) {
634 if (!IsRailStation(tile_cur
)) {
635 return ClearTile_Station(tile_cur
, DC_AUTO
); // get error message
637 StationID st
= GetStationIndex(tile_cur
);
638 if (*station
== INVALID_STATION
) {
640 } else if (*station
!= st
) {
641 return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING
);
645 /* Rail type is only valid when building a railway station; if station to
646 * build isn't a rail station it's INVALID_RAILTYPE. */
647 if (rt
!= INVALID_RAILTYPE
&& IsNormalRailTile(tile_cur
) &&
648 HasPowerOnRail(GetRailType(tile_cur
), rt
)) {
649 /* Allow overbuilding if the tile:
650 * - has rail, but no signals
651 * - it has exactly one track
652 * - the track is in line with the station
653 * - the current rail type has power on the to-be-built type (e.g. convert normal rail to el rail)
655 Track track
= AxisToTrack(axis
);
657 if (GetTrackBits(tile_cur
) == TrackToTrackBits(track
) && !HasSignalOnTrack(tile_cur
, track
)) {
658 /* Check for trains having a reservation for this tile. */
659 if (GetRailReservationTrackBits (tile_cur
) != TRACK_BIT_NONE
) {
660 Train
*v
= GetTrainForReservation(tile_cur
, track
);
662 *affected_vehicles
.Append() = v
;
665 CommandCost ret
= DoCommand(tile_cur
, 0, track
, flags
, CMD_REMOVE_SINGLE_RAIL
);
666 if (ret
.Failed()) return ret
;
668 /* With flags & ~DC_EXEC CmdLandscapeClear would fail since the rail still exists */
672 ret
= DoCommand(tile_cur
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
673 if (ret
.Failed()) return ret
;
682 * Checks if a road stop can be built at the given tile.
683 * @param tile_area Area to check.
684 * @param flags Operation to perform.
685 * @param invalid_dirs Prohibited directions (set of DiagDirections).
686 * @param is_drive_through True if trying to build a drive-through station.
687 * @param is_truck_stop True when building a truck stop, false otherwise.
688 * @param axis Axis of a drive-through road stop.
689 * @param station StationID to be queried and returned if available.
690 * @param rts Road types to build.
691 * @return The cost in case of success, or an error code if it failed.
693 static CommandCost
CheckFlatLandRoadStop(TileArea tile_area
, DoCommandFlag flags
, uint invalid_dirs
, bool is_drive_through
, bool is_truck_stop
, Axis axis
, StationID
*station
, RoadTypes rts
)
695 CommandCost
cost(EXPENSES_CONSTRUCTION
);
698 TILE_AREA_LOOP(cur_tile
, tile_area
) {
699 CommandCost ret
= CheckBuildableTile (cur_tile
, invalid_dirs
, allowed_z
, !is_drive_through
, 2);
700 if (ret
.Failed()) return ret
;
703 /* If station is set, then we have special handling to allow building on top of already existing stations.
704 * Station points to INVALID_STATION if we can build on any station.
705 * Or it points to a station if we're only allowed to build on exactly that station. */
706 if (station
!= NULL
&& IsStationTile(cur_tile
)) {
707 if (!IsRoadStop(cur_tile
)) {
708 return ClearTile_Station(cur_tile
, DC_AUTO
); // Get error message.
710 if (is_truck_stop
!= IsTruckStop(cur_tile
) ||
711 is_drive_through
!= IsDriveThroughStopTile(cur_tile
)) {
712 return ClearTile_Station(cur_tile
, DC_AUTO
); // Get error message.
714 /* Drive-through station in the wrong direction. */
715 if (is_drive_through
&& IsDriveThroughStopTile(cur_tile
) && GetRoadStopAxis(cur_tile
) != axis
){
716 return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION
);
718 StationID st
= GetStationIndex(cur_tile
);
719 if (*station
== INVALID_STATION
) {
721 } else if (*station
!= st
) {
722 return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING
);
726 bool build_over_road
= is_drive_through
&& IsNormalRoadTile(cur_tile
);
727 /* Road bits in the wrong direction. */
728 RoadBits rb
= IsRoadTile(cur_tile
) ? GetAllRoadBits(cur_tile
) : ROAD_NONE
;
729 if (build_over_road
&& (rb
& (axis
== AXIS_X
? ROAD_Y
: ROAD_X
)) != 0) {
730 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
731 switch (CountBits(rb
)) {
733 return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION
);
736 if (rb
== ROAD_X
|| rb
== ROAD_Y
) return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION
);
737 return_cmd_error(STR_ERROR_DRIVE_THROUGH_CORNER
);
740 return_cmd_error(STR_ERROR_DRIVE_THROUGH_JUNCTION
);
744 RoadTypes cur_rts
= IsRoadTile(cur_tile
) ? GetRoadTypes(cur_tile
) : ROADTYPES_NONE
;
745 uint num_roadbits
= 0;
746 if (build_over_road
) {
747 /* There is a road, check if we can build road+tram stop over it. */
748 if (HasBit(cur_rts
, ROADTYPE_ROAD
)) {
749 Owner road_owner
= GetRoadOwner(cur_tile
, ROADTYPE_ROAD
);
750 if (road_owner
== OWNER_TOWN
) {
751 if (!_settings_game
.construction
.road_stop_on_town_road
) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD
);
752 } else if (!_settings_game
.construction
.road_stop_on_competitor_road
&& road_owner
!= OWNER_NONE
) {
753 CommandCost ret
= CheckOwnership(road_owner
);
754 if (ret
.Failed()) return ret
;
756 num_roadbits
+= CountBits(GetRoadBits(cur_tile
, ROADTYPE_ROAD
));
759 /* There is a tram, check if we can build road+tram stop over it. */
760 if (HasBit(cur_rts
, ROADTYPE_TRAM
)) {
761 Owner tram_owner
= GetRoadOwner(cur_tile
, ROADTYPE_TRAM
);
762 if (Company::IsValidID(tram_owner
) &&
763 (!_settings_game
.construction
.road_stop_on_competitor_road
||
764 /* Disallow breaking end-of-line of someone else
765 * so trams can still reverse on this tile. */
766 HasExactlyOneBit(GetRoadBits(cur_tile
, ROADTYPE_TRAM
)))) {
767 CommandCost ret
= CheckOwnership(tram_owner
);
768 if (ret
.Failed()) return ret
;
770 num_roadbits
+= CountBits(GetRoadBits(cur_tile
, ROADTYPE_TRAM
));
773 /* Take into account existing roadbits. */
776 ret
= DoCommand(cur_tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
777 if (ret
.Failed()) return ret
;
781 uint roadbits_to_build
= CountBits(rts
) * 2 - num_roadbits
;
782 cost
.AddCost(_price
[PR_BUILD_ROAD
] * roadbits_to_build
);
790 * Checks if an airport can be built at the given area.
791 * @param airport_tile Airport reference tile.
792 * @param att Airport tile table.
793 * @param flags Operation to perform.
794 * @param station StationID of airport allowed in search area.
795 * @return The cost in case of success, or an error code if it failed.
797 static CommandCost
CheckFlatLandAirport (TileIndex airport_tile
,
798 const AirportTileTable
*att
, DoCommandFlag flags
, StationID
*station
)
800 CommandCost
cost(EXPENSES_CONSTRUCTION
);
803 for (AirportTileTableIterator
iter (att
, airport_tile
); iter
!= INVALID_TILE
; ++iter
) {
804 TileIndex tile_cur
= iter
;
805 CommandCost ret
= CheckBuildableTile(tile_cur
, 0, allowed_z
, true);
806 if (ret
.Failed()) return ret
;
809 /* if station is set, then allow building on top of an already
810 * existing airport, either the one in *station if it is not
811 * INVALID_STATION, or anyone otherwise and store which one
813 if (station
!= NULL
&& IsStationTile(tile_cur
)) {
814 if (!IsAirport(tile_cur
)) {
815 return ClearTile_Station(tile_cur
, DC_AUTO
); // get error message
817 StationID st
= GetStationIndex(tile_cur
);
818 if (*station
== INVALID_STATION
) {
820 } else if (*station
!= st
) {
821 return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING
);
825 ret
= DoCommand(tile_cur
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
826 if (ret
.Failed()) return ret
;
835 * Check whether we can expand the rail part of the given station.
836 * @param st the station to expand
837 * @param new_ta the current (and if all is fine new) tile area of the rail part of the station
838 * @param axis the axis of the newly build rail
839 * @return Succeeded or failed command.
841 CommandCost
CanExpandRailStation(const BaseStation
*st
, TileArea
&new_ta
, Axis axis
)
843 TileArea cur_ta
= st
->train_station
;
845 /* determine new size of train station region.. */
846 int x
= min(TileX(cur_ta
.tile
), TileX(new_ta
.tile
));
847 int y
= min(TileY(cur_ta
.tile
), TileY(new_ta
.tile
));
848 new_ta
.w
= max(TileX(cur_ta
.tile
) + cur_ta
.w
, TileX(new_ta
.tile
) + new_ta
.w
) - x
;
849 new_ta
.h
= max(TileY(cur_ta
.tile
) + cur_ta
.h
, TileY(new_ta
.tile
) + new_ta
.h
) - y
;
850 new_ta
.tile
= TileXY(x
, y
);
852 /* make sure the final size is not too big. */
853 if (new_ta
.w
> _settings_game
.station
.station_spread
|| new_ta
.h
> _settings_game
.station
.station_spread
) {
854 return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT
);
857 return CommandCost();
860 static inline byte
*CreateSingle(byte
*layout
, int n
)
863 do *layout
++ = 0; while (--i
);
864 layout
[((n
- 1) >> 1) - n
] = 2;
868 static inline byte
*CreateMulti(byte
*layout
, int n
, byte b
)
871 do *layout
++ = b
; while (--i
);
874 layout
[n
- 1 - n
] = 0;
880 * Create the station layout for the given number of tracks and platform length.
881 * @param layout The layout to write to.
882 * @param numtracks The number of tracks to write.
883 * @param plat_len The length of the platforms.
884 * @param statspec The specification of the station to (possibly) get the layout from.
886 void GetStationLayout(byte
*layout
, int numtracks
, int plat_len
, const StationSpec
*statspec
)
888 if (statspec
!= NULL
&& numtracks
<= statspec
->max_layout_width
889 && plat_len
<= statspec
->max_layout_length
[numtracks
]) {
890 const byte
*p
= statspec
->layouts
.get()[numtracks
- 1][plat_len
- 1];
892 /* Custom layout defined, follow it. */
893 memcpy (layout
, p
, plat_len
* numtracks
);
899 CreateSingle(layout
, numtracks
);
901 if (numtracks
& 1) layout
= CreateSingle(layout
, plat_len
);
904 while (--numtracks
>= 0) {
905 layout
= CreateMulti(layout
, plat_len
, 4);
906 layout
= CreateMulti(layout
, plat_len
, 6);
912 * Find a nearby station that joins this station.
913 * @param pst 'return' pointer for the found station
914 * @param ta the area of the newly built station
915 * @param existing_station an existing station we build over
916 * @param station_to_join the station to join, if adjacent is set
917 * @param adjacent whether adjacent stations are allowed
918 * @param waypoint find waypoints, else stations
919 * @param error_message the error message when building a station on top of others
920 * @return command cost with the error or 'okay'
922 static CommandCost
FindJoiningBaseStation (BaseStation
**pst
, TileArea ta
,
923 StationID existing_station
, StationID station_to_join
, bool adjacent
,
924 bool waypoint
, StringID error_message
)
926 BaseStation
*st
; // station to join
927 bool need_link
; // need an adjacent piece of joined station
928 bool avoid_other
; // avoid (other) adjacent stations
930 if (existing_station
!= INVALID_STATION
) {
931 /* we are partially overbuilding a station */
932 if (adjacent
&& station_to_join
!= existing_station
) {
933 /* you cannot join a different station */
934 return_cmd_error(error_message
);
937 assert (BaseStation::IsValidID (existing_station
));
938 st
= BaseStation::Get (existing_station
);
939 assert (st
->IsWaypoint() == waypoint
);
941 avoid_other
= !_settings_game
.station
.adjacent_stations
;
942 } else if (!adjacent
) {
943 /* join adjacent station if unique, else error out */
947 } else if (station_to_join
!= INVALID_STATION
) {
948 /* not overbuilding, and we want to join a given station */
949 st
= BaseStation::GetIfValid (station_to_join
);
950 if (st
== NULL
) return CMD_ERROR
;
951 if (st
->IsWaypoint() != waypoint
) return CMD_ERROR
;
952 need_link
= st
->IsInUse() && !_settings_game
.station
.distant_join_stations
;
953 avoid_other
= !_settings_game
.station
.adjacent_stations
;
955 /* not overbuilding, and we want to build a new station */
958 avoid_other
= !_settings_game
.station
.adjacent_stations
;
961 if (need_link
|| avoid_other
) {
963 TILE_AREA_LOOP(tile_cur
, ta
) {
964 if (IsStationTile(tile_cur
)) {
965 StationID t
= GetStationIndex(tile_cur
);
966 if (!BaseStation::IsValidID(t
)) continue;
967 BaseStation
*neighbour
= BaseStation::Get(t
);
968 if (neighbour
->IsWaypoint() != waypoint
) continue;
970 /* found an adjacent piece of a station */
972 /* wanted to join a given station */
973 if (t
== st
->index
) {
974 /* found an adjacent piece */
976 if (!avoid_other
) break;
977 } else if (avoid_other
) {
978 /* found a different station */
979 return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING
);
981 } else if (need_link
) {
982 /* wanted to join any station */
985 if (!avoid_other
) break;
986 } else if (avoid_other
) {
987 /* wanted to build a new station */
988 return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING
);
994 /* tried to join a non-adjacent station but distant join is disabled? */
995 if (st
!= NULL
&& need_link
) return CMD_ERROR
;
999 return CommandCost();
1003 * Find a nearby station that joins this station.
1004 * @tparam T the class to find a station for
1005 * @param pst 'return' pointer for the found station
1006 * @param ta the area of the newly built station
1007 * @param existing_station an existing station we build over
1008 * @param station_to_join the station to join, if adjacent is set
1009 * @param adjacent whether adjacent stations are allowed
1010 * @param error_message the error message when building a station on top of others
1011 * @return command cost with the error or 'okay'
1014 static inline CommandCost
FindJoiningBaseStation (T
**pst
, TileArea ta
,
1015 StationID existing_station
, StationID station_to_join
, bool adjacent
,
1016 StringID error_message
)
1019 CommandCost ret
= FindJoiningBaseStation (&bst
, ta
,
1020 existing_station
, station_to_join
, adjacent
,
1021 T::IS_WAYPOINT
, error_message
);
1022 if (ret
.Succeeded()) *pst
= bst
!= NULL
? T::From (bst
) : NULL
;
1027 * Find a nearby waypoint that joins this waypoint.
1028 * @param existing_waypoint an existing waypoint we build over
1029 * @param waypoint_to_join the waypoint to join to
1030 * @param adjacent whether adjacent waypoints are allowed
1031 * @param ta the area of the newly build waypoint
1032 * @param wp 'return' pointer for the found waypoint
1033 * @return command cost with the error or 'okay'
1035 CommandCost
FindJoiningWaypoint(StationID existing_waypoint
, StationID waypoint_to_join
, bool adjacent
, TileArea ta
, Waypoint
**wp
)
1037 return FindJoiningBaseStation
<Waypoint
> (wp
, ta
, existing_waypoint
,
1038 waypoint_to_join
, adjacent
,
1039 STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST
);
1043 * Common part of building various station parts and possibly attaching them to an existing one.
1044 * @param [out] st Station to attach to
1045 * @param area Area occupied by the new part
1046 * @param existing_station Existing station we build over
1047 * @param station_to_join Station to join, if adjacent is set
1048 * @param adjacent Whether adjacent stations are allowed
1049 * @param error_message Error message when building a station on top of others
1050 * @param flags Command flags
1051 * @param name_class Station naming class to use to generate the new station's name
1052 * @return Command error that occurred, if any
1054 static CommandCost
BuildStationPart (Station
**st
, const TileArea
&area
,
1055 StationID existing_station
, StationID station_to_join
, bool adjacent
,
1056 StringID error_message
, DoCommandFlag flags
, StationNaming name_class
)
1058 CommandCost ret
= FindJoiningBaseStation
<Station
> (st
, area
,
1059 existing_station
, station_to_join
, adjacent
, error_message
);
1060 if (ret
.Failed()) return ret
;
1062 /* Find a deleted station close to us */
1063 if (*st
== NULL
&& !adjacent
) *st
= GetClosestDeletedStation(area
.tile
);
1066 if ((*st
)->owner
!= _current_company
) {
1067 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION
);
1070 if (!(*st
)->TestAddRect(area
)) {
1071 return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT
);
1074 /* allocate and initialize new station */
1075 if (!Station::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING
);
1077 if (flags
& DC_EXEC
) {
1078 *st
= new Station(area
.tile
);
1080 (*st
)->town
= ClosestTownFromTile(area
.tile
);
1081 (*st
)->string_id
= GenerateStationName(*st
, area
.tile
, name_class
);
1083 if (Company::IsValidID(_current_company
)) {
1084 SetBit((*st
)->town
->have_ratings
, _current_company
);
1089 return CommandCost();
1093 static void FreeTrainReservation(Train
*v
)
1095 FreeTrainTrackReservation(v
);
1097 const RailPathPos pos
= v
->GetPos();
1098 if (!pos
.in_wormhole() && IsRailStationTile(pos
.tile
)) SetRailStationPlatformReservation(pos
, false);
1100 const RailPathPos rev
= v
->Last()->GetReversePos();
1101 if (!rev
.in_wormhole() && IsRailStationTile(rev
.tile
)) SetRailStationPlatformReservation(rev
, false);
1104 static void RestoreTrainReservation(Train
*v
)
1106 const RailPathPos pos
= v
->GetPos();
1107 if (!pos
.in_wormhole() && IsRailStationTile(pos
.tile
)) SetRailStationPlatformReservation(pos
, true);
1109 /* Check first if the train can have a reservation (not heading into a depot). */
1110 if (FreeTrainTrackReservation(v
)) TryPathReserve(v
, true, true);
1112 const RailPathPos rev
= v
->Last()->GetReversePos();
1113 if (!rev
.in_wormhole() && IsRailStationTile(rev
.tile
)) SetRailStationPlatformReservation(rev
, true);
1117 * Build rail station
1118 * @param tile_org northern most position of station dragging/placement
1119 * @param flags operation to perform
1120 * @param p1 various bitstuffed elements
1121 * - p1 = (bit 0- 3) - railtype
1122 * - p1 = (bit 4) - orientation (Axis)
1123 * - p1 = (bit 8-15) - number of tracks
1124 * - p1 = (bit 16-23) - platform length
1125 * - p1 = (bit 24) - allow stations directly adjacent to other stations.
1126 * @param p2 various bitstuffed elements
1127 * - p2 = (bit 0- 7) - custom station class
1128 * - p2 = (bit 8-15) - custom station id
1129 * - p2 = (bit 16-31) - station ID to join (INVALID_STATION if build new one)
1130 * @param text unused
1131 * @return the cost of this operation or an error
1133 CommandCost
CmdBuildRailStation(TileIndex tile_org
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1135 /* Unpack parameters */
1136 RailType rt
= Extract
<RailType
, 0, 4>(p1
);
1137 Axis axis
= Extract
<Axis
, 4, 1>(p1
);
1138 byte numtracks
= GB(p1
, 8, 8);
1139 byte plat_len
= GB(p1
, 16, 8);
1140 bool adjacent
= HasBit(p1
, 24);
1142 StationClassID spec_class
= Extract
<StationClassID
, 0, 8>(p2
);
1143 byte spec_index
= GB(p2
, 8, 8);
1144 StationID station_to_join
= GB(p2
, 16, 16);
1146 /* Does the authority allow this? */
1147 CommandCost ret
= CheckIfAuthorityAllowsNewStation(tile_org
, flags
);
1148 if (ret
.Failed()) return ret
;
1150 if (!ValParamRailtype(rt
)) return CMD_ERROR
;
1152 /* Check if the given station class is valid */
1153 if ((uint
)spec_class
>= StationClass::GetClassCount() || spec_class
== STAT_CLASS_WAYP
) return CMD_ERROR
;
1154 const StationClass
*statclass
= StationClass::Get(spec_class
);
1155 if (spec_index
>= statclass
->GetSpecCount()) return CMD_ERROR
;
1156 const StationSpec
*statspec
= statclass
->GetSpec(spec_index
);
1158 if (plat_len
== 0 || numtracks
== 0) return CMD_ERROR
;
1161 if (axis
== AXIS_X
) {
1169 if (h_org
> _settings_game
.station
.station_spread
|| w_org
> _settings_game
.station
.station_spread
) return CMD_ERROR
;
1171 byte
*layout_ptr
= AllocaM(byte
, numtracks
* plat_len
);
1172 GetStationLayout (layout_ptr
, numtracks
, plat_len
, statspec
);
1174 /* these values are those that will be stored in train_tile and station_platforms */
1175 TileArea
new_location(tile_org
, w_org
, h_org
);
1177 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1178 StationID est
= INVALID_STATION
;
1179 SmallVector
<Train
*, 4> affected_vehicles
;
1180 /* Clear the land below the station. */
1181 CommandCost cost
= CheckFlatLandRailStation (new_location
, flags
, axis
, &est
, rt
, affected_vehicles
, statspec
, plat_len
, numtracks
, layout_ptr
);
1182 if (cost
.Failed()) return cost
;
1183 /* Add construction expenses. */
1184 cost
.AddCost((numtracks
* _price
[PR_BUILD_STATION_RAIL
] + _price
[PR_BUILD_STATION_RAIL_LENGTH
]) * plat_len
);
1185 cost
.AddCost(numtracks
* plat_len
* RailBuildCost(rt
));
1188 ret
= BuildStationPart (&st
, new_location
, est
, station_to_join
,
1189 adjacent
, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST
,
1190 flags
, STATIONNAMING_RAIL
);
1191 if (ret
.Failed()) return ret
;
1193 if (st
!= NULL
&& st
->train_station
.tile
!= INVALID_TILE
) {
1194 CommandCost ret
= CanExpandRailStation(st
, new_location
, axis
);
1195 if (ret
.Failed()) return ret
;
1198 /* Check if we can allocate a custom stationspec to this station */
1199 int specindex
= AllocateSpecToStation(statspec
, st
, (flags
& DC_EXEC
) != 0);
1200 if (specindex
== -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS
);
1202 if (statspec
!= NULL
) {
1203 /* Perform NewStation checks */
1205 /* Check if the station size is permitted */
1206 if (HasBit(statspec
->disallowed_platforms
, min(numtracks
- 1, 7)) || HasBit(statspec
->disallowed_lengths
, min(plat_len
- 1, 7))) {
1210 /* Check if the station is buildable */
1211 if (HasBit(statspec
->callback_mask
, CBM_STATION_AVAIL
)) {
1212 uint16 cb_res
= GetStationCallback (CBID_STATION_AVAILABILITY
, 0, 0, statspec
, rt
);
1213 if (cb_res
!= CALLBACK_FAILED
&& !Convert8bitBooleanCallback(statspec
->grf_prop
.grffile
, CBID_STATION_AVAILABILITY
, cb_res
)) return CMD_ERROR
;
1217 if (flags
& DC_EXEC
) {
1218 st
->train_station
= new_location
;
1219 st
->AddFacility(FACIL_TRAIN
, new_location
.tile
);
1221 st
->rect
.Add (TileArea (tile_org
, w_org
, h_org
));
1223 if (statspec
!= NULL
) {
1224 /* Include this station spec's animation trigger bitmask
1225 * in the station's cached copy. */
1226 st
->cached_anim_triggers
|= statspec
->animation
.triggers
;
1229 Company
*c
= Company::Get(st
->owner
);
1231 TileIndexDiff delta_along
= (axis
== AXIS_X
? TileDiffXY (1, 0) : TileDiffXY (0, 1));
1232 TileIndexDiff delta_across
= delta_along
^ TileDiffXY (1, 1); // perpendicular to delta_along
1234 TileIndex tile_track
= tile_org
;
1235 for (uint i
= 0; i
< numtracks
; i
++, tile_track
+= delta_across
) {
1236 TileIndex tile
= tile_track
;
1237 for (uint j
= 0; j
< plat_len
; j
++, tile
+= delta_along
) {
1238 byte layout
= *layout_ptr
++;
1239 if (IsRailStationTile(tile
) && HasStationReservation(tile
)) {
1240 /* Check for trains having a reservation for this tile. */
1241 Train
*v
= GetTrainForReservation(tile
, AxisToTrack(GetRailStationAxis(tile
)));
1243 *affected_vehicles
.Append() = v
;
1244 FreeTrainReservation(v
);
1248 /* Railtype can change when overbuilding. */
1249 if (IsRailStationTile(tile
)) {
1250 if (!IsStationTileBlocked(tile
)) c
->infrastructure
.rail
[GetRailType(tile
)]--;
1251 c
->infrastructure
.station
--;
1254 /* Remove animation if overbuilding */
1255 DeleteAnimatedTile(tile
);
1256 byte old_specindex
= HasStationTileRail(tile
) ? GetCustomStationSpecIndex(tile
) : 0;
1257 MakeRailStation(tile
, st
->owner
, st
->index
, axis
, layout
& ~1, rt
);
1258 /* Free the spec if we overbuild something */
1259 DeallocateSpecFromStation(st
, old_specindex
);
1261 SetCustomStationSpecIndex(tile
, specindex
);
1262 SetStationTileRandomBits(tile
, GB(Random(), 0, 4));
1263 SetAnimationFrame(tile
, 0);
1265 if (!IsStationTileBlocked(tile
)) c
->infrastructure
.rail
[rt
]++;
1266 c
->infrastructure
.station
++;
1268 if (statspec
!= NULL
) {
1269 uint32 platinfo
= GetPlatformInfo (GetStationGfx(tile
), numtracks
, plat_len
, i
, j
, false);
1271 /* As the station is not yet completely finished, the station does not yet exist. */
1272 uint16 callback
= GetStationCallback (CBID_STATION_TILE_LAYOUT
, platinfo
, 0, statspec
, rt
, tile
);
1273 if (callback
!= CALLBACK_FAILED
) {
1275 SetStationGfx(tile
, (callback
& ~1) + axis
);
1277 ErrorUnknownCallbackResult(statspec
->grf_prop
.grffile
->grfid
, CBID_STATION_TILE_LAYOUT
, callback
);
1281 /* Trigger station animation -- after building? */
1282 TriggerStationAnimation(st
, tile
, SAT_BUILT
);
1286 AddTrackToSignalBuffer (tile_track
, AxisToTrack(axis
), _current_company
);
1287 YapfNotifyTrackLayoutChange();
1290 for (uint i
= 0; i
< affected_vehicles
.Length(); ++i
) {
1291 RestoreTrainReservation(affected_vehicles
[i
]);
1294 /* Check whether we need to expand the reservation of trains already on the station. */
1295 TileIndex tile
= tile_org
;
1296 for (uint i
= 0; i
< numtracks
; i
++, tile
+= delta_across
) {
1297 /* Don't even try to make eye candy parts reserved. */
1298 if (IsStationTileBlocked(tile
)) continue;
1300 bool reservation
= false;
1302 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1303 TileIndex platform_begin
= tile
;
1305 reservation
|= HasStationReservation (platform_begin
);
1306 TileIndex prev
= platform_begin
- delta_along
;
1307 if (!IsCompatibleTrainStationTile (prev
, platform_begin
)) break;
1308 platform_begin
= prev
;
1311 TileIndex platform_end
= tile
;
1312 while (!reservation
) {
1313 TileIndex next
= platform_end
+ delta_along
;
1314 if (!IsCompatibleTrainStationTile (next
, platform_end
)) break;
1315 platform_end
= next
;
1316 reservation
= HasStationReservation (next
);
1319 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1321 SetRailStationPlatformReservation (platform_begin
, AxisToDiagDir(axis
), true);
1325 st
->MarkTilesDirty(false);
1326 st
->UpdateVirtCoord();
1327 UpdateStationAcceptance(st
, false);
1328 st
->RecomputeIndustriesNear();
1329 InvalidateWindowData(WC_SELECT_STATION
, 0, 0);
1330 InvalidateWindowData(WC_STATION_LIST
, st
->owner
, 0);
1331 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_TRAINS
);
1332 DirtyCompanyInfrastructureWindows(st
->owner
);
1339 * Remove a number of tiles from any rail station or waypoint within the area.
1340 * @param start tile of station piece to remove
1341 * @param flags operation to perform
1342 * @param p1 start_tile
1343 * @param p2 various bitstuffed elements
1344 * - p2 = bit 0 - if set keep the rail
1345 * @param waypoint remove from waypoints, else from stations
1346 * @return the cost of this operation or an error
1348 static CommandCost
RemoveFromRailBaseStation (TileIndex start
,
1349 DoCommandFlag flags
, uint32 p1
, uint32 p2
, bool waypoint
)
1351 TileIndex end
= p1
== 0 ? start
: p1
;
1352 if (start
>= MapSize() || end
>= MapSize()) return CMD_ERROR
;
1354 bool keep_rail
= HasBit(p2
, 0);
1356 TileArea
ta(start
, end
);
1357 SmallVector
<BaseStation
*, 4> affected_stations
;
1359 /* Count of the number of tiles removed */
1361 CommandCost
total_cost(EXPENSES_CONSTRUCTION
);
1362 /* Accumulator for the errors seen during clearing. If no errors happen,
1363 * and the quantity is 0 there is no station. Otherwise it will be one
1364 * of the other error that got accumulated. */
1367 /* Do the action for every tile into the area */
1368 TILE_AREA_LOOP(tile
, ta
) {
1369 /* Make sure the specified tile is a rail station */
1370 if (!HasStationTileRail(tile
)) continue;
1372 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1373 StringID str
= CheckVehicleOnGround (tile
);
1374 if (str
!= STR_NULL
) {
1375 error
.AddCost (CommandCost (str
));
1379 /* Check ownership of station */
1380 BaseStation
*st
= BaseStation::GetByTile (tile
);
1381 if (st
== NULL
|| st
->IsWaypoint() != waypoint
) continue;
1383 if (_current_company
!= OWNER_WATER
) {
1384 CommandCost ret
= CheckOwnership(st
->owner
);
1386 if (ret
.Failed()) continue;
1389 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1392 if (keep_rail
|| IsStationTileBlocked(tile
)) {
1393 /* Don't refund the 'steel' of the track when we keep the
1394 * rail, or when the tile didn't have any rail at all. */
1395 total_cost
.AddCost(-_price
[PR_CLEAR_RAIL
]);
1398 if (flags
& DC_EXEC
) {
1399 /* read variables before the station tile is removed */
1400 uint specindex
= GetCustomStationSpecIndex(tile
);
1401 Track track
= GetRailStationTrack(tile
);
1402 Owner owner
= GetTileOwner(tile
);
1403 RailType rt
= GetRailType(tile
);
1406 if (HasStationReservation(tile
)) {
1407 v
= GetTrainForReservation(tile
, track
);
1408 if (v
!= NULL
) FreeTrainReservation(v
);
1411 bool build_rail
= keep_rail
&& !IsStationTileBlocked(tile
);
1412 if (!build_rail
&& !IsStationTileBlocked(tile
)) Company::Get(owner
)->infrastructure
.rail
[rt
]--;
1414 DoClearSquare(tile
);
1415 DeleteNewGRFInspectWindow(GSF_STATIONS
, tile
);
1416 if (build_rail
) MakeRailNormal(tile
, owner
, TrackToTrackBits(track
), rt
);
1417 Company::Get(owner
)->infrastructure
.station
--;
1418 DirtyCompanyInfrastructureWindows(owner
);
1420 st
->AfterRemoveTile(tile
);
1421 AddTrackToSignalBuffer(tile
, track
, owner
);
1422 YapfNotifyTrackLayoutChange();
1424 DeallocateSpecFromStation(st
, specindex
);
1426 affected_stations
.Include(st
);
1428 if (v
!= NULL
) RestoreTrainReservation(v
);
1432 if (quantity
== 0) return error
.Failed() ? error
: CommandCost(STR_ERROR_THERE_IS_NO_STATION
);
1434 for (BaseStation
**stp
= affected_stations
.Begin(); stp
!= affected_stations
.End(); stp
++) {
1435 BaseStation
*st
= *stp
;
1437 /* now we need to make the "spanned" area of the railway station smaller
1438 * if we deleted something at the edges.
1439 * we also need to adjust train_tile. */
1440 st
->train_station
.shrink_span (std::bind1st (std::mem_fun (&BaseStation::TileBelongsToRailStation
), st
));
1441 UpdateStationSign (st
);
1443 /* if we deleted the whole station, delete the train facility. */
1444 if (st
->train_station
.tile
== INVALID_TILE
) {
1445 st
->facilities
&= ~FACIL_TRAIN
;
1446 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_TRAINS
);
1447 DeleteStationIfEmpty(st
);
1451 total_cost
.AddCost(quantity
* _price
[waypoint
? PR_CLEAR_WAYPOINT_RAIL
: PR_CLEAR_STATION_RAIL
]);
1454 /* Do all station specific functions here. */
1455 for (BaseStation
**stp
= affected_stations
.Begin(); stp
!= affected_stations
.End(); stp
++) {
1456 Station
*st
= Station::From(*stp
);
1458 if (st
->train_station
.tile
== INVALID_TILE
) SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_TRAINS
);
1459 st
->MarkTilesDirty(false);
1460 st
->RecomputeIndustriesNear();
1468 * Remove a single tile from a rail station.
1469 * This allows for custom-built station with holes and weird layouts
1470 * @param start tile of station piece to remove
1471 * @param flags operation to perform
1472 * @param p1 start_tile
1473 * @param p2 various bitstuffed elements
1474 * - p2 = bit 0 - if set keep the rail
1475 * @param text unused
1476 * @return the cost of this operation or an error
1478 CommandCost
CmdRemoveFromRailStation(TileIndex start
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1480 return RemoveFromRailBaseStation (start
, flags
, p1
, p2
, false);
1484 * Remove a single tile from a waypoint.
1485 * This allows for custom-built waypoint with holes and weird layouts
1486 * @param start tile of waypoint piece to remove
1487 * @param flags operation to perform
1488 * @param p1 start_tile
1489 * @param p2 various bitstuffed elements
1490 * - p2 = bit 0 - if set keep the rail
1491 * @param text unused
1492 * @return the cost of this operation or an error
1494 CommandCost
CmdRemoveFromRailWaypoint(TileIndex start
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1496 return RemoveFromRailBaseStation (start
, flags
, p1
, p2
, true);
1501 * Remove a rail station/waypoint
1502 * @param st The station/waypoint to remove the rail part from
1503 * @param flags operation to perform
1504 * @param removal_cost the cost for removing a tile
1505 * @return cost or failure of operation
1507 static CommandCost
RemoveRailStation (BaseStation
*st
, DoCommandFlag flags
,
1510 /* Current company owns the station? */
1511 if (_current_company
!= OWNER_WATER
) {
1512 CommandCost ret
= CheckOwnership(st
->owner
);
1513 if (ret
.Failed()) return ret
;
1516 /* determine width and height of platforms */
1517 TileArea ta
= st
->train_station
;
1519 assert(ta
.w
!= 0 && ta
.h
!= 0);
1521 CommandCost
cost(EXPENSES_CONSTRUCTION
);
1522 /* clear all areas of the station */
1523 TILE_AREA_LOOP(tile
, ta
) {
1524 /* only remove tiles that are actually train station tiles */
1525 if (!st
->TileBelongsToRailStation(tile
)) continue;
1527 StringID str
= CheckVehicleOnGround (tile
);
1528 if (str
!= STR_NULL
) return_cmd_error(str
);
1530 cost
.AddCost(removal_cost
);
1531 if (flags
& DC_EXEC
) {
1532 /* read variables before the station tile is removed */
1533 Track track
= GetRailStationTrack(tile
);
1534 Owner owner
= GetTileOwner(tile
); // _current_company can be OWNER_WATER
1536 if (HasStationReservation(tile
)) {
1537 v
= GetTrainForReservation (tile
, track
, true);
1539 if (!IsStationTileBlocked(tile
)) Company::Get(owner
)->infrastructure
.rail
[GetRailType(tile
)]--;
1540 Company::Get(owner
)->infrastructure
.station
--;
1541 DoClearSquare(tile
);
1542 DeleteNewGRFInspectWindow(GSF_STATIONS
, tile
);
1543 AddTrackToSignalBuffer(tile
, track
, owner
);
1544 YapfNotifyTrackLayoutChange();
1545 if (v
!= NULL
) TryPathReserve(v
, true);
1549 if (flags
& DC_EXEC
) {
1550 st
->AfterRemoveRect(st
->train_station
);
1552 st
->train_station
.Clear();
1554 st
->facilities
&= ~FACIL_TRAIN
;
1558 st
->speclist
= NULL
;
1559 st
->cached_anim_triggers
= 0;
1561 DirtyCompanyInfrastructureWindows(st
->owner
);
1562 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_TRAINS
);
1563 UpdateStationSign (st
);
1564 DeleteStationIfEmpty(st
);
1571 * Remove a rail station
1572 * @param tile Tile of the station.
1573 * @param flags operation to perform
1574 * @return cost or failure of operation
1576 static CommandCost
RemoveRailStation(TileIndex tile
, DoCommandFlag flags
)
1578 /* if there is flooding, remove platforms tile by tile */
1579 if (_current_company
== OWNER_WATER
) {
1580 return DoCommand(tile
, 0, 0, DC_EXEC
, CMD_REMOVE_FROM_RAIL_STATION
);
1583 Station
*st
= Station::GetByTile(tile
);
1584 CommandCost cost
= RemoveRailStation(st
, flags
, _price
[PR_CLEAR_STATION_RAIL
]);
1586 if (flags
& DC_EXEC
) st
->RecomputeIndustriesNear();
1592 * Remove a rail waypoint
1593 * @param tile Tile of the waypoint.
1594 * @param flags operation to perform
1595 * @return cost or failure of operation
1597 static CommandCost
RemoveRailWaypoint(TileIndex tile
, DoCommandFlag flags
)
1599 /* if there is flooding, remove waypoints tile by tile */
1600 if (_current_company
== OWNER_WATER
) {
1601 return DoCommand(tile
, 0, 0, DC_EXEC
, CMD_REMOVE_FROM_RAIL_WAYPOINT
);
1604 return RemoveRailStation(Waypoint::GetByTile(tile
), flags
, _price
[PR_CLEAR_WAYPOINT_RAIL
]);
1609 * @param truck_station Determines whether a stop is #ROADSTOP_BUS or #ROADSTOP_TRUCK
1610 * @param st The Station to do the whole procedure for
1611 * @return a pointer to where to link a new RoadStop*
1613 static RoadStop
**FindRoadStopSpot(bool truck_station
, Station
*st
)
1615 RoadStop
**primary_stop
= (truck_station
) ? &st
->truck_stops
: &st
->bus_stops
;
1617 if (*primary_stop
== NULL
) {
1618 /* we have no roadstop of the type yet, so write a "primary stop" */
1619 return primary_stop
;
1621 /* there are stops already, so append to the end of the list */
1622 RoadStop
*stop
= *primary_stop
;
1623 while (stop
->next
!= NULL
) stop
= stop
->next
;
1628 static CommandCost
RemoveRoadStop(TileIndex tile
, DoCommandFlag flags
);
1631 * Build a bus or truck stop.
1632 * @param tile Northernmost tile of the stop.
1633 * @param flags Operation to perform.
1634 * @param p1 bit 0..7: Width of the road stop.
1635 * bit 8..15: Length of the road stop.
1636 * @param p2 bit 0: 0 For bus stops, 1 for truck stops.
1637 * bit 1: 0 For normal stops, 1 for drive-through.
1638 * bit 2..3: The roadtypes.
1639 * bit 5: Allow stations directly adjacent to other stations.
1640 * bit 6..7: Entrance direction (#DiagDirection).
1641 * bit 16..31: Station ID to join (INVALID_STATION if build new one).
1642 * @param text Unused.
1643 * @return The cost of this operation or an error.
1645 CommandCost
CmdBuildRoadStop(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1647 bool type
= HasBit(p2
, 0);
1648 bool is_drive_through
= HasBit(p2
, 1);
1649 RoadTypes rts
= Extract
<RoadTypes
, 2, 2>(p2
);
1650 StationID station_to_join
= GB(p2
, 16, 16);
1652 uint8 width
= (uint8
)GB(p1
, 0, 8);
1653 uint8 lenght
= (uint8
)GB(p1
, 8, 8);
1655 /* Check if the requested road stop is too big */
1656 if (width
> _settings_game
.station
.station_spread
|| lenght
> _settings_game
.station
.station_spread
) return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT
);
1657 /* Check for incorrect width / length. */
1658 if (width
== 0 || lenght
== 0) return CMD_ERROR
;
1659 /* Check if the first tile and the last tile are valid */
1660 if (!IsValidTile(tile
) || TileAddWrap(tile
, width
- 1, lenght
- 1) == INVALID_TILE
) return CMD_ERROR
;
1662 TileArea
roadstop_area(tile
, width
, lenght
);
1664 if (!HasExactlyOneBit(rts
) || !HasRoadTypesAvail(_current_company
, rts
)) return CMD_ERROR
;
1666 /* Trams only have drive through stops */
1667 if (!is_drive_through
&& HasBit(rts
, ROADTYPE_TRAM
)) return CMD_ERROR
;
1669 DiagDirection ddir
= Extract
<DiagDirection
, 6, 2>(p2
);
1671 /* Safeguard the parameters. */
1672 if (!IsValidDiagDirection(ddir
)) return CMD_ERROR
;
1673 /* If it is a drive-through stop, check for valid axis. */
1674 if (is_drive_through
&& !IsValidAxis((Axis
)ddir
)) return CMD_ERROR
;
1676 CommandCost ret
= CheckIfAuthorityAllowsNewStation(tile
, flags
);
1677 if (ret
.Failed()) return ret
;
1679 /* Total road stop cost. */
1680 CommandCost
cost(EXPENSES_CONSTRUCTION
, roadstop_area
.w
* roadstop_area
.h
* _price
[type
? PR_BUILD_STATION_TRUCK
: PR_BUILD_STATION_BUS
]);
1681 StationID est
= INVALID_STATION
;
1682 ret
= CheckFlatLandRoadStop(roadstop_area
, flags
, is_drive_through
? 5 << ddir
: 1 << ddir
, is_drive_through
, type
, DiagDirToAxis(ddir
), &est
, rts
);
1683 if (ret
.Failed()) return ret
;
1687 ret
= BuildStationPart (&st
, roadstop_area
, est
, station_to_join
,
1688 HasBit (p2
, 5), STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST
,
1689 flags
, STATIONNAMING_ROAD
);
1690 if (ret
.Failed()) return ret
;
1692 /* Check if this number of road stops can be allocated. */
1693 if (!RoadStop::CanAllocateItem(roadstop_area
.w
* roadstop_area
.h
)) return_cmd_error(type
? STR_ERROR_TOO_MANY_TRUCK_STOPS
: STR_ERROR_TOO_MANY_BUS_STOPS
);
1695 if (flags
& DC_EXEC
) {
1696 /* Check every tile in the area. */
1697 TILE_AREA_LOOP(cur_tile
, roadstop_area
) {
1698 RoadTypes cur_rts
= (IsRoadTile(cur_tile
) || IsStationTile(cur_tile
)) ? GetRoadTypes(cur_tile
) : ROADTYPES_NONE
;
1699 Owner road_owner
= HasBit(cur_rts
, ROADTYPE_ROAD
) ? GetRoadOwner(cur_tile
, ROADTYPE_ROAD
) : _current_company
;
1700 Owner tram_owner
= HasBit(cur_rts
, ROADTYPE_TRAM
) ? GetRoadOwner(cur_tile
, ROADTYPE_TRAM
) : _current_company
;
1702 if (IsStationTile(cur_tile
) && IsRoadStop(cur_tile
)) {
1703 RemoveRoadStop(cur_tile
, flags
);
1706 RoadStop
*road_stop
= new RoadStop(cur_tile
);
1707 /* Insert into linked list of RoadStops. */
1708 RoadStop
**currstop
= FindRoadStopSpot(type
, st
);
1709 *currstop
= road_stop
;
1712 st
->truck_station
.Add(cur_tile
);
1714 st
->bus_station
.Add(cur_tile
);
1717 /* Initialize an empty station. */
1718 st
->AddFacility((type
) ? FACIL_TRUCK_STOP
: FACIL_BUS_STOP
, cur_tile
);
1720 st
->rect
.Add (cur_tile
);
1722 RoadStopType rs_type
= type
? ROADSTOP_TRUCK
: ROADSTOP_BUS
;
1723 if (is_drive_through
) {
1724 /* Update company infrastructure counts. If the current tile is a normal
1725 * road tile, count only the new road bits needed to get a full diagonal road. */
1727 FOR_EACH_SET_ROADTYPE(rt
, cur_rts
| rts
) {
1728 Company
*c
= Company::GetIfValid(rt
== ROADTYPE_ROAD
? road_owner
: tram_owner
);
1730 c
->infrastructure
.road
[rt
] += 2 - (IsRoadTile(cur_tile
) && HasBit(cur_rts
, rt
) ? CountBits(GetRoadBits(cur_tile
, rt
)) : 0);
1731 DirtyCompanyInfrastructureWindows(c
->index
);
1735 MakeDriveThroughRoadStop(cur_tile
, st
->owner
, road_owner
, tram_owner
, st
->index
, rs_type
, rts
| cur_rts
, DiagDirToAxis(ddir
));
1736 road_stop
->MakeDriveThrough();
1738 /* Non-drive-through stop never overbuild and always count as two road bits. */
1739 Company::Get(st
->owner
)->infrastructure
.road
[FIND_FIRST_BIT(rts
)] += 2;
1740 MakeRoadStop(cur_tile
, st
->owner
, st
->index
, rs_type
, rts
, ddir
);
1742 Company::Get(st
->owner
)->infrastructure
.station
++;
1743 DirtyCompanyInfrastructureWindows(st
->owner
);
1745 MarkTileDirtyByTile(cur_tile
);
1750 st
->UpdateVirtCoord();
1751 UpdateStationAcceptance(st
, false);
1752 st
->RecomputeIndustriesNear();
1753 InvalidateWindowData(WC_SELECT_STATION
, 0, 0);
1754 InvalidateWindowData(WC_STATION_LIST
, st
->owner
, 0);
1755 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_ROADVEHS
);
1762 * Remove a bus station/truck stop
1763 * @param tile TileIndex been queried
1764 * @param flags operation to perform
1765 * @return cost or failure of operation
1767 static CommandCost
RemoveRoadStop(TileIndex tile
, DoCommandFlag flags
)
1769 Station
*st
= Station::GetByTile(tile
);
1771 if (_current_company
!= OWNER_WATER
) {
1772 CommandCost ret
= CheckOwnership(st
->owner
);
1773 if (ret
.Failed()) return ret
;
1776 bool is_truck
= IsTruckStop(tile
);
1778 RoadStop
**primary_stop
;
1780 if (is_truck
) { // truck stop
1781 primary_stop
= &st
->truck_stops
;
1782 cur_stop
= RoadStop::GetByTile(tile
, ROADSTOP_TRUCK
);
1784 primary_stop
= &st
->bus_stops
;
1785 cur_stop
= RoadStop::GetByTile(tile
, ROADSTOP_BUS
);
1788 assert(cur_stop
!= NULL
);
1790 /* don't do the check for drive-through road stops when company bankrupts */
1791 if (IsDriveThroughStopTile(tile
) && (flags
& DC_BANKRUPT
)) {
1792 /* remove the 'going through road stop' status from all vehicles on that tile */
1793 VehicleTileIterator
iter (tile
);
1794 while (!iter
.finished()) {
1795 Vehicle
*v
= iter
.next();
1796 if (v
->type
== VEH_ROAD
) {
1797 /* Okay... we are a road vehicle on a drive through road stop.
1798 * But that road stop has just been removed, so we need to make
1799 * sure we are in a valid state... however, vehicles can also
1800 * turn on road stop tiles, so only clear the 'road stop' state
1801 * bits and only when the state was 'in road stop', otherwise
1802 * we'll end up clearing the turn around bits. */
1803 RoadVehicle
*rv
= RoadVehicle::From(v
);
1804 if (HasBit(rv
->state
, RVS_IN_DT_ROAD_STOP
)) rv
->state
&= RVSB_ROAD_STOP_TRACKDIR_MASK
;
1808 StringID str
= CheckVehicleOnGround (tile
);
1809 if (str
!= STR_NULL
) return_cmd_error(str
);
1812 if (flags
& DC_EXEC
) {
1813 if (*primary_stop
== cur_stop
) {
1814 /* removed the first stop in the list */
1815 *primary_stop
= cur_stop
->next
;
1816 /* removed the only stop? */
1817 if (*primary_stop
== NULL
) {
1818 st
->facilities
&= (is_truck
? ~FACIL_TRUCK_STOP
: ~FACIL_BUS_STOP
);
1821 /* tell the predecessor in the list to skip this stop */
1822 RoadStop
*pred
= *primary_stop
;
1823 while (pred
->next
!= cur_stop
) pred
= pred
->next
;
1824 pred
->next
= cur_stop
->next
;
1827 /* Update company infrastructure counts. */
1829 FOR_EACH_SET_ROADTYPE(rt
, GetRoadTypes(tile
)) {
1830 Company
*c
= Company::GetIfValid(GetRoadOwner(tile
, rt
));
1832 c
->infrastructure
.road
[rt
] -= 2;
1833 DirtyCompanyInfrastructureWindows(c
->index
);
1836 Company::Get(st
->owner
)->infrastructure
.station
--;
1837 DirtyCompanyInfrastructureWindows(st
->owner
);
1839 if (IsDriveThroughStopTile(tile
)) {
1840 /* Clears the tile for us */
1841 cur_stop
->ClearDriveThrough();
1843 DoClearSquare(tile
);
1846 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_ROADVEHS
);
1849 /* Make sure no vehicle is going to the old roadstop */
1851 FOR_ALL_ROADVEHICLES(v
) {
1852 if (v
->First() == v
&& v
->current_order
.IsType(OT_GOTO_STATION
) &&
1853 v
->dest_tile
== tile
) {
1854 v
->dest_tile
= v
->GetOrderStationLocation(st
->index
);
1858 st
->AfterRemoveTile(tile
);
1860 UpdateStationSign (st
);
1861 st
->RecomputeIndustriesNear();
1862 DeleteStationIfEmpty(st
);
1864 /* Update the tile area of the truck/bus stop */
1866 st
->truck_station
.Clear();
1867 for (const RoadStop
*rs
= st
->truck_stops
; rs
!= NULL
; rs
= rs
->next
) st
->truck_station
.Add(rs
->xy
);
1869 st
->bus_station
.Clear();
1870 for (const RoadStop
*rs
= st
->bus_stops
; rs
!= NULL
; rs
= rs
->next
) st
->bus_station
.Add(rs
->xy
);
1874 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[is_truck
? PR_CLEAR_STATION_TRUCK
: PR_CLEAR_STATION_BUS
]);
1878 * Remove bus or truck stops.
1879 * @param tile Northernmost tile of the removal area.
1880 * @param flags Operation to perform.
1881 * @param p1 bit 0..7: Width of the removal area.
1882 * bit 8..15: Height of the removal area.
1883 * @param p2 bit 0: 0 For bus stops, 1 for truck stops.
1884 * @param p2 bit 1: 0 to keep roads of all drive-through stops, 1 to remove them.
1885 * @param text Unused.
1886 * @return The cost of this operation or an error.
1888 CommandCost
CmdRemoveRoadStop(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1890 uint8 width
= (uint8
)GB(p1
, 0, 8);
1891 uint8 height
= (uint8
)GB(p1
, 8, 8);
1892 bool keep_drive_through_roads
= !HasBit(p2
, 1);
1894 /* Check for incorrect width / height. */
1895 if (width
== 0 || height
== 0) return CMD_ERROR
;
1896 /* Check if the first tile and the last tile are valid */
1897 if (!IsValidTile(tile
) || TileAddWrap(tile
, width
- 1, height
- 1) == INVALID_TILE
) return CMD_ERROR
;
1898 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
1899 if (!keep_drive_through_roads
&& (flags
& DC_BANKRUPT
)) return CMD_ERROR
;
1901 TileArea
roadstop_area(tile
, width
, height
);
1903 CommandCost
cost(EXPENSES_CONSTRUCTION
);
1904 CommandCost
last_error(STR_ERROR_THERE_IS_NO_STATION
);
1905 bool had_success
= false;
1907 TILE_AREA_LOOP(cur_tile
, roadstop_area
) {
1908 /* Make sure the specified tile is a road stop of the correct type */
1909 if (!IsStationTile(cur_tile
) || !IsRoadStop(cur_tile
) || (uint32
)GetRoadStopType(cur_tile
) != GB(p2
, 0, 1)) continue;
1911 /* Save information on to-be-restored roads before the stop is removed. */
1912 RoadTypes rts
= ROADTYPES_NONE
;
1913 RoadBits road_bits
= ROAD_NONE
;
1914 Owner road_owner
[] = { OWNER_NONE
, OWNER_NONE
};
1915 assert_compile(lengthof(road_owner
) == ROADTYPE_END
);
1916 if (IsDriveThroughStopTile(cur_tile
)) {
1918 FOR_EACH_SET_ROADTYPE(rt
, GetRoadTypes(cur_tile
)) {
1919 road_owner
[rt
] = GetRoadOwner(cur_tile
, rt
);
1920 /* If we don't want to preserve our roads then restore only roads of others. */
1921 if (keep_drive_through_roads
|| road_owner
[rt
] != _current_company
) SetBit(rts
, rt
);
1923 road_bits
= AxisToRoadBits (GetRoadStopAxis (cur_tile
));
1926 CommandCost ret
= RemoveRoadStop(cur_tile
, flags
);
1934 /* Restore roads. */
1935 if ((flags
& DC_EXEC
) && rts
!= ROADTYPES_NONE
) {
1936 MakeRoadNormal(cur_tile
, road_bits
, rts
, ClosestTownFromTile(cur_tile
)->index
,
1937 road_owner
[ROADTYPE_ROAD
], road_owner
[ROADTYPE_TRAM
]);
1939 /* Update company infrastructure counts. */
1941 FOR_EACH_SET_ROADTYPE(rt
, rts
) {
1942 Company
*c
= Company::GetIfValid(GetRoadOwner(cur_tile
, rt
));
1944 c
->infrastructure
.road
[rt
] += CountBits(road_bits
);
1945 DirtyCompanyInfrastructureWindows(c
->index
);
1951 return had_success
? cost
: last_error
;
1955 * Computes the minimal distance from town's xy to any airport's tile.
1956 * @param att Airport tile table
1957 * @param airport_tile Airport reference tile
1958 * @param town_tile town's tile (t->xy)
1959 * @return minimal manhattan distance from town_tile to any airport's tile
1961 static uint
GetMinimalAirportDistanceToTile (const AirportTileTable
*att
,
1962 TileIndex airport_tile
, TileIndex town_tile
)
1964 uint mindist
= UINT_MAX
;
1966 for (AirportTileTableIterator
iter (att
, airport_tile
); iter
!= INVALID_TILE
; ++iter
) {
1967 mindist
= min (mindist
, DistanceManhattan (town_tile
, iter
));
1974 * Get a possible noise reduction factor based on distance from town center.
1975 * The further you get, the less noise you generate.
1976 * So all those folks at city council can now happily slee... work in their offices
1977 * @param as airport information
1978 * @param layout Airport layout
1979 * @param airport_tile Airport reference tile
1980 * @param town_tile TileIndex of town's center, the one who will receive the airport's candidature
1981 * @return the noise that will be generated, according to distance
1983 uint8
GetAirportNoiseLevelForTown (const AirportSpec
*as
, uint layout
,
1984 TileIndex airport_tile
, TileIndex town_tile
)
1986 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
1987 * So no need to go any further*/
1988 if (as
->noise_level
< 2) return as
->noise_level
;
1990 uint distance
= GetMinimalAirportDistanceToTile (as
->table
[layout
], airport_tile
, town_tile
);
1992 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
1993 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
1994 * Basically, it says that the less tolerant a town is, the bigger the distance before
1995 * an actual decrease can be granted */
1996 uint8 town_tolerance_distance
= 8 + (_settings_game
.difficulty
.town_council_tolerance
* 4);
1998 /* now, we want to have the distance segmented using the distance judged bareable by town
1999 * This will give us the coefficient of reduction the distance provides. */
2000 uint noise_reduction
= distance
/ town_tolerance_distance
;
2002 /* If the noise reduction equals the airport noise itself, don't give it for free.
2003 * Otherwise, simply reduce the airport's level. */
2004 return noise_reduction
>= as
->noise_level
? 1 : as
->noise_level
- noise_reduction
;
2008 * Finds the town nearest to given airport. Based on minimal manhattan distance to any airport's tile.
2009 * If two towns have the same distance, town with lower index is returned.
2010 * @param as airport's description
2011 * @param layout Airport layout
2012 * @param tile Airport reference tile
2013 * @return nearest town to airport
2015 Town
*AirportGetNearestTown (const AirportSpec
*as
, uint layout
, TileIndex tile
)
2017 Town
*t
, *nearest
= NULL
;
2018 uint add
= as
->size_x
+ as
->size_y
- 2; // GetMinimalAirportDistanceToTile can differ from DistanceManhattan by this much
2019 uint mindist
= UINT_MAX
- add
; // prevent overflow
2020 const AirportTileTable
*att
= as
->table
[layout
];
2022 if (DistanceManhattan (t
->xy
, tile
) < mindist
+ add
) { // avoid calling GetMinimalAirportDistanceToTile too often
2023 uint dist
= GetMinimalAirportDistanceToTile (att
, tile
, t
->xy
);
2024 if (dist
< mindist
) {
2035 /** Recalculate the noise generated by the airports of each town */
2036 void UpdateAirportsNoise()
2041 FOR_ALL_TOWNS(t
) t
->noise_reached
= 0;
2043 FOR_ALL_STATIONS(st
) {
2044 if (st
->airport
.tile
!= INVALID_TILE
&& st
->airport
.type
!= AT_OILRIG
) {
2045 const AirportSpec
*as
= st
->airport
.GetSpec();
2046 Town
*nearest
= AirportGetNearestTown (as
, st
->airport
.layout
, st
->airport
.tile
);
2047 nearest
->noise_reached
+= GetAirportNoiseLevelForTown (as
, st
->airport
.layout
, st
->airport
.tile
, nearest
->xy
);
2054 * Checks if an airport can be removed (no aircraft on it or landing)
2055 * @param st Station whose airport is to be removed
2056 * @param flags Operation to perform
2057 * @return Cost or failure of operation
2059 static CommandCost
CanRemoveAirport(Station
*st
, DoCommandFlag flags
)
2062 FOR_ALL_AIRCRAFT(a
) {
2063 if (!a
->IsNormalAircraft()) continue;
2064 if (a
->targetairport
== st
->index
&& a
->state
!= FLYING
)
2065 return_cmd_error(STR_ERROR_AIRCRAFT_IN_THE_WAY
);
2068 CommandCost
cost(EXPENSES_CONSTRUCTION
);
2070 TILE_AREA_LOOP(tile_cur
, st
->airport
) {
2071 if (!st
->TileBelongsToAirport(tile_cur
)) continue;
2073 StringID str
= CheckVehicleOnGround (tile_cur
);
2074 if (str
!= STR_NULL
) return_cmd_error(str
);
2076 cost
.AddCost(_price
[PR_CLEAR_STATION_AIRPORT
]);
2082 /** Clear the map area of an airport and delete related windows. */
2083 static void ClearAirportArea (Station
*st
)
2085 for (uint i
= 0; i
< st
->airport
.GetNumHangars(); ++i
) {
2086 TileIndex tile
= st
->airport
.GetHangarTile (i
);
2087 DeleteWindowById (WC_VEHICLE_DEPOT
, tile
);
2088 OrderBackup::Reset (tile
, false);
2091 TILE_AREA_LOOP(tile
, st
->airport
) {
2092 if (st
->TileBelongsToAirport (tile
)) {
2093 DeleteAnimatedTile (tile
);
2094 DoClearSquare (tile
);
2095 DeleteNewGRFInspectWindow (GSF_AIRPORTTILES
, tile
);
2099 /* Clear the persistent storage. */
2100 delete st
->airport
.psa
;
2101 st
->airport
.psa
= NULL
;
2106 * @param tile tile where airport will be built
2107 * @param flags operation to perform
2109 * - p1 = (bit 0- 7) - airport type, @see airport.h
2110 * - p1 = (bit 8-15) - airport layout
2111 * @param p2 various bitstuffed elements
2112 * - p2 = (bit 0) - allow airports directly adjacent to other airports.
2113 * - p2 = (bit 16-31) - station ID to join (INVALID_STATION if build new one)
2114 * @param text unused
2115 * @return the cost of this operation or an error
2117 CommandCost
CmdBuildAirport(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
2119 StationID station_to_join
= GB(p2
, 16, 16);
2120 byte airport_type
= GB(p1
, 0, 8);
2121 byte layout
= GB(p1
, 8, 8);
2123 if (airport_type
>= NUM_AIRPORTS
) return CMD_ERROR
;
2125 CommandCost ret
= CheckIfAuthorityAllowsNewStation(tile
, flags
);
2126 if (ret
.Failed()) return ret
;
2128 /* Check if a valid, buildable airport was chosen for construction */
2129 const AirportSpec
*as
= AirportSpec::Get(airport_type
);
2130 if (!as
->IsAvailable() || layout
>= as
->num_table
) return CMD_ERROR
;
2132 Direction rotation
= (Direction
)as
->table
[layout
]->rotation
;
2135 if (rotation
== DIR_E
|| rotation
== DIR_W
) Swap(w
, h
);
2137 if (w
> _settings_game
.station
.station_spread
|| h
> _settings_game
.station
.station_spread
) {
2138 return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT
);
2141 StationID est
= INVALID_STATION
;
2142 CommandCost cost
= CheckFlatLandAirport (tile
, as
->table
[layout
],
2144 if (cost
.Failed()) return cost
;
2147 ret
= BuildStationPart (&st
, TileArea (tile
, w
, h
), est
,
2148 station_to_join
, HasBit (p2
, 0),
2149 STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST
, flags
,
2150 (as
->fsm
->flags
& AirportFTA::AIRPLANES
) ?
2151 STATIONNAMING_AIRPORT
: STATIONNAMING_HELIPORT
);
2152 if (ret
.Failed()) return ret
;
2154 /* action to be performed */
2156 AIRPORT_NEW
, // airport is a new station
2157 AIRPORT_ADD
, // add an airport to an existing station
2158 AIRPORT_UPGRADE
, // upgrade the airport in a station
2160 (est
!= INVALID_STATION
) ? AIRPORT_UPGRADE
:
2161 (st
!= NULL
) ? AIRPORT_ADD
: AIRPORT_NEW
;
2163 if (action
== AIRPORT_ADD
&& st
->airport
.tile
!= INVALID_TILE
) {
2164 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT
);
2167 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2168 Town
*nearest
= AirportGetNearestTown (as
, layout
, tile
);
2169 uint newnoise_level
= nearest
->noise_reached
+ GetAirportNoiseLevelForTown (as
, layout
, tile
, nearest
->xy
);
2171 if (action
== AIRPORT_UPGRADE
) {
2172 const AirportSpec
*old_as
= st
->airport
.GetSpec();
2173 Town
*old_nearest
= AirportGetNearestTown (old_as
, st
->airport
.layout
, st
->airport
.tile
);
2174 if (old_nearest
== nearest
) {
2175 newnoise_level
-= GetAirportNoiseLevelForTown (old_as
, st
->airport
.layout
, st
->airport
.tile
, nearest
->xy
);
2179 /* Check if local auth would allow a new airport */
2180 StringID authority_refuse_message
= STR_NULL
;
2181 Town
*authority_refuse_town
= NULL
;
2183 if (_settings_game
.economy
.station_noise_level
) {
2184 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2185 if (newnoise_level
> nearest
->MaxTownNoise()) {
2186 authority_refuse_message
= STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE
;
2187 authority_refuse_town
= nearest
;
2189 } else if (action
!= AIRPORT_UPGRADE
) {
2190 Town
*t
= ClosestTownFromTile(tile
);
2193 FOR_ALL_STATIONS(st
) {
2194 if (st
->town
== t
&& (st
->facilities
& FACIL_AIRPORT
) && st
->airport
.type
!= AT_OILRIG
) num
++;
2197 authority_refuse_message
= STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT
;
2198 authority_refuse_town
= t
;
2202 if (authority_refuse_message
!= STR_NULL
) {
2203 SetDParam(0, authority_refuse_town
->index
);
2204 return_cmd_error(authority_refuse_message
);
2207 if (action
== AIRPORT_UPGRADE
) {
2208 /* check that the old airport can be removed */
2209 CommandCost r
= CanRemoveAirport(st
, flags
);
2210 if (r
.Failed()) return r
;
2214 for (AirportTileTableIterator
iter(as
->table
[layout
], tile
); iter
!= INVALID_TILE
; ++iter
) {
2215 cost
.AddCost(_price
[PR_BUILD_STATION_AIRPORT
]);
2218 if (flags
& DC_EXEC
) {
2219 if (action
== AIRPORT_UPGRADE
) {
2220 /* delete old airport if upgrading */
2221 const AirportSpec
*old_as
= st
->airport
.GetSpec();
2222 Town
*old_nearest
= AirportGetNearestTown (old_as
, st
->airport
.layout
, st
->airport
.tile
);
2224 if (old_nearest
!= nearest
) {
2225 old_nearest
->noise_reached
-= GetAirportNoiseLevelForTown (old_as
, st
->airport
.layout
, st
->airport
.tile
, old_nearest
->xy
);
2226 if (_settings_game
.economy
.station_noise_level
) {
2227 SetWindowDirty(WC_TOWN_VIEW
, st
->town
->index
);
2231 ClearAirportArea (st
);
2233 st
->AfterRemoveRect(st
->airport
);
2234 st
->airport
.Clear();
2237 /* Always add the noise, so there will be no need to recalculate when option toggles */
2238 nearest
->noise_reached
= newnoise_level
;
2240 st
->AddFacility(FACIL_AIRPORT
, tile
);
2241 st
->airport
.type
= airport_type
;
2242 st
->airport
.layout
= layout
;
2243 st
->airport
.flags
= 0;
2244 st
->airport
.rotation
= rotation
;
2246 st
->rect
.Add (TileArea (tile
, w
, h
));
2248 for (AirportTileTableIterator
iter(as
->table
[layout
], tile
); iter
!= INVALID_TILE
; ++iter
) {
2249 MakeAirport(iter
, st
->owner
, st
->index
, iter
.GetStationGfx(), WATER_CLASS_INVALID
);
2250 SetStationTileRandomBits(iter
, GB(Random(), 0, 4));
2251 st
->airport
.Add(iter
);
2253 if (AirportTileSpec::Get(GetTranslatedAirportTileID(iter
.GetStationGfx()))->animation
.status
!= ANIM_STATUS_NO_ANIMATION
) AddAnimatedTile(iter
);
2256 /* Only call the animation trigger after all tiles have been built */
2257 for (AirportTileTableIterator
iter(as
->table
[layout
], tile
); iter
!= INVALID_TILE
; ++iter
) {
2258 AirportTileAnimationTrigger(st
, iter
, AAT_BUILT
);
2261 if (action
!= AIRPORT_NEW
) UpdateAirplanesOnNewStation(st
);
2263 if (action
== AIRPORT_UPGRADE
) {
2264 UpdateStationSign (st
);
2266 Company::Get(st
->owner
)->infrastructure
.airport
++;
2267 DirtyCompanyInfrastructureWindows(st
->owner
);
2268 st
->UpdateVirtCoord();
2271 UpdateStationAcceptance(st
, false);
2272 st
->RecomputeIndustriesNear();
2273 InvalidateWindowData(WC_SELECT_STATION
, 0, 0);
2274 InvalidateWindowData(WC_STATION_LIST
, st
->owner
, 0);
2275 InvalidateWindowData(WC_STATION_VIEW
, st
->index
, -1);
2277 if (_settings_game
.economy
.station_noise_level
) {
2278 SetWindowDirty(WC_TOWN_VIEW
, st
->town
->index
);
2287 * @param tile TileIndex been queried
2288 * @param flags operation to perform
2289 * @return cost or failure of operation
2291 static CommandCost
RemoveAirport(TileIndex tile
, DoCommandFlag flags
)
2293 Station
*st
= Station::GetByTile(tile
);
2295 if (_current_company
!= OWNER_WATER
) {
2296 CommandCost ret
= CheckOwnership(st
->owner
);
2297 if (ret
.Failed()) return ret
;
2300 CommandCost cost
= CanRemoveAirport(st
, flags
);
2301 if (cost
.Failed()) return cost
;
2303 if (flags
& DC_EXEC
) {
2304 const AirportSpec
*as
= st
->airport
.GetSpec();
2305 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2306 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2307 * need of recalculation */
2308 Town
*nearest
= AirportGetNearestTown (as
, st
->airport
.layout
, st
->airport
.tile
);
2309 nearest
->noise_reached
-= GetAirportNoiseLevelForTown (as
, st
->airport
.layout
, st
->airport
.tile
, nearest
->xy
);
2311 ClearAirportArea (st
);
2313 st
->AfterRemoveRect(st
->airport
);
2315 st
->airport
.Clear();
2316 st
->facilities
&= ~FACIL_AIRPORT
;
2318 InvalidateWindowData(WC_STATION_VIEW
, st
->index
, -1);
2320 if (_settings_game
.economy
.station_noise_level
) {
2321 SetWindowDirty(WC_TOWN_VIEW
, st
->town
->index
);
2324 Company::Get(st
->owner
)->infrastructure
.airport
--;
2325 DirtyCompanyInfrastructureWindows(st
->owner
);
2327 UpdateStationSign (st
);
2328 st
->RecomputeIndustriesNear();
2329 DeleteStationIfEmpty(st
);
2330 DeleteNewGRFInspectWindow(GSF_AIRPORTS
, st
->index
);
2337 * Open/close an airport to incoming aircraft.
2338 * @param tile Unused.
2339 * @param flags Operation to perform.
2340 * @param p1 Station ID of the airport.
2342 * @param text unused
2343 * @return the cost of this operation or an error
2345 CommandCost
CmdOpenCloseAirport(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
2347 if (!Station::IsValidID(p1
)) return CMD_ERROR
;
2348 Station
*st
= Station::Get(p1
);
2350 if (!(st
->facilities
& FACIL_AIRPORT
) || st
->owner
== OWNER_NONE
) return CMD_ERROR
;
2352 CommandCost ret
= CheckOwnership(st
->owner
);
2353 if (ret
.Failed()) return ret
;
2355 if (flags
& DC_EXEC
) {
2356 st
->airport
.flags
^= AIRPORT_CLOSED_block
;
2357 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_CLOSE_AIRPORT
);
2359 return CommandCost();
2363 * Tests whether the company's vehicles have this station in orders
2364 * @param station station ID
2365 * @param include_company If true only check vehicles of \a company, if false only check vehicles of other companies
2366 * @param company company ID
2368 bool HasStationInUse(StationID station
, bool include_company
, CompanyID company
)
2371 FOR_ALL_VEHICLES(v
) {
2372 if ((v
->owner
== company
) == include_company
) {
2374 FOR_VEHICLE_ORDERS(v
, order
) {
2375 if ((order
->IsType(OT_GOTO_STATION
) || order
->IsType(OT_GOTO_WAYPOINT
)) && order
->GetDestination() == station
) {
2384 /** Information about dock tile area for a given direction. */
2385 struct DockTileArea
{
2386 CoordDiff offset
; ///< offset to northern tile
2387 byte width
; ///< width of dock area
2388 byte height
; ///< height of dock area
2392 * Build a dock/haven.
2393 * @param tile tile where dock will be built
2394 * @param flags operation to perform
2395 * @param p1 (bit 0) - allow docks directly adjacent to other docks.
2396 * @param p2 bit 16-31: station ID to join (INVALID_STATION if build new one)
2397 * @param text unused
2398 * @return the cost of this operation or an error
2400 CommandCost
CmdBuildDock(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
2402 static const DockTileArea dock_tilearea
[DIAGDIR_END
] = {
2403 { { -1, 0 }, 2, 1 },
2406 { { 0, -1 }, 1, 2 },
2409 StationID station_to_join
= GB(p2
, 16, 16);
2411 Slope slope
= GetTileSlope (tile
);
2412 DiagDirection direction
= GetInclinedSlopeDirection (slope
);
2415 if (direction
!= INVALID_DIAGDIR
) {
2416 /* Docks cannot be placed on rapids */
2417 if (HasTileWaterGround(tile
)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE
);
2419 direction
= ReverseDiagDir(direction
);
2421 CommandCost ret
= CheckIfAuthorityAllowsNewStation(tile
, flags
);
2422 if (ret
.Failed()) return ret
;
2424 ret
= DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
2425 if (ret
.Failed()) return ret
;
2427 TileIndex tile_cur
= tile
+ TileOffsByDiagDir(direction
);
2430 if (!IsWaterTile (tile_cur
) || !IsTileFlat (tile_cur
, &h
)) {
2431 return_cmd_error(STR_ERROR_SITE_UNSUITABLE
);
2434 if (HasBridgeAbove (tile_cur
)
2435 && (GetBridgeHeight (GetSouthernBridgeEnd (tile_cur
)) < h
+ 2)) {
2436 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
2439 /* Get the water class of the water tile before it is cleared.*/
2440 wc
= GetWaterClass (tile_cur
);
2442 ret
= DoCommand(tile_cur
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
2443 if (ret
.Failed()) return ret
;
2445 tile_cur
+= TileOffsByDiagDir(direction
);
2446 if (!IsWaterTile(tile_cur
) || !IsTileFlat(tile_cur
)) {
2447 return_cmd_error(STR_ERROR_SITE_UNSUITABLE
);
2450 dock_area
= TileArea(tile
+ ToTileIndexDiff(dock_tilearea
[direction
].offset
),
2451 dock_tilearea
[direction
].width
, dock_tilearea
[direction
].height
);
2452 } else if (slope
== SLOPE_FLAT
) {
2453 if (!HasTileWaterGround(tile
)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE
);
2455 CommandCost ret
= CheckIfAuthorityAllowsNewStation(tile
, flags
);
2456 if (ret
.Failed()) return ret
;
2458 /* Get the water class of the water tile before it is cleared.*/
2459 wc
= GetWaterClass (tile
);
2460 ret
= DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
2461 if (ret
.Failed()) return ret
;
2463 dock_area
= TileArea (tile
);
2465 return_cmd_error(STR_ERROR_SITE_UNSUITABLE
);
2470 CommandCost ret
= BuildStationPart (&st
, dock_area
, INVALID_STATION
,
2471 station_to_join
, HasBit (p1
, 0), INVALID_STRING_ID
,
2472 flags
, STATIONNAMING_DOCK
);
2473 if (ret
.Failed()) return ret
;
2475 /* Check if we can allocate a new dock. */
2476 if (!Dock::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_DOCKS
);
2478 if (flags
& DC_EXEC
) {
2479 Dock
**dl
= &st
->docks
;
2480 while (*dl
!= NULL
) dl
= &(*dl
)->next
;
2482 *dl
= new Dock(tile
);
2483 st
->dock_area
.Add(dock_area
);
2485 st
->AddFacility(FACIL_DOCK
, tile
);
2487 st
->rect
.Add (dock_area
);
2489 /* If the water part of the dock is on a canal, update infrastructure counts.
2490 * This is needed as we've unconditionally cleared that tile before. */
2491 if (wc
== WATER_CLASS_CANAL
) {
2492 Company::Get(st
->owner
)->infrastructure
.water
++;
2494 Company::Get(st
->owner
)->infrastructure
.station
+= 2;
2495 DirtyCompanyInfrastructureWindows(st
->owner
);
2497 if (direction
!= INVALID_DIAGDIR
) {
2498 MakeDock (tile
, st
->owner
, st
->index
, direction
, wc
);
2500 MakeDockBuoy (tile
, st
->owner
, st
->index
, wc
);
2503 st
->UpdateVirtCoord();
2504 UpdateStationAcceptance(st
, false);
2505 st
->RecomputeIndustriesNear();
2506 InvalidateWindowData(WC_SELECT_STATION
, 0, 0);
2507 InvalidateWindowData(WC_STATION_LIST
, st
->owner
, 0);
2508 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_SHIPS
);
2511 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_STATION_DOCK
]);
2516 * @param tile TileIndex been queried
2517 * @param flags operation to perform
2518 * @return cost or failure of operation
2520 static CommandCost
RemoveDock(TileIndex tile
, DoCommandFlag flags
)
2522 assert(IsDock(tile
));
2524 Station
*st
= Station::GetByTile(tile
);
2525 CommandCost ret
= CheckOwnership(st
->owner
);
2526 if (ret
.Failed()) return ret
;
2528 Dock
**d
= &st
->docks
;
2529 TileIndex tile1
, tile2
;
2530 while ( tile1
= (*d
)->xy
, tile2
= GetOtherDockTile(tile1
),
2531 tile
!= tile1
&& tile
!= tile2
) {
2532 /* the dock should really be there, so no check for NULL */
2536 StringID str
= CheckVehicleOnGround (tile1
);
2537 if (str
== STR_NULL
&& tile2
!= INVALID_TILE
) str
= CheckVehicleOnGround (tile2
);
2538 if (str
!= STR_NULL
) return_cmd_error(str
);
2540 if (flags
& DC_EXEC
) {
2541 TileIndex docking_location
= GetDockingTile(tile1
);
2543 TileArea
dock_area (tile1
);
2544 if (tile2
!= INVALID_TILE
) {
2545 DoClearSquare (tile1
);
2546 MarkTileDirtyByTile (tile1
);
2547 MakeWaterKeepingClass (tile2
, st
->owner
);
2548 dock_area
.Add (tile2
);
2550 MakeWaterKeepingClass (tile1
, st
->owner
);
2552 st
->AfterRemoveRect (dock_area
);
2554 Dock
*next
= (*d
)->next
;
2557 if (next
== NULL
&& d
== &st
->docks
) st
->facilities
&= ~FACIL_DOCK
;
2559 Company::Get(st
->owner
)->infrastructure
.station
-= 2;
2560 DirtyCompanyInfrastructureWindows(st
->owner
);
2562 /* Update the tile area of the docks */
2563 st
->dock_area
.Clear();
2564 for (const Dock
*dock
= st
->docks
; dock
!= NULL
; dock
= dock
->next
) {
2565 st
->dock_area
.Add(dock
->xy
);
2566 TileIndex other
= GetOtherDockTile (dock
->xy
);
2567 if (other
!= INVALID_TILE
) st
->dock_area
.Add (other
);
2570 SetWindowWidgetDirty(WC_STATION_VIEW
, st
->index
, WID_SV_SHIPS
);
2571 UpdateStationSign (st
);
2572 st
->RecomputeIndustriesNear();
2573 DeleteStationIfEmpty(st
);
2575 /* All ships that were going to our station, can't go to it anymore.
2576 * Just clear the order, then automatically the next appropriate order
2577 * will be selected and in case of no appropriate order it will just
2578 * wander around the world. */
2581 if (s
->current_order
.IsType(OT_LOADING
) && s
->tile
== docking_location
) {
2585 if (s
->dest_tile
== docking_location
) {
2587 s
->current_order
.Clear();
2592 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_STATION_DOCK
]);
2595 #include "table/station_land.h"
2597 const DrawTileSprites
*GetDefaultStationTileLayout (void)
2599 return _station_display_datas_rail
;
2603 * Check whether a sprite is a track sprite that can be replaced by a non-track
2604 * ground sprite and a rail overlay. If the ground sprite is suitable,
2605 * \a ground is replaced with the new non-track ground sprite, and
2606 * \a overlay_offset is set to the overlay to draw.
2607 * @param [in,out] ground Groundsprite to draw.
2608 * @param [out] overlay_offset Overlay to draw.
2609 * @return true if overlay can be drawn.
2611 bool SplitGroundSpriteForOverlay (SpriteID
*ground
, RailTrackOffset
*overlay_offset
)
2614 case SPR_RAIL_TRACK_X
:
2615 *ground
= SPR_FLAT_GRASS_TILE
;
2616 *overlay_offset
= RTO_X
;
2619 case SPR_RAIL_TRACK_Y
:
2620 *ground
= SPR_FLAT_GRASS_TILE
;
2621 *overlay_offset
= RTO_Y
;
2624 case SPR_RAIL_TRACK_X_SNOW
:
2625 *ground
= SPR_FLAT_SNOW_DESERT_TILE
;
2626 *overlay_offset
= RTO_X
;
2629 case SPR_RAIL_TRACK_Y_SNOW
:
2630 *ground
= SPR_FLAT_SNOW_DESERT_TILE
;
2631 *overlay_offset
= RTO_Y
;
2640 * Get the ground sprite to use for an overlay depending on landscape.
2641 * @param ti Positional info for the tile to decide snowyness etc.
2642 * @param [in,out] ground Groundsprite to draw.
2644 static void AdjustGroundSpriteForOverlay (const TileInfo
*ti
, SpriteID
*ground
)
2648 /* Decide snow/desert from tile */
2649 switch (_settings_game
.game_creation
.landscape
) {
2651 snow_desert
= (uint
)ti
->z
> GetSnowLine() * TILE_HEIGHT
;
2655 snow_desert
= GetTropicZone(ti
->tile
) == TROPICZONE_DESERT
;
2662 *ground
= snow_desert
? SPR_FLAT_SNOW_DESERT_TILE
: SPR_FLAT_GRASS_TILE
;
2665 static void DrawTile_Airport (TileInfo
*ti
)
2667 StationGfx gfx
= GetAirportGfx (ti
->tile
);
2668 if (gfx
>= NEW_AIRPORTTILE_OFFSET
) {
2669 const AirportTileSpec
*ats
= AirportTileSpec::Get (gfx
);
2670 if (ats
->grf_prop
.spritegroup
!= NULL
&& DrawNewAirportTile (ti
, Station::GetByTile(ti
->tile
), gfx
, ats
)) {
2673 /* No sprite group (or no valid one) found, meaning no graphics associated.
2674 * Use the substitute one instead */
2675 assert (ats
->grf_prop
.subst_id
!= INVALID_AIRPORTTILE
);
2676 gfx
= ats
->grf_prop
.subst_id
;
2679 const DrawTileSprites
*t
= &_station_display_datas_airport
[gfx
];
2680 PalSpriteID ground
= t
->ground
;
2681 const DrawTileSeqStruct
*const *seq
;
2684 case APT_GRASS_FENCE_NE_FLAG
:
2685 case APT_GRASS_FENCE_NE_FLAG_2
:
2686 seq
= _station_display_datas_airport_flag_grass_fence_ne
;
2688 case APT_RADAR_GRASS_FENCE_SW
:
2689 case APT_RADAR_FENCE_SW
:
2690 seq
= _station_display_datas_airport_radar_fence_sw
;
2692 case APT_RADAR_FENCE_NE
:
2693 seq
= _station_display_datas_airport_radar_fence_ne
;
2700 if (anim
) seq
+= GetAnimationFrame (ti
->tile
);
2702 if (ti
->tileh
!= SLOPE_FLAT
) {
2703 DrawFoundation (ti
, FOUNDATION_LEVELED
);
2706 Owner owner
= GetTileOwner (ti
->tile
);
2707 PaletteID palette
= COMPANY_SPRITE_COLOUR(owner
);
2709 SpriteID image
= ground
.sprite
;
2710 PaletteID pal
= ground
.pal
;
2711 DrawGroundSprite (ti
, image
, GroundSpritePaletteTransform (image
, pal
, palette
));
2713 DrawOrigTileSeq (ti
, *seq
, TO_BUILDINGS
, palette
);
2717 * Draw custom foundations for a station tile.
2718 * @param ti TileInfo of the tile.
2719 * @param statspec Station spec.
2720 * @param st Station.
2721 * @param tile_layout Tile layout.
2722 * @return Whether foundations were actually drawn.
2724 static bool DrawRailStationFoundation (TileInfo
*ti
,
2725 const StationSpec
*statspec
, BaseStation
*st
, uint tile_layout
)
2727 /* Check whether the foundation continues beyond the tile's upper sides. */
2728 uint edge_info
= GetFoundationSpriteBlock (ti
->tile
);
2729 SpriteID image
= GetCustomStationFoundationRelocation (statspec
, st
, ti
->tile
, tile_layout
, edge_info
);
2730 if (image
== 0) return false;
2732 if (HasBit(statspec
->flags
, SSF_EXTENDED_FOUNDATIONS
)) {
2733 /* Station provides extended foundations. */
2734 static const uint8 foundation_parts
[] = {
2735 0, 0, 0, 0, // Invalid, Invalid, Invalid, SLOPE_SW
2736 0, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
2737 0, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
2738 7, 8, 9 // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
2741 AddSortableSpriteToDraw (ti
->vd
, image
+ foundation_parts
[ti
->tileh
],
2742 PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 7, ti
->z
);
2744 /* Draw simple foundations, built up from 8 possible foundation sprites. */
2746 /* Each set bit represents one of the eight composite sprites to be drawn.
2747 * 'Invalid' entries will not drawn but are included for completeness. */
2748 static const uint8 composite_foundation_parts
[] = {
2749 /* Invalid (00000000), Invalid (11010001), Invalid (11100100), SLOPE_SW (11100000) */
2750 0x00, 0xD1, 0xE4, 0xE0,
2751 /* Invalid (11001010), SLOPE_EW (11001001), SLOPE_SE (11000100), SLOPE_WSE (11000000) */
2752 0xCA, 0xC9, 0xC4, 0xC0,
2753 /* Invalid (11010010), SLOPE_NW (10010001), SLOPE_NS (11100100), SLOPE_NWS (10100000) */
2754 0xD2, 0x91, 0xE4, 0xA0,
2755 /* SLOPE_NE (01001010), SLOPE_ENW (00001001), SLOPE_SEN (01000100) */
2759 uint8 parts
= composite_foundation_parts
[ti
->tileh
];
2761 /* If foundations continue beyond the tile's upper sides then
2762 * mask out the last two pieces. */
2763 if (HasBit(edge_info
, 0)) ClrBit(parts
, 6);
2764 if (HasBit(edge_info
, 1)) ClrBit(parts
, 7);
2767 /* We always have to draw at least one sprite to make
2768 * sure there is a boundingbox and a sprite with the
2769 * correct offset for the childsprites. So, draw the
2770 * (completely empty) sprite of the default foundations. */
2774 StartSpriteCombine (ti
->vd
);
2775 for (int i
= 0; i
< 8; i
++) {
2776 if (HasBit(parts
, i
)) {
2777 AddSortableSpriteToDraw (ti
->vd
, image
+ i
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 7, ti
->z
);
2780 EndSpriteCombine (ti
->vd
);
2783 OffsetGroundSprite (ti
->vd
, 31, 1);
2784 ti
->z
+= ApplyPixelFoundationToSlope (FOUNDATION_LEVELED
, &ti
->tileh
);
2788 static void DrawTile_RailStation (TileInfo
*ti
)
2790 const RailtypeInfo
*rti
= GetRailTypeInfo (GetRailType (ti
->tile
));
2792 const NewGRFSpriteLayout
*layout
= NULL
;
2793 const DrawTileSprites
*t
= NULL
;
2794 BaseStation
*st
= NULL
;
2795 const StationSpec
*statspec
= NULL
;
2796 uint tile_layout
= 0;
2798 uint spec_index
= GetCustomStationSpecIndex (ti
->tile
);
2799 if (spec_index
!= 0) {
2800 /* look for customization */
2801 st
= BaseStation::GetByTile (ti
->tile
);
2802 statspec
= st
->speclist
[spec_index
].spec
;
2804 if (statspec
!= NULL
) {
2805 tile_layout
= GetStationGfx (ti
->tile
);
2807 if (HasBit(statspec
->callback_mask
, CBM_STATION_SPRITE_LAYOUT
)) {
2808 uint16 callback
= GetStationCallback (CBID_STATION_SPRITE_LAYOUT
, 0, 0, statspec
, st
, ti
->tile
);
2809 if (callback
!= CALLBACK_FAILED
) tile_layout
= (callback
& ~1) + GetRailStationAxis (ti
->tile
);
2812 /* Ensure the chosen tile layout is valid for this custom station */
2813 if (!statspec
->renderdata
.empty()) {
2814 uint i
= (tile_layout
< statspec
->renderdata
.size()) ? tile_layout
: (uint
)GetRailStationAxis(ti
->tile
);
2815 layout
= statspec
->renderdata
[i
].get();
2816 if (!layout
->NeedsPreprocessing()) {
2824 if (layout
== NULL
&& (t
== NULL
|| t
->seq
== NULL
)) {
2825 StationGfx gfx
= GetStationGfx (ti
->tile
);
2826 bool waypoint
= (GetStationType (ti
->tile
) == STATION_WAYPOINT
);
2827 t
= (waypoint
? _station_display_datas_waypoint
: _station_display_datas_rail
) + gfx
;
2830 if (ti
->tileh
!= SLOPE_FLAT
) {
2831 if (statspec
== NULL
|| !HasBit(statspec
->flags
, SSF_CUSTOM_FOUNDATIONS
)
2832 || !DrawRailStationFoundation (ti
, statspec
, st
, tile_layout
)) {
2833 DrawFoundation(ti
, FOUNDATION_LEVELED
);
2837 int32 total_offset
= rti
->GetRailtypeSpriteOffset();
2838 uint32 relocation
= 0;
2839 uint32 ground_relocation
= 0;
2841 NewGRFSpriteLayout::Result result
;
2843 const DrawTileSeqStruct
*seq
;
2844 if (layout
!= NULL
) {
2845 /* Sprite layout which needs preprocessing */
2846 bool separate_ground
= HasBit(statspec
->flags
, SSF_SEPARATE_GROUND
);
2847 uint32 var10_values
= result
.prepare (layout
, 0, total_offset
, rti
->fallback_railtype
, separate_ground
);
2849 FOR_EACH_SET_BIT(var10
, var10_values
) {
2850 uint32 var10_relocation
= GetCustomStationRelocation (statspec
, st
, ti
->tile
, var10
);
2851 result
.process (layout
, var10
, var10_relocation
, separate_ground
);
2853 ground
= result
.get_ground();
2854 seq
= result
.get_seq();
2859 if (statspec
!= NULL
) {
2860 /* Simple sprite layout */
2861 ground_relocation
= relocation
= GetCustomStationRelocation (statspec
, st
, ti
->tile
, 0);
2862 if (HasBit(statspec
->flags
, SSF_SEPARATE_GROUND
)) {
2863 ground_relocation
= GetCustomStationRelocation (statspec
, st
, ti
->tile
, 1);
2865 ground_relocation
+= rti
->fallback_railtype
;
2869 Owner owner
= GetTileOwner (ti
->tile
);
2870 PaletteID palette
= COMPANY_SPRITE_COLOUR(owner
);
2872 bool reserved
= _game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasStationReservation (ti
->tile
);
2873 SpriteID image
= ground
.sprite
;
2874 PaletteID pal
= ground
.pal
;
2875 RailTrackOffset overlay_offset
;
2876 if (rti
->UsesOverlay() && SplitGroundSpriteForOverlay (&image
, &overlay_offset
)) {
2877 AdjustGroundSpriteForOverlay (ti
, &image
);
2878 SpriteID ground
= GetCustomRailSprite (rti
, ti
->tile
, RTSG_GROUND
);
2879 DrawGroundSprite (ti
, image
, PAL_NONE
);
2880 DrawGroundSprite (ti
, ground
+ overlay_offset
, PAL_NONE
);
2883 image
= GetCustomRailSprite (rti
, ti
->tile
, RTSG_OVERLAY
) + overlay_offset
;
2886 image
+= HasBit(image
, SPRITE_MODIFIER_CUSTOM_SPRITE
) ? ground_relocation
: total_offset
;
2887 if (HasBit(pal
, SPRITE_MODIFIER_CUSTOM_SPRITE
)) pal
+= ground_relocation
;
2888 DrawGroundSprite (ti
, image
, GroundSpritePaletteTransform (image
, pal
, palette
));
2891 image
= rti
->base_sprites
.single
[GetRailStationTrack(ti
->tile
)];
2895 /* PBS debugging, draw reserved tracks darker */
2897 DrawGroundSprite (ti
, image
, PALETTE_CRASH
);
2900 if (HasRailCatenaryDrawn (rti
)) {
2901 uint gfx
= GetStationGfx (ti
->tile
);
2902 DrawRailAxisCatenary (ti
, rti
, GetRailStationAxis (ti
->tile
),
2903 /* Default stations do not draw pylons under roofs (gfx >= 4) */
2904 statspec
!= NULL
? HasBit(statspec
->pylons
, gfx
) : gfx
< 4,
2905 statspec
== NULL
|| !HasBit(statspec
->wires
, gfx
));
2908 if (IsRailWaypoint(ti
->tile
)) {
2909 /* Don't offset the waypoint graphics; they're always the same. */
2913 DrawRailTileSeq (ti
, seq
, TO_BUILDINGS
, total_offset
, relocation
, palette
);
2916 static void DrawTile_RoadStop (TileInfo
*ti
)
2918 if (ti
->tileh
!= SLOPE_FLAT
) {
2919 DrawFoundation (ti
, FOUNDATION_LEVELED
);
2922 StationGfx gfx
= GetStationGfx(ti
->tile
);
2923 bool bus
= (GetStationType (ti
->tile
) == STATION_BUS
);
2924 const DrawTileSprites
*t
= (bus
? _station_display_datas_bus
: _station_display_datas_truck
) + gfx
;
2926 Owner owner
= GetTileOwner(ti
->tile
);
2927 PaletteID palette
= COMPANY_SPRITE_COLOUR(owner
);
2929 SpriteID image
= t
->ground
.sprite
;
2930 PaletteID pal
= t
->ground
.pal
;
2931 DrawGroundSprite (ti
, image
, GroundSpritePaletteTransform (image
, pal
, palette
));
2933 RoadTypes roadtypes
= GetRoadTypes (ti
->tile
);
2934 if (HasBit(roadtypes
, ROADTYPE_TRAM
)) {
2935 Axis axis
= GetRoadStopAxis(ti
->tile
); // tram stops are always drive-through
2936 DrawGroundSprite (ti
, (HasBit(roadtypes
, ROADTYPE_ROAD
) ? SPR_TRAMWAY_OVERLAY
: SPR_TRAMWAY_TRAM
) + (axis
^ 1), PAL_NONE
);
2937 DrawRoadCatenary(ti
, axis
== AXIS_X
? ROAD_X
: ROAD_Y
);
2940 DrawOrigTileSeq (ti
, t
->seq
, TO_BUILDINGS
, palette
);
2943 static void DrawTile_OilRig (TileInfo
*ti
)
2945 if (IsTileOnWater (ti
->tile
)) {
2946 DrawWaterClassGround (ti
);
2948 DrawGroundSprite (ti
, SPR_FLAT_WATER_TILE
, PAL_NONE
);
2952 static void DrawTile_Dock (TileInfo
*ti
)
2954 StationGfx gfx
= IsBuoy (ti
->tile
) ? (int)GFX_DOCK_BUOY
: GetStationGfx (ti
->tile
);
2956 int32 total_offset
= 0;
2957 if (gfx
< DIAGDIR_END
) {
2958 TileIndex water_tile
= GetOtherDockTile (ti
->tile
);
2959 WaterClass wc
= GetWaterClass (water_tile
);
2960 if (wc
== WATER_CLASS_SEA
) {
2963 DrawClearLandTile (ti
, 3);
2965 } else if (gfx
< GFX_DOCK_BUOY
) {
2966 DrawWaterClassGround (ti
);
2968 DrawWaterClassGround(ti
);
2969 SpriteID sprite
= GetCanalSprite(CF_BUOY
, ti
->tile
);
2970 if (sprite
!= 0) total_offset
= sprite
- SPR_IMG_BUOY
;
2973 Owner owner
= GetTileOwner(ti
->tile
);
2976 if (Company::IsValidID(owner
)) {
2977 palette
= COMPANY_SPRITE_COLOUR(owner
);
2979 palette
= PALETTE_TO_GREY
;
2982 DrawRailTileSeq (ti
, _station_display_datas_dock
[gfx
], TO_BUILDINGS
,
2983 total_offset
, 0, palette
);
2986 static void DrawTile_Station (TileInfo
*ti
)
2988 switch (GetStationType (ti
->tile
)) {
2990 case STATION_WAYPOINT
:
2991 DrawTile_RailStation (ti
);
2994 case STATION_AIRPORT
:
2995 DrawTile_Airport (ti
);
2996 /* Airports cannot have bridges over them. */
3001 DrawTile_RoadStop (ti
);
3004 case STATION_OILRIG
:
3005 DrawTile_OilRig (ti
);
3013 DrawBridgeMiddle (ti
);
3016 void RailStationPickerDrawSprite (BlitArea
*dpi
, int x
, int y
, bool waypoint
, RailType railtype
, int image
)
3018 PaletteID pal
= COMPANY_SPRITE_COLOUR(_local_company
);
3019 const DrawTileSprites
*t
= (waypoint
? _station_display_datas_waypoint
: _station_display_datas_rail
) + image
;
3020 const RailtypeInfo
*rti
= GetRailTypeInfo (railtype
);
3021 int32 total_offset
= rti
->GetRailtypeSpriteOffset();
3023 SpriteID ground_spr
;
3024 PaletteID ground_pal
;
3025 if (rti
->UsesOverlay()) {
3026 DrawSprite (dpi
, SPR_FLAT_GRASS_TILE
, PAL_NONE
, x
, y
);
3027 ground_spr
= GetCustomRailSprite (rti
, INVALID_TILE
, RTSG_GROUND
);
3028 bool odd
= (image
% 2) != 0;
3029 assert (t
->ground
.sprite
== (odd
? SPR_RAIL_TRACK_Y
: SPR_RAIL_TRACK_X
));
3030 ground_spr
+= odd
? RTO_Y
: RTO_X
;
3031 ground_pal
= PAL_NONE
;
3033 SpriteID img
= t
->ground
.sprite
;
3034 ground_spr
= img
+ total_offset
;
3035 ground_pal
= HasBit(img
, PALETTE_MODIFIER_COLOUR
) ? pal
: PAL_NONE
;
3037 DrawSprite (dpi
, ground_spr
, ground_pal
, x
, y
);
3039 /* Default waypoint has no railtype specific sprites */
3040 DrawRailTileSeqInGUI (dpi
, x
, y
, t
->seq
, waypoint
? 0 : total_offset
, 0, pal
);
3043 void RoadStationPickerDrawSprite (BlitArea
*dpi
, int x
, int y
, bool bus
, bool tram
, int image
)
3045 PaletteID pal
= COMPANY_SPRITE_COLOUR(_local_company
);
3046 const DrawTileSprites
*t
= (bus
? _station_display_datas_bus
: _station_display_datas_truck
) + image
;
3048 SpriteID img
= t
->ground
.sprite
;
3049 DrawSprite (dpi
, img
, HasBit(img
, PALETTE_MODIFIER_COLOUR
) ? pal
: PAL_NONE
, x
, y
);
3052 DrawSprite (dpi
, SPR_TRAMWAY_TRAM
+ (t
->ground
.sprite
== SPR_ROAD_PAVED_STRAIGHT_X
? 1 : 0), PAL_NONE
, x
, y
);
3055 DrawOrigTileSeqInGUI (dpi
, x
, y
, t
->seq
, pal
);
3058 static int GetSlopePixelZ_Station(TileIndex tile
, uint x
, uint y
)
3060 return GetTileMaxPixelZ(tile
);
3063 static Foundation
GetFoundation_Station(TileIndex tile
, Slope tileh
)
3065 return FlatteningFoundation(tileh
);
3068 static void GetTileDesc_Station(TileIndex tile
, TileDesc
*td
)
3070 td
->owner
[0] = GetTileOwner(tile
);
3071 if (IsDriveThroughStopTile(tile
)) {
3072 Owner road_owner
= INVALID_OWNER
;
3073 Owner tram_owner
= INVALID_OWNER
;
3074 RoadTypes rts
= GetRoadTypes(tile
);
3075 if (HasBit(rts
, ROADTYPE_ROAD
)) road_owner
= GetRoadOwner(tile
, ROADTYPE_ROAD
);
3076 if (HasBit(rts
, ROADTYPE_TRAM
)) tram_owner
= GetRoadOwner(tile
, ROADTYPE_TRAM
);
3078 /* Is there a mix of owners? */
3079 if ((tram_owner
!= INVALID_OWNER
&& tram_owner
!= td
->owner
[0]) ||
3080 (road_owner
!= INVALID_OWNER
&& road_owner
!= td
->owner
[0])) {
3082 if (road_owner
!= INVALID_OWNER
) {
3083 td
->owner_type
[i
] = STR_LAND_AREA_INFORMATION_ROAD_OWNER
;
3084 td
->owner
[i
] = road_owner
;
3087 if (tram_owner
!= INVALID_OWNER
) {
3088 td
->owner_type
[i
] = STR_LAND_AREA_INFORMATION_TRAM_OWNER
;
3089 td
->owner
[i
] = tram_owner
;
3093 td
->build_date
= BaseStation::GetByTile(tile
)->build_date
;
3095 if (HasStationTileRail(tile
)) {
3096 const StationSpec
*spec
= GetStationSpec(tile
);
3099 td
->station_class
= StationClass::Get(spec
->cls_id
)->name
;
3100 td
->station_name
= spec
->name
;
3102 if (spec
->grf_prop
.grffile
!= NULL
) {
3103 const GRFConfig
*gc
= GetGRFConfig(spec
->grf_prop
.grffile
->grfid
);
3104 td
->grf
= gc
->GetName();
3108 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(tile
));
3109 td
->rail
[0].type
= rti
->strings
.name
;
3110 td
->rail
[0].speed
= rti
->max_speed
;
3113 if (IsAirport(tile
)) {
3114 const AirportSpec
*as
= Station::GetByTile(tile
)->airport
.GetSpec();
3115 td
->airport_class
= AirportClass::Get(as
->cls_id
)->name
;
3116 td
->airport_name
= as
->name
;
3118 const AirportTileSpec
*ats
= AirportTileSpec::GetByTile(tile
);
3119 td
->airport_tile_name
= ats
->name
;
3121 if (as
->grf_prop
.grffile
!= NULL
) {
3122 const GRFConfig
*gc
= GetGRFConfig(as
->grf_prop
.grffile
->grfid
);
3123 td
->grf
= gc
->GetName();
3124 } else if (ats
->grf_prop
.grffile
!= NULL
) {
3125 const GRFConfig
*gc
= GetGRFConfig(ats
->grf_prop
.grffile
->grfid
);
3126 td
->grf
= gc
->GetName();
3131 switch (GetStationType(tile
)) {
3132 default: NOT_REACHED();
3133 case STATION_RAIL
: str
= STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION
; break;
3134 case STATION_AIRPORT
:
3135 str
= (IsHangar(tile
) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR
: STR_LAI_STATION_DESCRIPTION_AIRPORT
);
3137 case STATION_TRUCK
: str
= STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA
; break;
3138 case STATION_BUS
: str
= STR_LAI_STATION_DESCRIPTION_BUS_STATION
; break;
3139 case STATION_OILRIG
: str
= STR_INDUSTRY_NAME_OIL_RIG
; break;
3140 case STATION_DOCK
: str
= STR_LAI_STATION_DESCRIPTION_SHIP_DOCK
; break;
3141 case STATION_BUOY
: str
= STR_LAI_STATION_DESCRIPTION_BUOY
; break;
3142 case STATION_WAYPOINT
: str
= STR_LAI_STATION_DESCRIPTION_WAYPOINT
; break;
3148 static TrackStatus
GetTileRailwayStatus_Station(TileIndex tile
, DiagDirection side
)
3150 if (!HasStationRail(tile
) || IsStationTileBlocked(tile
)) return 0;
3152 return CombineTrackStatus(TrackBitsToTrackdirBits(GetRailStationTrackBits(tile
)), TRACKDIR_BIT_NONE
);
3155 static TrackStatus
GetTileRoadStatus_Station(TileIndex tile
, uint sub_mode
, DiagDirection side
)
3157 if (!IsRoadStop(tile
) || (GetRoadTypes(tile
) & sub_mode
) == 0) return 0;
3159 TrackBits trackbits
;
3161 if (IsStandardRoadStopTile(tile
)) {
3162 DiagDirection dir
= GetRoadStopDir(tile
);
3164 if (side
!= INVALID_DIAGDIR
&& dir
!= side
) return 0;
3166 trackbits
= DiagDirToDiagTrackBits(dir
);
3168 Axis axis
= GetRoadStopAxis(tile
);
3170 if (side
!= INVALID_DIAGDIR
&& axis
!= DiagDirToAxis(side
)) return 0;
3172 trackbits
= AxisToTrackBits(axis
);
3175 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits
), TRACKDIR_BIT_NONE
);
3178 static TrackdirBits
GetTileWaterwayStatus_Station(TileIndex tile
, DiagDirection side
)
3180 if (!IsBuoy(tile
) && !(IsDock(tile
) && IsDockBuoy(tile
))) return TRACKDIR_BIT_NONE
;
3182 /* buoy is coded as a station, it is always on open water */
3183 TrackBits trackbits
= TRACK_BIT_ALL
;
3184 /* remove tracks that connect NE map edge */
3185 if (TileX(tile
) == 0) trackbits
&= ~(TRACK_BIT_X
| TRACK_BIT_UPPER
| TRACK_BIT_RIGHT
);
3186 /* remove tracks that connect NW map edge */
3187 if (TileY(tile
) == 0) trackbits
&= ~(TRACK_BIT_Y
| TRACK_BIT_LEFT
| TRACK_BIT_UPPER
);
3189 return TrackBitsToTrackdirBits(trackbits
);
3193 static void TileLoop_Station(TileIndex tile
)
3195 /* FIXME -- GetTileTrackStatus_Station -> animated stationtiles
3196 * hardcoded.....not good */
3197 switch (GetStationType(tile
)) {
3198 case STATION_AIRPORT
:
3199 AirportTileAnimationTrigger(Station::GetByTile(tile
), tile
, AAT_TILELOOP
);
3203 if (!IsTileFlat(tile
)) break; // only handle water part
3206 case STATION_OILRIG
: //(station part)
3208 TileLoop_Water(tile
);
3216 static void AnimateTile_Station(TileIndex tile
)
3218 if (HasStationRail(tile
)) {
3219 AnimateStationTile(tile
);
3223 if (IsAirport(tile
)) {
3224 AnimateAirportTile(tile
);
3229 static bool ClickTile_Station(TileIndex tile
)
3231 const BaseStation
*bst
= BaseStation::GetByTile(tile
);
3233 if (bst
->IsWaypoint()) {
3234 ShowWaypointWindow(Waypoint::From(bst
));
3235 } else if (IsHangar(tile
)) {
3236 ShowDepotWindow (tile
, VEH_AIRCRAFT
);
3238 ShowStationViewWindow(bst
->index
);
3244 * Run the watched cargo callback for all houses in the catchment area.
3245 * @param st Station.
3247 void TriggerWatchedCargoCallbacks(Station
*st
)
3249 /* Collect cargoes accepted since the last big tick. */
3251 for (CargoID cid
= 0; cid
< NUM_CARGO
; cid
++) {
3252 if (HasBit(st
->goods
[cid
].status
, GoodsEntry::GES_ACCEPTED_BIGTICK
)) SetBit(cargoes
, cid
);
3255 /* Anything to do? */
3256 if (cargoes
== 0) return;
3258 /* Loop over all houses in the catchment. */
3259 TileArea ta
= st
->GetCatchmentArea();
3260 TILE_AREA_LOOP(tile
, ta
) {
3261 if (IsHouseTile(tile
)) {
3262 WatchedCargoCallback(tile
, cargoes
);
3268 * This function is called for each station once every 250 ticks.
3269 * Not all stations will get the tick at the same time.
3270 * @param st the station receiving the tick.
3271 * @return true if the station is still valid (wasn't deleted)
3273 static bool StationHandleBigTick(BaseStation
*st
)
3275 if (!st
->IsInUse()) {
3276 if (++st
->delete_ctr
>= 8) delete st
;
3280 if (!st
->IsWaypoint()) {
3281 TriggerWatchedCargoCallbacks(Station::From(st
));
3283 for (CargoID i
= 0; i
< NUM_CARGO
; i
++) {
3284 ClrBit(Station::From(st
)->goods
[i
].status
, GoodsEntry::GES_ACCEPTED_BIGTICK
);
3287 UpdateStationAcceptance(Station::From(st
), true);
3293 static inline void byte_inc_sat(byte
*p
)
3300 * Truncate the cargo by a specific amount.
3301 * @param cs The type of cargo to perform the truncation for.
3302 * @param ge The goods entry, of the station, to truncate.
3303 * @param amount The amount to truncate the cargo by.
3305 static void TruncateCargo(const CargoSpec
*cs
, GoodsEntry
*ge
, uint amount
= UINT_MAX
)
3307 /* If truncating also punish the source stations' ratings to
3308 * decrease the flow of incoming cargo. */
3310 StationCargoAmountMap waiting_per_source
;
3311 ge
->cargo
.Truncate(amount
, &waiting_per_source
);
3312 for (StationCargoAmountMap::iterator
i(waiting_per_source
.begin()); i
!= waiting_per_source
.end(); ++i
) {
3313 Station
*source_station
= Station::GetIfValid(i
->first
);
3314 if (source_station
== NULL
) continue;
3316 GoodsEntry
&source_ge
= source_station
->goods
[cs
->Index()];
3317 source_ge
.max_waiting_cargo
= max(source_ge
.max_waiting_cargo
, i
->second
);
3321 static void UpdateStationRating(Station
*st
)
3323 bool waiting_changed
= false;
3325 byte_inc_sat(&st
->time_since_load
);
3326 byte_inc_sat(&st
->time_since_unload
);
3328 const CargoSpec
*cs
;
3329 FOR_ALL_CARGOSPECS(cs
) {
3330 GoodsEntry
*ge
= &st
->goods
[cs
->Index()];
3331 /* Slowly increase the rating back to his original level in the case we
3332 * didn't deliver cargo yet to this station. This happens when a bribe
3333 * failed while you didn't moved that cargo yet to a station. */
3334 if (!ge
->HasRating() && ge
->rating
< INITIAL_STATION_RATING
) {
3338 /* Only change the rating if we are moving this cargo */
3339 if (ge
->HasRating()) {
3340 byte_inc_sat(&ge
->time_since_pickup
);
3341 if (ge
->time_since_pickup
== 255 && _settings_game
.order
.selectgoods
) {
3342 ClrBit(ge
->status
, GoodsEntry::GES_RATING
);
3344 TruncateCargo(cs
, ge
);
3345 waiting_changed
= true;
3351 uint waiting
= ge
->cargo
.AvailableCount();
3353 /* num_dests is at least 1 if there is any cargo as
3354 * INVALID_STATION is also a destination.
3356 uint num_dests
= (uint
)ge
->cargo
.Packets()->MapSize();
3358 /* Average amount of cargo per next hop, but prefer solitary stations
3359 * with only one or two next hops. They are allowed to have more
3360 * cargo waiting per next hop.
3361 * With manual cargo distribution waiting_avg = waiting / 2 as then
3362 * INVALID_STATION is the only destination.
3364 uint waiting_avg
= waiting
/ (num_dests
+ 1);
3366 if (HasBit(cs
->callback_mask
, CBM_CARGO_STATION_RATING_CALC
)) {
3367 /* Perform custom station rating. If it succeeds the speed, days in transit and
3368 * waiting cargo ratings must not be executed. */
3370 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
3371 uint last_speed
= ge
->HasVehicleEverTriedLoading() ? ge
->last_speed
: 0xFF;
3373 uint32 var18
= min(ge
->time_since_pickup
, 0xFF) | (min(ge
->max_waiting_cargo
, 0xFFFF) << 8) | (min(last_speed
, 0xFF) << 24);
3374 /* Convert to the 'old' vehicle types */
3375 uint32 var10
= (st
->last_vehicle_type
== VEH_INVALID
) ? 0x0 : (st
->last_vehicle_type
+ 0x10);
3376 uint16 callback
= GetCargoCallback(CBID_CARGO_STATION_RATING_CALC
, var10
, var18
, cs
);
3377 if (callback
!= CALLBACK_FAILED
) {
3379 rating
= GB(callback
, 0, 14);
3381 /* Simulate a 15 bit signed value */
3382 if (HasBit(callback
, 14)) rating
-= 0x4000;
3387 int b
= ge
->last_speed
- 85;
3388 if (b
>= 0) rating
+= b
>> 2;
3390 byte waittime
= ge
->time_since_pickup
;
3391 if (st
->last_vehicle_type
== VEH_SHIP
) waittime
>>= 2;
3393 (rating
+= 25, waittime
> 12) ||
3394 (rating
+= 25, waittime
> 6) ||
3395 (rating
+= 45, waittime
> 3) ||
3396 (rating
+= 35, true);
3398 (rating
-= 90, ge
->max_waiting_cargo
> 1500) ||
3399 (rating
+= 55, ge
->max_waiting_cargo
> 1000) ||
3400 (rating
+= 35, ge
->max_waiting_cargo
> 600) ||
3401 (rating
+= 10, ge
->max_waiting_cargo
> 300) ||
3402 (rating
+= 20, ge
->max_waiting_cargo
> 100) ||
3403 (rating
+= 10, true);
3406 if (Company::IsValidID(st
->owner
) && HasBit(st
->town
->statues
, st
->owner
)) rating
+= 26;
3408 byte age
= ge
->last_age
;
3410 (rating
+= 10, age
>= 2) ||
3411 (rating
+= 10, age
>= 1) ||
3412 (rating
+= 13, true);
3415 int or_
= ge
->rating
; // old rating
3417 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
3418 ge
->rating
= rating
= or_
+ Clamp(Clamp(rating
, 0, 255) - or_
, -2, 2);
3420 /* if rating is <= 64 and more than 100 items waiting on average per destination,
3421 * remove some random amount of goods from the station */
3422 if (rating
<= 64 && waiting_avg
>= 100) {
3423 int dec
= Random() & 0x1F;
3424 if (waiting_avg
< 200) dec
&= 7;
3425 waiting
-= (dec
+ 1) * num_dests
;
3426 waiting_changed
= true;
3429 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
3430 if (rating
<= 127 && waiting
!= 0) {
3431 uint32 r
= Random();
3432 if (rating
<= (int)GB(r
, 0, 7)) {
3433 /* Need to have int, otherwise it will just overflow etc. */
3434 waiting
= max((int)waiting
- (int)((GB(r
, 8, 2) - 1) * num_dests
), 0);
3435 waiting_changed
= true;
3439 /* At some point we really must cap the cargo. Previously this
3440 * was a strict 4095, but now we'll have a less strict, but
3441 * increasingly aggressive truncation of the amount of cargo. */
3442 static const uint WAITING_CARGO_THRESHOLD
= 1 << 12;
3443 static const uint WAITING_CARGO_CUT_FACTOR
= 1 << 6;
3444 static const uint MAX_WAITING_CARGO
= 1 << 15;
3446 if (waiting
> WAITING_CARGO_THRESHOLD
) {
3447 uint difference
= waiting
- WAITING_CARGO_THRESHOLD
;
3448 waiting
-= (difference
/ WAITING_CARGO_CUT_FACTOR
);
3450 waiting
= min(waiting
, MAX_WAITING_CARGO
);
3451 waiting_changed
= true;
3454 /* We can't truncate cargo that's already reserved for loading.
3455 * Thus StoredCount() here. */
3456 if (waiting_changed
&& waiting
< ge
->cargo
.AvailableCount()) {
3457 /* Feed back the exact own waiting cargo at this station for the
3458 * next rating calculation. */
3459 ge
->max_waiting_cargo
= 0;
3461 TruncateCargo(cs
, ge
, ge
->cargo
.AvailableCount() - waiting
);
3463 /* If the average number per next hop is low, be more forgiving. */
3464 ge
->max_waiting_cargo
= waiting_avg
;
3470 StationID index
= st
->index
;
3471 if (waiting_changed
) {
3472 SetWindowDirty(WC_STATION_VIEW
, index
); // update whole window
3474 SetWindowWidgetDirty(WC_STATION_VIEW
, index
, WID_SV_ACCEPT_RATING_LIST
); // update only ratings list
3479 * Reroute cargo of type c at station st or in any vehicles unloading there.
3480 * Make sure the cargo's new next hop is neither "avoid" nor "avoid2".
3481 * @param st Station to be rerouted at.
3482 * @param c Type of cargo.
3483 * @param avoid Original next hop of cargo, avoid this.
3485 void RerouteCargo (Station
*st
, CargoID c
, StationID avoid
)
3487 GoodsEntry
&ge
= st
->goods
[c
];
3489 /* Reroute cargo in station. */
3490 ge
.cargo
.Reroute (avoid
, st
->index
, &ge
);
3492 /* Reroute cargo staged to be transfered. */
3493 for (std::list
<Vehicle
*>::iterator
it(st
->loading_vehicles
.begin()); it
!= st
->loading_vehicles
.end(); ++it
) {
3494 for (Vehicle
*v
= *it
; v
!= NULL
; v
= v
->Next()) {
3495 if (v
->cargo_type
!= c
) continue;
3496 v
->cargo
.Reroute (avoid
, st
->index
, &ge
);
3502 * Check if an order list contains an order for both of the given stations.
3503 * @param l The order list to check.
3504 * @param st1 The first station to look for.
3505 * @param st2 The second station to look for.
3506 * @return Whether the order list has an order for both of the stations.
3508 static bool CheckOrderListLink (const OrderList
*l
, StationID st1
,
3511 bool found1
= false;
3512 bool found2
= false;
3513 for (const Order
*order
= l
->GetFirstOrder(); order
!= NULL
; order
= order
->next
) {
3514 if (!order
->IsType(OT_GOTO_STATION
) && !order
->IsType(OT_IMPLICIT
)) continue;
3515 StationID dest
= order
->GetDestination();
3518 if (found2
) return true;
3519 } else if (dest
== st2
) {
3521 if (found1
) return true;
3528 * Check if a link is stale.
3529 * @param from Source station.
3530 * @param to Destination station.
3531 * @param edge Link to check.
3532 * @return Whether the link has been updated.
3534 static bool CheckStaleLink (StationID from
, StationID to
,
3535 const LinkGraph::Edge
*edge
)
3537 /* Have all vehicles refresh their next hops before deciding to
3538 * remove the node. */
3540 SmallVector
<Vehicle
*, 32> vehicles
;
3541 FOR_ALL_ORDER_LISTS(l
) {
3542 if (!CheckOrderListLink (l
, from
, to
)) continue;
3543 *(vehicles
.Append()) = l
->GetFirstSharedVehicle();
3546 Vehicle
**iter
= vehicles
.Begin();
3547 while (iter
!= vehicles
.End()) {
3550 LinkRefresher::Run (v
, false); // Don't allow merging. Otherwise lg might get deleted.
3551 if (edge
->LastUpdate() == _date
) return true;
3553 Vehicle
*next_shared
= v
->NextShared();
3555 *iter
= next_shared
;
3558 vehicles
.Erase (iter
);
3561 if (iter
== vehicles
.End()) iter
= vehicles
.Begin();
3568 * Check all next hops of cargo packets in this station for existance of a
3569 * a valid link they may use to travel on. Reroute any cargo not having a valid
3570 * link and remove timed out links found like this from the linkgraph. We're
3571 * not all links here as that is expensive and useless. A link no one is using
3572 * doesn't hurt either.
3573 * @param from Station to check.
3575 static void DeleteStaleLinks (Station
*from
)
3577 for (CargoID c
= 0; c
< NUM_CARGO
; ++c
) {
3578 const bool auto_distributed
= (_settings_game
.linkgraph
.GetDistributionType(c
) != DT_MANUAL
);
3579 GoodsEntry
&ge
= from
->goods
[c
];
3580 LinkGraph
*lg
= LinkGraph::GetIfValid(ge
.link_graph
);
3581 if (lg
== NULL
) continue;
3582 LinkGraph::NodeRef node
= (*lg
)[ge
.node
];
3583 for (LinkGraph::EdgeIterator
it(node
.Begin()); it
!= node
.End();) {
3584 LinkGraph::Edge
*edge
= &*it
;
3585 Station
*to
= Station::Get((*lg
)[it
.get_id()]->Station());
3586 assert(to
->goods
[c
].node
== it
.get_id());
3587 ++it
; // Do that before removing the edge. Anything else may crash.
3588 assert(_date
>= edge
->LastUpdate());
3589 uint timeout
= LinkGraph::MIN_TIMEOUT_DISTANCE
+ (DistanceManhattan(from
->xy
, to
->xy
) >> 3);
3590 if ((uint
)(_date
- edge
->LastUpdate()) > timeout
) {
3591 if (!auto_distributed
|| !CheckStaleLink (from
->index
, to
->index
, edge
)) {
3592 /* If it's still considered dead remove it. */
3593 lg
->RemoveEdge (ge
.node
, to
->goods
[c
].node
);
3594 ge
.flows
.DeleteFlows(to
->index
);
3595 RerouteCargo (from
, c
, to
->index
);
3597 } else if (edge
->LastUnrestrictedUpdate() != INVALID_DATE
&& (uint
)(_date
- edge
->LastUnrestrictedUpdate()) > timeout
) {
3599 ge
.flows
.RestrictFlows(to
->index
);
3600 RerouteCargo (from
, c
, to
->index
);
3601 } else if (edge
->LastRestrictedUpdate() != INVALID_DATE
&& (uint
)(_date
- edge
->LastRestrictedUpdate()) > timeout
) {
3605 assert(_date
>= lg
->LastCompression());
3606 if ((uint
)(_date
- lg
->LastCompression()) > LinkGraph::COMPRESSION_INTERVAL
) {
3613 * Increase capacity for a link stat given by station cargo and next hop.
3614 * @param st Station to get the link stats from.
3615 * @param cargo Cargo to increase stat for.
3616 * @param next_station_id Station the consist will be travelling to next.
3617 * @param capacity Capacity to add to link stat.
3618 * @param usage Usage to add to link stat.
3619 * @param mode Update mode to be applied.
3621 void IncreaseStats(Station
*st
, CargoID cargo
, StationID next_station_id
, uint capacity
, uint usage
, EdgeUpdateMode mode
)
3623 GoodsEntry
&ge1
= st
->goods
[cargo
];
3624 Station
*st2
= Station::Get(next_station_id
);
3625 GoodsEntry
&ge2
= st2
->goods
[cargo
];
3626 LinkGraph
*lg
= NULL
;
3627 if (ge1
.link_graph
== INVALID_LINK_GRAPH
) {
3628 if (ge2
.link_graph
== INVALID_LINK_GRAPH
) {
3629 if (LinkGraph::CanAllocateItem()) {
3630 lg
= new LinkGraph(cargo
);
3631 LinkGraphSchedule::instance
.Queue(lg
);
3632 ge2
.link_graph
= lg
->index
;
3633 ge2
.node
= lg
->AddNode(st2
);
3635 DEBUG(misc
, 0, "Can't allocate link graph");
3638 lg
= LinkGraph::Get(ge2
.link_graph
);
3641 ge1
.link_graph
= lg
->index
;
3642 ge1
.node
= lg
->AddNode(st
);
3644 } else if (ge2
.link_graph
== INVALID_LINK_GRAPH
) {
3645 lg
= LinkGraph::Get(ge1
.link_graph
);
3646 ge2
.link_graph
= lg
->index
;
3647 ge2
.node
= lg
->AddNode(st2
);
3649 lg
= LinkGraph::Get(ge1
.link_graph
);
3650 if (ge1
.link_graph
!= ge2
.link_graph
) {
3651 LinkGraph
*lg2
= LinkGraph::Get(ge2
.link_graph
);
3652 if (lg
->Size() < lg2
->Size()) {
3653 LinkGraphSchedule::instance
.Unqueue(lg
);
3654 lg2
->Merge(lg
); // Updates GoodsEntries of lg
3657 LinkGraphSchedule::instance
.Unqueue(lg2
);
3658 lg
->Merge(lg2
); // Updates GoodsEntries of lg2
3663 lg
->UpdateEdge (ge1
.node
, ge2
.node
, capacity
, usage
, mode
);
3668 * Increase capacity for all link stats associated with vehicles in the given consist.
3669 * @param st Station to get the link stats from.
3670 * @param front First vehicle in the consist.
3671 * @param next_station_id Station the consist will be travelling to next.
3673 void IncreaseStats(Station
*st
, const Vehicle
*front
, StationID next_station_id
)
3675 for (const Vehicle
*v
= front
; v
!= NULL
; v
= v
->Next()) {
3676 if (v
->refit_cap
> 0) {
3677 /* The cargo count can indeed be higher than the refit_cap if
3678 * wagons have been auto-replaced and subsequently auto-
3679 * refitted to a higher capacity. The cargo gets redistributed
3680 * among the wagons in that case.
3681 * As usage is not such an important figure anyway we just
3682 * ignore the additional cargo then.*/
3683 IncreaseStats(st
, v
->cargo_type
, next_station_id
, v
->refit_cap
,
3684 min(v
->refit_cap
, v
->cargo
.StoredCount()), EUM_INCREASE
);
3689 /* called for every station each tick */
3690 static void StationHandleSmallTick(BaseStation
*st
)
3692 if (st
->IsWaypoint() || !st
->IsInUse()) return;
3694 byte b
= st
->delete_ctr
+ 1;
3695 if (b
>= STATION_RATING_TICKS
) b
= 0;
3698 if (b
== 0) UpdateStationRating(Station::From(st
));
3701 void OnTick_Station()
3703 if (_game_mode
== GM_EDITOR
) return;
3706 FOR_ALL_BASE_STATIONS(st
) {
3707 StationHandleSmallTick(st
);
3709 /* Clean up the link graph about once a week. */
3710 if (!st
->IsWaypoint() && (_tick_counter
+ st
->index
) % STATION_LINKGRAPH_TICKS
== 0) {
3711 DeleteStaleLinks(Station::From(st
));
3714 /* Run STATION_ACCEPTANCE_TICKS = 250 tick interval trigger for station animation.
3715 * Station index is included so that triggers are not all done
3716 * at the same time. */
3717 if ((_tick_counter
+ st
->index
) % STATION_ACCEPTANCE_TICKS
== 0) {
3718 /* Stop processing this station if it was deleted */
3719 if (!StationHandleBigTick(st
)) continue;
3720 TriggerStationAnimation(st
, st
->xy
, SAT_250_TICKS
);
3721 if (!st
->IsWaypoint()) AirportAnimationTrigger(Station::From(st
), AAT_STATION_250_TICKS
);
3726 /** Monthly loop for stations. */
3727 void StationMonthlyLoop()
3731 FOR_ALL_STATIONS(st
) {
3732 for (CargoID i
= 0; i
< NUM_CARGO
; i
++) {
3733 GoodsEntry
*ge
= &st
->goods
[i
];
3734 SB(ge
->status
, GoodsEntry::GES_LAST_MONTH
, 1, GB(ge
->status
, GoodsEntry::GES_CURRENT_MONTH
, 1));
3735 ClrBit(ge
->status
, GoodsEntry::GES_CURRENT_MONTH
);
3741 void ModifyStationRatingAround(TileIndex tile
, Owner owner
, int amount
, uint radius
)
3745 FOR_ALL_STATIONS(st
) {
3746 if (st
->owner
== owner
&&
3747 DistanceManhattan(tile
, st
->xy
) <= radius
) {
3748 for (CargoID i
= 0; i
< NUM_CARGO
; i
++) {
3749 GoodsEntry
*ge
= &st
->goods
[i
];
3751 if (ge
->status
!= 0) {
3752 ge
->rating
= Clamp(ge
->rating
+ amount
, 0, 255);
3759 static uint
UpdateStationWaiting(Station
*st
, CargoID type
, uint amount
, SourceType source_type
, SourceID source_id
)
3761 /* We can't allocate a CargoPacket? Then don't do anything
3762 * at all; i.e. just discard the incoming cargo. */
3763 if (!CargoPacket::CanAllocateItem()) return 0;
3765 GoodsEntry
&ge
= st
->goods
[type
];
3766 amount
+= ge
.amount_fract
;
3767 ge
.amount_fract
= GB(amount
, 0, 8);
3770 /* No new "real" cargo item yet. */
3771 if (amount
== 0) return 0;
3773 StationID next
= ge
.GetVia(st
->index
);
3774 ge
.cargo
.Append (new CargoPacket (st
, amount
, source_type
, source_id
), next
);
3775 LinkGraph
*lg
= NULL
;
3776 if (ge
.link_graph
== INVALID_LINK_GRAPH
) {
3777 if (LinkGraph::CanAllocateItem()) {
3778 lg
= new LinkGraph(type
);
3779 LinkGraphSchedule::instance
.Queue(lg
);
3780 ge
.link_graph
= lg
->index
;
3781 ge
.node
= lg
->AddNode(st
);
3783 DEBUG(misc
, 0, "Can't allocate link graph");
3786 lg
= LinkGraph::Get(ge
.link_graph
);
3788 if (lg
!= NULL
) (*lg
)[ge
.node
]->UpdateSupply(amount
);
3790 if (!ge
.HasRating()) {
3791 InvalidateWindowData(WC_STATION_LIST
, st
->index
);
3792 SetBit(ge
.status
, GoodsEntry::GES_RATING
);
3795 TriggerStationRandomisation(st
, st
->xy
, SRT_NEW_CARGO
, type
);
3796 TriggerStationAnimation(st
, st
->xy
, SAT_NEW_CARGO
, type
);
3797 AirportAnimationTrigger(st
, AAT_STATION_NEW_CARGO
, type
);
3799 SetWindowDirty(WC_STATION_VIEW
, st
->index
);
3800 st
->MarkTilesDirty(true);
3804 static bool IsUniqueStationName(const char *name
)
3808 FOR_ALL_STATIONS(st
) {
3809 if (st
->name
!= NULL
&& strcmp(st
->name
, name
) == 0) return false;
3817 * @param tile unused
3818 * @param flags operation to perform
3819 * @param p1 station ID that is to be renamed
3821 * @param text the new name or an empty string when resetting to the default
3822 * @return the cost of this operation or an error
3824 CommandCost
CmdRenameStation(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
3826 Station
*st
= Station::GetIfValid(p1
);
3827 if (st
== NULL
) return CMD_ERROR
;
3829 CommandCost ret
= CheckOwnership(st
->owner
);
3830 if (ret
.Failed()) return ret
;
3832 bool reset
= StrEmpty(text
);
3835 if (Utf8StringLength(text
) >= MAX_LENGTH_STATION_NAME_CHARS
) return CMD_ERROR
;
3836 if (!IsUniqueStationName(text
)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE
);
3839 if (flags
& DC_EXEC
) {
3841 st
->name
= reset
? NULL
: xstrdup(text
);
3843 st
->UpdateVirtCoord();
3844 InvalidateWindowData(WC_STATION_LIST
, st
->owner
, 1);
3847 return CommandCost();
3851 * Find all stations around a rectangular producer (industry, house, headquarter, ...)
3853 * @param location The location/area of the producer
3854 * @param stations The list to store the stations in
3856 void FindStationsAroundTiles(const TileArea
&location
, StationList
*stations
)
3858 /* area to search = producer plus station catchment radius */
3859 uint max_rad
= (_settings_game
.station
.modified_catchment
? MAX_CATCHMENT
: CA_UNMODIFIED
);
3861 uint x
= TileX(location
.tile
);
3862 uint y
= TileY(location
.tile
);
3864 uint min_x
= (x
> max_rad
) ? x
- max_rad
: 0;
3865 uint max_x
= x
+ location
.w
+ max_rad
;
3866 uint min_y
= (y
> max_rad
) ? y
- max_rad
: 0;
3867 uint max_y
= y
+ location
.h
+ max_rad
;
3869 if (min_x
== 0 && _settings_game
.construction
.freeform_edges
) min_x
= 1;
3870 if (min_y
== 0 && _settings_game
.construction
.freeform_edges
) min_y
= 1;
3871 if (max_x
>= MapSizeX()) max_x
= MapSizeX() - 1;
3872 if (max_y
>= MapSizeY()) max_y
= MapSizeY() - 1;
3874 for (uint cy
= min_y
; cy
< max_y
; cy
++) {
3875 for (uint cx
= min_x
; cx
< max_x
; cx
++) {
3876 TileIndex cur_tile
= TileXY(cx
, cy
);
3877 if (!IsStationTile(cur_tile
)) continue;
3879 Station
*st
= Station::GetByTile(cur_tile
);
3880 /* st can be NULL in case of waypoints */
3881 if (st
== NULL
) continue;
3883 if (_settings_game
.station
.modified_catchment
) {
3884 int rad
= st
->GetCatchmentRadius();
3888 if (rad_x
< -rad
|| rad_x
>= rad
+ location
.w
) continue;
3889 if (rad_y
< -rad
|| rad_y
>= rad
+ location
.h
) continue;
3892 /* Insert the station in the set. This will fail if it has
3893 * already been added.
3895 stations
->Include(st
);
3901 * Run a tile loop to find stations around a tile, on demand. Cache the result for further requests
3902 * @return pointer to a StationList containing all stations found
3904 const StationList
*StationFinder::GetStations()
3906 if (this->tile
!= INVALID_TILE
) {
3907 FindStationsAroundTiles(*this, &this->stations
);
3908 this->tile
= INVALID_TILE
;
3910 return &this->stations
;
3913 uint
MoveGoodsToStation(CargoID type
, uint amount
, SourceType source_type
, SourceID source_id
, const StationList
*all_stations
)
3915 /* Return if nothing to do. Also the rounding below fails for 0. */
3916 if (amount
== 0) return 0;
3918 Station
*st1
= NULL
; // Station with best rating
3919 Station
*st2
= NULL
; // Second best station
3920 uint best_rating1
= 0; // rating of st1
3921 uint best_rating2
= 0; // rating of st2
3923 for (Station
* const *st_iter
= all_stations
->Begin(); st_iter
!= all_stations
->End(); ++st_iter
) {
3924 Station
*st
= *st_iter
;
3926 /* Is the station reserved exclusively for somebody else? */
3927 if (st
->town
->exclusive_counter
> 0 && st
->town
->exclusivity
!= st
->owner
) continue;
3929 if (st
->goods
[type
].rating
== 0) continue; // Lowest possible rating, better not to give cargo anymore
3931 if (_settings_game
.order
.selectgoods
&& !st
->goods
[type
].HasVehicleEverTriedLoading()) continue; // Selectively servicing stations, and not this one
3933 if (!st
->CanHandleCargo(type
)) continue; // passengers on truck stop or freight on bus stop
3935 /* This station can be used, add it to st1/st2 */
3936 if (st1
== NULL
|| st
->goods
[type
].rating
>= best_rating1
) {
3937 st2
= st1
; best_rating2
= best_rating1
; st1
= st
; best_rating1
= st
->goods
[type
].rating
;
3938 } else if (st2
== NULL
|| st
->goods
[type
].rating
>= best_rating2
) {
3939 st2
= st
; best_rating2
= st
->goods
[type
].rating
;
3943 /* no stations around at all? */
3944 if (st1
== NULL
) return 0;
3946 /* From now we'll calculate with fractal cargo amounts.
3947 * First determine how much cargo we really have. */
3948 amount
*= best_rating1
+ 1;
3951 /* only one station around */
3952 return UpdateStationWaiting(st1
, type
, amount
, source_type
, source_id
);
3955 /* several stations around, the best two (highest rating) are in st1 and st2 */
3956 assert(st1
!= NULL
);
3957 assert(st2
!= NULL
);
3958 assert(best_rating1
!= 0 || best_rating2
!= 0);
3960 /* Then determine the amount the worst station gets. We do it this way as the
3961 * best should get a bonus, which in this case is the rounding difference from
3962 * this calculation. In reality that will mean the bonus will be pretty low.
3963 * Nevertheless, the best station should always get the most cargo regardless
3964 * of rounding issues. */
3965 uint worst_cargo
= amount
* best_rating2
/ (best_rating1
+ best_rating2
);
3966 assert(worst_cargo
<= (amount
- worst_cargo
));
3968 /* And then send the cargo to the stations! */
3969 uint moved
= UpdateStationWaiting(st1
, type
, amount
- worst_cargo
, source_type
, source_id
);
3970 /* These two UpdateStationWaiting's can't be in the statement as then the order
3971 * of execution would be undefined and that could cause desyncs with callbacks. */
3972 return moved
+ UpdateStationWaiting(st2
, type
, worst_cargo
, source_type
, source_id
);
3975 void BuildOilRig(TileIndex tile
)
3977 if (!Station::CanAllocateItem()) {
3978 DEBUG(misc
, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile
);
3982 if (!Dock::CanAllocateItem()) {
3983 DEBUG(misc
, 0, "Can't allocate dock for oilrig at 0x%X, reverting to oilrig only", tile
);
3987 Station
*st
= new Station(tile
);
3988 st
->town
= ClosestTownFromTile(tile
);
3990 st
->string_id
= GenerateStationName(st
, tile
, STATIONNAMING_OILRIG
);
3992 assert(IsIndustryTile(tile
));
3993 DeleteAnimatedTile(tile
);
3994 MakeOilrig(tile
, st
->index
, GetWaterClass(tile
));
3996 st
->owner
= OWNER_NONE
;
3997 st
->docks
= new Dock(tile
);
3998 st
->dock_area
= TileArea(tile
, 1, 1);
3999 st
->airport
.type
= AT_OILRIG
;
4000 st
->airport
.Add(tile
);
4001 st
->facilities
= FACIL_AIRPORT
| FACIL_DOCK
;
4002 st
->build_date
= _date
;
4006 st
->UpdateVirtCoord();
4007 UpdateStationAcceptance(st
, false);
4008 st
->RecomputeIndustriesNear();
4011 void DeleteOilRig(TileIndex tile
)
4013 Station
*st
= Station::GetByTile(tile
);
4015 MakeWaterKeepingClass(tile
, OWNER_NONE
);
4019 st
->dock_area
.Clear();
4020 st
->airport
.Clear();
4021 st
->facilities
&= ~(FACIL_AIRPORT
| FACIL_DOCK
);
4022 st
->airport
.flags
= 0;
4024 st
->AfterRemoveTile(tile
);
4026 st
->UpdateVirtCoord();
4027 st
->RecomputeIndustriesNear();
4028 if (!st
->IsInUse()) delete st
;
4031 static void ChangeTileOwner_Station(TileIndex tile
, Owner old_owner
, Owner new_owner
)
4033 if (IsRoadStopTile(tile
)) {
4034 for (RoadType rt
= ROADTYPE_ROAD
; rt
< ROADTYPE_END
; rt
++) {
4035 /* Update all roadtypes, no matter if they are present */
4036 if (GetRoadOwner(tile
, rt
) == old_owner
) {
4037 if (HasTileRoadType(tile
, rt
)) {
4038 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4039 Company::Get(old_owner
)->infrastructure
.road
[rt
] -= 2;
4040 if (new_owner
!= INVALID_OWNER
) Company::Get(new_owner
)->infrastructure
.road
[rt
] += 2;
4042 SetRoadOwner(tile
, rt
, new_owner
== INVALID_OWNER
? OWNER_NONE
: new_owner
);
4047 if (!IsTileOwner(tile
, old_owner
)) return;
4049 if (new_owner
!= INVALID_OWNER
) {
4050 /* Update company infrastructure counts. Only do it here
4051 * if the new owner is valid as otherwise the clear
4052 * command will do it for us. No need to dirty windows
4053 * here, we'll redraw the whole screen anyway.*/
4054 Company
*old_company
= Company::Get(old_owner
);
4055 Company
*new_company
= Company::Get(new_owner
);
4057 /* Update counts for underlying infrastructure. */
4058 switch (GetStationType(tile
)) {
4060 case STATION_WAYPOINT
:
4061 if (!IsStationTileBlocked(tile
)) {
4062 old_company
->infrastructure
.rail
[GetRailType(tile
)]--;
4063 new_company
->infrastructure
.rail
[GetRailType(tile
)]++;
4069 /* Road stops were already handled above. */
4074 if (GetWaterClass(tile
) == WATER_CLASS_CANAL
) {
4075 old_company
->infrastructure
.water
--;
4076 new_company
->infrastructure
.water
++;
4084 /* Update station tile count. */
4085 if (!IsBuoy(tile
) && !IsAirport(tile
)) {
4086 old_company
->infrastructure
.station
--;
4087 new_company
->infrastructure
.station
++;
4090 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4091 SetTileOwner(tile
, new_owner
);
4092 InvalidateWindowClassesData(WC_STATION_LIST
, 0);
4094 if (IsDriveThroughStopTile(tile
)) {
4095 /* Remove the drive-through road stop */
4096 DoCommand(tile
, 1 | 1 << 8, (GetStationType(tile
) == STATION_TRUCK
) ? ROADSTOP_TRUCK
: ROADSTOP_BUS
, DC_EXEC
| DC_BANKRUPT
, CMD_REMOVE_ROAD_STOP
);
4097 assert(IsNormalRoadTile(tile
));
4098 /* Change owner of tile and all roadtypes */
4099 ChangeTileOwner(tile
, old_owner
, new_owner
);
4101 DoCommand(tile
, 0, 0, DC_EXEC
| DC_BANKRUPT
, CMD_LANDSCAPE_CLEAR
);
4102 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4103 * Update owner of buoy if it was not removed (was in orders).
4104 * Do not update when owned by OWNER_WATER (sea and rivers). */
4105 if ((IsWaterTile(tile
) || IsBuoyTile(tile
)) && IsTileOwner(tile
, old_owner
)) SetTileOwner(tile
, OWNER_NONE
);
4111 * Check if a drive-through road stop tile can be cleared.
4112 * Road stops built on town-owned roads check the conditions
4113 * that would allow clearing of the original road.
4114 * @param tile road stop tile to check
4115 * @param flags command flags
4116 * @return true if the road can be cleared
4118 static bool CanRemoveRoadWithStop(TileIndex tile
, DoCommandFlag flags
)
4120 /* Yeah... water can always remove stops, right? */
4121 if (_current_company
== OWNER_WATER
) return true;
4123 RoadTypes rts
= GetRoadTypes(tile
);
4124 if (HasBit(rts
, ROADTYPE_TRAM
)) {
4125 Owner tram_owner
= GetRoadOwner(tile
, ROADTYPE_TRAM
);
4126 if (tram_owner
!= OWNER_NONE
&& CheckOwnership(tram_owner
).Failed()) return false;
4128 if (HasBit(rts
, ROADTYPE_ROAD
)) {
4129 Owner road_owner
= GetRoadOwner(tile
, ROADTYPE_ROAD
);
4130 if (road_owner
!= OWNER_TOWN
) {
4131 if (road_owner
!= OWNER_NONE
&& CheckOwnership(road_owner
).Failed()) return false;
4133 if (CheckAllowRemoveRoad(tile
, GetAnyRoadBits(tile
, ROADTYPE_ROAD
), OWNER_TOWN
, ROADTYPE_ROAD
, flags
).Failed()) return false;
4141 * Clear a single tile of a station.
4142 * @param tile The tile to clear.
4143 * @param flags The DoCommand flags related to the "command".
4144 * @return The cost, or error of clearing.
4146 CommandCost
ClearTile_Station(TileIndex tile
, DoCommandFlag flags
)
4148 if (flags
& DC_AUTO
) {
4149 switch (GetStationType(tile
)) {
4151 case STATION_RAIL
: return_cmd_error(STR_ERROR_MUST_DEMOLISH_RAILROAD
);
4152 case STATION_WAYPOINT
: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED
);
4153 case STATION_AIRPORT
: return_cmd_error(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST
);
4154 case STATION_TRUCK
: return_cmd_error(HasTileRoadType(tile
, ROADTYPE_TRAM
) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST
: STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST
);
4155 case STATION_BUS
: return_cmd_error(HasTileRoadType(tile
, ROADTYPE_TRAM
) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST
: STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST
);
4156 case STATION_BUOY
: return_cmd_error(STR_ERROR_BUOY_IN_THE_WAY
);
4157 case STATION_DOCK
: return_cmd_error(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST
);
4158 case STATION_OILRIG
:
4159 SetDParam(1, STR_INDUSTRY_NAME_OIL_RIG
);
4160 return_cmd_error(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY
);
4164 switch (GetStationType(tile
)) {
4165 case STATION_RAIL
: return RemoveRailStation(tile
, flags
);
4166 case STATION_WAYPOINT
: return RemoveRailWaypoint(tile
, flags
);
4167 case STATION_AIRPORT
: return RemoveAirport(tile
, flags
);
4169 if (IsDriveThroughStopTile(tile
) && !CanRemoveRoadWithStop(tile
, flags
)) {
4170 return_cmd_error(STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST
);
4172 return RemoveRoadStop(tile
, flags
);
4174 if (IsDriveThroughStopTile(tile
) && !CanRemoveRoadWithStop(tile
, flags
)) {
4175 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST
);
4177 return RemoveRoadStop(tile
, flags
);
4178 case STATION_BUOY
: return RemoveBuoy(tile
, flags
);
4179 case STATION_DOCK
: return RemoveDock(tile
, flags
);
4186 static CommandCost
TerraformTile_Station(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
4188 if (_settings_game
.construction
.build_on_slopes
&& AutoslopeEnabled()) {
4189 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4190 * TTDP does not call it.
4192 if (GetTileMaxZ(tile
) == z_new
+ GetSlopeMaxZ(tileh_new
)) {
4193 switch (GetStationType(tile
)) {
4194 case STATION_WAYPOINT
:
4195 case STATION_RAIL
: {
4196 DiagDirection direction
= AxisToDiagDir(GetRailStationAxis(tile
));
4197 if (!AutoslopeCheckForEntranceEdge(tile
, z_new
, tileh_new
, direction
)) break;
4198 if (!AutoslopeCheckForEntranceEdge(tile
, z_new
, tileh_new
, ReverseDiagDir(direction
))) break;
4199 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
4202 case STATION_AIRPORT
:
4203 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
4207 DiagDirection direction
= GetRoadStopDir(tile
);
4208 if (!AutoslopeCheckForEntranceEdge(tile
, z_new
, tileh_new
, direction
)) break;
4209 if (IsDriveThroughStopTile(tile
)) {
4210 if (!AutoslopeCheckForEntranceEdge(tile
, z_new
, tileh_new
, ReverseDiagDir(direction
))) break;
4212 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
4219 return DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
4223 * Get flow for a station.
4224 * @param st Station to get flow for.
4225 * @return Flow for st.
4227 uint
FlowStat::GetShare(StationID st
) const
4230 for (SharesMap::const_iterator it
= this->shares
.begin(); it
!= this->shares
.end(); ++it
) {
4231 if (it
->second
== st
) {
4232 return it
->first
- prev
;
4241 * Get a station a package can be routed to, but exclude the given ones.
4242 * @param excluded StationID not to be selected.
4243 * @param excluded2 Another StationID not to be selected.
4244 * @return A station ID from the shares map.
4246 StationID
FlowStat::GetVia(StationID excluded
, StationID excluded2
) const
4248 if (this->unrestricted
== 0) return INVALID_STATION
;
4249 assert(!this->shares
.empty());
4250 SharesMap::const_iterator it
= this->shares
.upper_bound(RandomRange(this->unrestricted
));
4251 assert(it
!= this->shares
.end() && it
->first
<= this->unrestricted
);
4252 if (it
->second
!= excluded
&& it
->second
!= excluded2
) return it
->second
;
4254 /* We've hit one of the excluded stations.
4255 * Draw another share, from outside its range. */
4257 uint end
= it
->first
;
4258 uint begin
= (it
== this->shares
.begin() ? 0 : (--it
)->first
);
4259 uint interval
= end
- begin
;
4260 if (interval
>= this->unrestricted
) return INVALID_STATION
; // Only one station in the map.
4261 uint new_max
= this->unrestricted
- interval
;
4262 uint rand
= RandomRange(new_max
);
4263 SharesMap::const_iterator it2
= (rand
< begin
) ? this->shares
.upper_bound(rand
) :
4264 this->shares
.upper_bound(rand
+ interval
);
4265 assert(it2
!= this->shares
.end() && it2
->first
<= this->unrestricted
);
4266 if (it2
->second
!= excluded
&& it2
->second
!= excluded2
) return it2
->second
;
4268 /* We've hit the second excluded station.
4269 * Same as before, only a bit more complicated. */
4271 uint end2
= it2
->first
;
4272 uint begin2
= (it2
== this->shares
.begin() ? 0 : (--it2
)->first
);
4273 uint interval2
= end2
- begin2
;
4274 if (interval2
>= new_max
) return INVALID_STATION
; // Only the two excluded stations in the map.
4275 new_max
-= interval2
;
4276 if (begin
> begin2
) {
4277 Swap(begin
, begin2
);
4279 Swap(interval
, interval2
);
4281 rand
= RandomRange(new_max
);
4282 SharesMap::const_iterator it3
= this->shares
.upper_bound(this->unrestricted
);
4284 it3
= this->shares
.upper_bound(rand
);
4285 } else if (rand
< begin2
- interval
) {
4286 it3
= this->shares
.upper_bound(rand
+ interval
);
4288 it3
= this->shares
.upper_bound(rand
+ interval
+ interval2
);
4290 assert(it3
!= this->shares
.end() && it3
->first
<= this->unrestricted
);
4295 * Reduce all flows to minimum capacity so that they don't get in the way of
4296 * link usage statistics too much. Keep them around, though, to continue
4297 * routing any remaining cargo.
4299 void FlowStat::Invalidate()
4301 assert(!this->shares
.empty());
4302 SharesMap new_shares
;
4304 for (SharesMap::iterator
it(this->shares
.begin()); it
!= this->shares
.end(); ++it
) {
4305 new_shares
[++i
] = it
->second
;
4306 if (it
->first
== this->unrestricted
) this->unrestricted
= i
;
4308 this->shares
.swap(new_shares
);
4309 assert(!this->shares
.empty() && this->unrestricted
<= (--this->shares
.end())->first
);
4313 * Change share for specified station. By specifing INT_MIN as parameter you
4314 * can erase a share. Newly added flows will be unrestricted.
4315 * @param st Next Hop to be removed.
4316 * @param flow Share to be added or removed.
4318 void FlowStat::ChangeShare(StationID st
, int flow
)
4320 /* We assert only before changing as afterwards the shares can actually
4321 * be empty. In that case the whole flow stat must be deleted then. */
4322 assert(!this->shares
.empty());
4324 uint removed_shares
= 0;
4325 uint added_shares
= 0;
4326 uint last_share
= 0;
4327 SharesMap new_shares
;
4328 for (SharesMap::iterator
it(this->shares
.begin()); it
!= this->shares
.end(); ++it
) {
4329 if (it
->second
== st
) {
4331 uint share
= it
->first
- last_share
;
4332 if (flow
== INT_MIN
|| (uint
)(-flow
) >= share
) {
4333 removed_shares
+= share
;
4334 if (it
->first
<= this->unrestricted
) this->unrestricted
-= share
;
4335 if (flow
!= INT_MIN
) flow
+= share
;
4336 last_share
= it
->first
;
4337 continue; // remove the whole share
4339 removed_shares
+= (uint
)(-flow
);
4341 added_shares
+= (uint
)(flow
);
4343 if (it
->first
<= this->unrestricted
) this->unrestricted
+= flow
;
4345 /* If we don't continue above the whole flow has been added or
4349 new_shares
[it
->first
+ added_shares
- removed_shares
] = it
->second
;
4350 last_share
= it
->first
;
4353 new_shares
[last_share
+ (uint
)flow
] = st
;
4354 if (this->unrestricted
< last_share
) {
4355 this->ReleaseShare(st
);
4357 this->unrestricted
+= flow
;
4360 this->shares
.swap(new_shares
);
4364 * Restrict a flow by moving it to the end of the map and decreasing the amount
4365 * of unrestricted flow.
4366 * @param st Station of flow to be restricted.
4368 void FlowStat::RestrictShare(StationID st
)
4370 assert(!this->shares
.empty());
4372 uint last_share
= 0;
4373 SharesMap new_shares
;
4374 for (SharesMap::iterator
it(this->shares
.begin()); it
!= this->shares
.end(); ++it
) {
4376 if (it
->first
> this->unrestricted
) return; // Not present or already restricted.
4377 if (it
->second
== st
) {
4378 flow
= it
->first
- last_share
;
4379 this->unrestricted
-= flow
;
4381 new_shares
[it
->first
] = it
->second
;
4384 new_shares
[it
->first
- flow
] = it
->second
;
4386 last_share
= it
->first
;
4388 if (flow
== 0) return;
4389 new_shares
[last_share
+ flow
] = st
;
4390 this->shares
.swap(new_shares
);
4391 assert(!this->shares
.empty());
4395 * Release ("unrestrict") a flow by moving it to the begin of the map and
4396 * increasing the amount of unrestricted flow.
4397 * @param st Station of flow to be released.
4399 void FlowStat::ReleaseShare(StationID st
)
4401 assert(!this->shares
.empty());
4403 uint next_share
= 0;
4405 for (SharesMap::reverse_iterator
it(this->shares
.rbegin()); it
!= this->shares
.rend(); ++it
) {
4406 if (it
->first
< this->unrestricted
) return; // Note: not <= as the share may hit the limit.
4408 flow
= next_share
- it
->first
;
4409 this->unrestricted
+= flow
;
4412 if (it
->first
== this->unrestricted
) return; // !found -> Limit not hit.
4413 if (it
->second
== st
) found
= true;
4415 next_share
= it
->first
;
4417 if (flow
== 0) return;
4418 SharesMap new_shares
;
4419 new_shares
[flow
] = st
;
4420 for (SharesMap::iterator
it(this->shares
.begin()); it
!= this->shares
.end(); ++it
) {
4421 if (it
->second
!= st
) {
4422 new_shares
[flow
+ it
->first
] = it
->second
;
4427 this->shares
.swap(new_shares
);
4428 assert(!this->shares
.empty());
4432 * Scale all shares from link graph's runtime to monthly values.
4433 * @param runtime Time the link graph has been running without compression.
4434 * @pre runtime must be greater than 0 as we don't want infinite flow values.
4436 void FlowStat::ScaleToMonthly(uint runtime
)
4438 assert(runtime
> 0);
4439 SharesMap new_shares
;
4441 for (SharesMap::iterator i
= this->shares
.begin(); i
!= this->shares
.end(); ++i
) {
4442 share
= max(share
+ 1, i
->first
* 30 / runtime
);
4443 new_shares
[share
] = i
->second
;
4444 if (this->unrestricted
== i
->first
) this->unrestricted
= share
;
4446 this->shares
.swap(new_shares
);
4450 * Add some flow from "origin", going via "via".
4451 * @param origin Origin of the flow.
4452 * @param via Next hop.
4453 * @param flow Amount of flow to be added.
4455 void FlowStatMap::AddFlow(StationID origin
, StationID via
, uint flow
)
4457 FlowStatMap::iterator origin_it
= this->find(origin
);
4458 if (origin_it
== this->end()) {
4459 this->insert(std::make_pair(origin
, FlowStat(via
, flow
)));
4461 origin_it
->second
.ChangeShare(via
, flow
);
4462 assert(!origin_it
->second
.GetShares()->empty());
4467 * Pass on some flow, remembering it as invalid, for later subtraction from
4468 * locally consumed flow. This is necessary because we can't have negative
4469 * flows and we don't want to sort the flows before adding them up.
4470 * @param origin Origin of the flow.
4471 * @param via Next hop.
4472 * @param flow Amount of flow to be passed.
4474 void FlowStatMap::PassOnFlow(StationID origin
, StationID via
, uint flow
)
4476 FlowStatMap::iterator prev_it
= this->find(origin
);
4477 if (prev_it
== this->end()) {
4478 FlowStat
fs(via
, flow
);
4479 fs
.AppendShare(INVALID_STATION
, flow
);
4480 this->insert(std::make_pair(origin
, fs
));
4482 prev_it
->second
.ChangeShare(via
, flow
);
4483 prev_it
->second
.ChangeShare(INVALID_STATION
, flow
);
4484 assert(!prev_it
->second
.GetShares()->empty());
4489 * Subtract invalid flows from locally consumed flow.
4490 * @param self ID of own station.
4492 void FlowStatMap::FinalizeLocalConsumption(StationID self
)
4494 for (FlowStatMap::iterator i
= this->begin(); i
!= this->end(); ++i
) {
4495 FlowStat
&fs
= i
->second
;
4496 uint local
= fs
.GetShare(INVALID_STATION
);
4497 if (local
> INT_MAX
) { // make sure it fits in an int
4498 fs
.ChangeShare(self
, -INT_MAX
);
4499 fs
.ChangeShare(INVALID_STATION
, -INT_MAX
);
4502 fs
.ChangeShare(self
, -(int)local
);
4503 fs
.ChangeShare(INVALID_STATION
, -(int)local
);
4505 /* If the local share is used up there must be a share for some
4506 * remote station. */
4507 assert(!fs
.GetShares()->empty());
4512 * Delete all flows at a station for specific cargo and destination.
4513 * @param via Remote station of flows to be deleted.
4514 * @param erased Station id stack to which to append the source stations
4515 * for which the complete FlowStat, not only a share, has been erased.
4517 void FlowStatMap::DeleteFlows (StationID via
, StationIDStack
*erased
)
4519 for (FlowStatMap::iterator f_it
= this->begin(); f_it
!= this->end();) {
4520 FlowStat
&s_flows
= f_it
->second
;
4521 s_flows
.ChangeShare(via
, INT_MIN
);
4522 if (s_flows
.GetShares()->empty()) {
4523 if (erased
!= NULL
) erased
->push_back (f_it
->first
);
4524 this->erase(f_it
++);
4532 * Restrict all flows at a station for specific cargo and destination.
4533 * @param via Remote station of flows to be restricted.
4535 void FlowStatMap::RestrictFlows(StationID via
)
4537 for (FlowStatMap::iterator it
= this->begin(); it
!= this->end(); ++it
) {
4538 it
->second
.RestrictShare(via
);
4543 * Release all flows at a station for specific cargo and destination.
4544 * @param via Remote station of flows to be released.
4546 void FlowStatMap::ReleaseFlows(StationID via
)
4548 for (FlowStatMap::iterator it
= this->begin(); it
!= this->end(); ++it
) {
4549 it
->second
.ReleaseShare(via
);
4554 * Get the sum of all flows from this FlowStatMap.
4555 * @return sum of all flows.
4557 uint
FlowStatMap::GetFlow() const
4560 for (FlowStatMap::const_iterator i
= this->begin(); i
!= this->end(); ++i
) {
4561 ret
+= (--(i
->second
.GetShares()->end()))->first
;
4567 * Get the sum of flows via a specific station from this FlowStatMap.
4568 * @param via Remote station to look for.
4569 * @return all flows for 'via' added up.
4571 uint
FlowStatMap::GetFlowVia(StationID via
) const
4574 for (FlowStatMap::const_iterator i
= this->begin(); i
!= this->end(); ++i
) {
4575 ret
+= i
->second
.GetShare(via
);
4581 * Get the sum of flows from a specific station from this FlowStatMap.
4582 * @param from Origin station to look for.
4583 * @return all flows from 'from' added up.
4585 uint
FlowStatMap::GetFlowFrom(StationID from
) const
4587 FlowStatMap::const_iterator i
= this->find(from
);
4588 if (i
== this->end()) return 0;
4589 return (--(i
->second
.GetShares()->end()))->first
;
4593 * Get the flow from a specific station via a specific other station.
4594 * @param from Origin station to look for.
4595 * @param via Remote station to look for.
4596 * @return flow share originating at 'from' and going to 'via'.
4598 uint
FlowStatMap::GetFlowFromVia(StationID from
, StationID via
) const
4600 FlowStatMap::const_iterator i
= this->find(from
);
4601 if (i
== this->end()) return 0;
4602 return i
->second
.GetShare(via
);
4605 extern const TileTypeProcs _tile_type_station_procs
= {
4606 DrawTile_Station
, // draw_tile_proc
4607 GetSlopePixelZ_Station
, // get_slope_z_proc
4608 ClearTile_Station
, // clear_tile_proc
4609 NULL
, // add_accepted_cargo_proc
4610 GetTileDesc_Station
, // get_tile_desc_proc
4611 GetTileRailwayStatus_Station
, // get_tile_railway_status_proc
4612 GetTileRoadStatus_Station
, // get_tile_road_status_proc
4613 GetTileWaterwayStatus_Station
, // get_tile_waterway_status_proc
4614 ClickTile_Station
, // click_tile_proc
4615 AnimateTile_Station
, // animate_tile_proc
4616 TileLoop_Station
, // tile_loop_proc
4617 ChangeTileOwner_Station
, // change_tile_owner_proc
4618 NULL
, // add_produced_cargo_proc
4619 GetFoundation_Station
, // get_foundation_proc
4620 TerraformTile_Station
, // terraform_tile_proc