OpenTTD Source 20260311-master-g511d3794ce
station_cmd.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "core/flatset_type.hpp"
12#include "aircraft.h"
13#include "bridge_map.h"
14#include "vehiclelist_func.h"
15#include "viewport_func.h"
16#include "viewport_kdtree.h"
17#include "command_func.h"
18#include "town.h"
19#include "news_func.h"
20#include "train.h"
21#include "ship.h"
22#include "roadveh.h"
23#include "industry.h"
24#include "newgrf_cargo.h"
25#include "newgrf_debug.h"
26#include "newgrf_station.h"
27#include "newgrf_canal.h" /* For the buoy */
29#include "road_internal.h" /* For drawing catenary/checking road removal */
30#include "autoslope.h"
31#include "water.h"
32#include "tilehighlight_func.h"
33#include "strings_func.h"
34#include "clear_func.h"
36#include "vehicle_func.h"
37#include "string_func.h"
38#include "animated_tile_func.h"
39#include "elrail_func.h"
40#include "station_base.h"
41#include "station_func.h"
42#include "station_kdtree.h"
43#include "roadstop_base.h"
44#include "newgrf_railtype.h"
45#include "newgrf_roadtype.h"
46#include "waypoint_base.h"
47#include "waypoint_func.h"
48#include "pbs.h"
49#include "debug.h"
50#include "core/random_func.hpp"
52#include "company_base.h"
54#include "newgrf_airporttiles.h"
55#include "order_backup.h"
56#include "newgrf_house.h"
57#include "company_gui.h"
59#include "linkgraph/refresh.h"
60#include "tunnelbridge_map.h"
61#include "station_cmd.h"
62#include "waypoint_cmd.h"
63#include "landscape_cmd.h"
64#include "rail_cmd.h"
65#include "newgrf_roadstop.h"
66#include "timer/timer.h"
70#include "cheat_type.h"
71#include "road_func.h"
72#include "station_layout_type.h"
73
75#include "widgets/misc_widget.h"
76
77#include "table/strings.h"
78#include "table/station_land.h"
79
80#include <bitset>
81
82#include "safeguards.h"
83
89/* static */ const FlowStat::SharesMap FlowStat::empty_sharesmap;
90
98{
99 assert(IsTileType(t, TileType::Station));
100
101 /* If the tile isn't an airport there's no chance it's a hangar. */
102 if (!IsAirport(t)) return false;
103
104 const Station *st = Station::GetByTile(t);
105 const AirportSpec *as = st->airport.GetSpec();
106
107 for (const auto &depot : as->depots) {
108 if (st->airport.GetRotatedTileFromOffset(depot.ti) == TileIndex(t)) return true;
109 }
110
111 return false;
112}
113
123template <class T, class F>
124CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
125{
126 ta.Expand(1);
127
128 /* check around to see if there are any stations there owned by the company */
129 for (TileIndex tile_cur : ta) {
130 if (IsTileType(tile_cur, TileType::Station)) {
131 StationID t = GetStationIndex(tile_cur);
132 if (!T::IsValidID(t) || T::Get(t)->owner != company || !filter(T::Get(t))) continue;
133 if (closest_station == StationID::Invalid()) {
134 closest_station = t;
135 } else if (closest_station != t) {
136 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
137 }
138 }
139 }
140 *st = (closest_station == StationID::Invalid()) ? nullptr : T::Get(closest_station);
141 return CommandCost();
142}
143
149typedef bool (*CMSAMatcher)(TileIndex tile);
150
158{
159 int num = 0;
160
161 for (int dx = -3; dx <= 3; dx++) {
162 for (int dy = -3; dy <= 3; dy++) {
163 TileIndex t = TileAddWrap(tile, dx, dy);
164 if (t != INVALID_TILE && cmp(t)) num++;
165 }
166 }
167
168 return num;
169}
170
176static bool CMSAMine(TileIndex tile)
177{
178 /* No industry */
179 if (!IsTileType(tile, TileType::Industry)) return false;
180
181 const Industry *ind = Industry::GetByTile(tile);
182
183 /* No extractive industry */
185
186 for (const auto &p : ind->produced) {
187 /* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine.
188 * Also the production of passengers and mail is ignored. */
189 if (IsValidCargoType(p.cargo) &&
190 !CargoSpec::Get(p.cargo)->classes.Any({CargoClass::Liquid, CargoClass::Passengers, CargoClass::Mail})) {
191 return true;
192 }
193 }
194
195 return false;
196}
197
203static bool CMSAWater(TileIndex tile)
204{
205 return IsTileType(tile, TileType::Water) && IsWater(tile);
206}
207
213static bool CMSATree(TileIndex tile)
214{
215 return IsTileType(tile, TileType::Trees);
216}
217
227
230 std::bitset<STR_SV_STNAME_FALLBACK - STR_SV_STNAME> used_names;
231 std::bitset<NUM_INDUSTRYTYPES> indtypes;
232
238 bool IsAvailable(StringID str) const
239 {
240 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
241 return !this->used_names.test(str - STR_SV_STNAME);
242 }
243
249 {
250 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
251 this->used_names.set(str - STR_SV_STNAME);
252 }
253};
254
264{
265 const Town *t = st->town;
266
268
269 for (const Station *s : Station::Iterate()) {
270 if (s != st && s->town == t) {
271 if (s->indtype != IT_INVALID) {
272 sni.indtypes[s->indtype] = true;
273 StringID name = GetIndustrySpec(s->indtype)->station_name;
274 if (name != STR_UNDEFINED) {
275 /* Filter for other industrytypes with the same name */
276 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
277 const IndustrySpec *indsp = GetIndustrySpec(it);
278 if (indsp->enabled && indsp->station_name == name) sni.indtypes[it] = true;
279 }
280 }
281 continue;
282 }
283 if (IsInsideMM(s->string_id, STR_SV_STNAME, STR_SV_STNAME_FALLBACK)) {
284 auto str = s->string_id;
285 if (str == STR_SV_STNAME_FOREST) str = STR_SV_STNAME_WOODS;
286 sni.SetUsed(str);
287 }
288 }
289 }
290
291 for (auto indtile : SpiralTileSequence(tile, 7)) {
292 if (!IsTileType(indtile, TileType::Industry)) continue;
293
294 /* If the station name is undefined it means that it doesn't name a station */
295 IndustryType indtype = GetIndustryType(indtile);
296 const IndustrySpec *indsp = GetIndustrySpec(indtype);
297 if (indsp->station_name == STR_UNDEFINED) continue;
298
299 /* In all cases if an industry that provides a name is found two of
300 * the standard names will be disabled. */
301 sni.SetUsed(STR_SV_STNAME_OILFIELD);
302 sni.SetUsed(STR_SV_STNAME_MINES);
303 if (sni.indtypes[indtype]) continue;
304
305 /* STR_NULL means it only disables oil rig/mines */
306 if (indsp->station_name != STR_NULL) {
307 st->indtype = indtype;
308 return STR_SV_STNAME_FALLBACK;
309 }
310 break;
311 }
312
313 /* check default names
314 * Oil rigs/mines name could be marked not free by looking for a near by industry. */
315 switch (name_class) {
317 if (sni.IsAvailable(STR_SV_STNAME_AIRPORT)) return STR_SV_STNAME_AIRPORT;
318 break;
320 if (sni.IsAvailable(STR_SV_STNAME_OILFIELD)) return STR_SV_STNAME_OILFIELD;
321 break;
323 if (sni.IsAvailable(STR_SV_STNAME_DOCKS)) return STR_SV_STNAME_DOCKS;
324 break;
326 if (sni.IsAvailable(STR_SV_STNAME_HELIPORT)) return STR_SV_STNAME_HELIPORT;
327 break;
328 default:
329 break;
330 };
331
332 /* check mine? */
333 if (sni.IsAvailable(STR_SV_STNAME_MINES)) {
334 if (CountMapSquareAround(tile, CMSAMine) >= 2) {
335 return STR_SV_STNAME_MINES;
336 }
337 }
338
339 /* check close enough to town to get central as name? */
340 if (DistanceMax(tile, t->xy) < 8) {
341 if (sni.IsAvailable(STR_SV_STNAME)) return STR_SV_STNAME;
342 if (sni.IsAvailable(STR_SV_STNAME_CENTRAL)) return STR_SV_STNAME_CENTRAL;
343 }
344
345 /* Check lakeside */
346 if (sni.IsAvailable(STR_SV_STNAME_LAKESIDE) &&
347 DistanceFromEdge(tile) < 20 &&
348 CountMapSquareAround(tile, CMSAWater) >= 5) {
349 return STR_SV_STNAME_LAKESIDE;
350 }
351
352 /* Check woods */
353 if (sni.IsAvailable(STR_SV_STNAME_WOODS) && (
354 CountMapSquareAround(tile, CMSATree) >= 8 ||
356 ) {
357 return _settings_game.game_creation.landscape == LandscapeType::Tropic ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
358 }
359
360 /* check elevation compared to town */
361 int z = GetTileZ(tile);
362 int z2 = GetTileZ(t->xy);
363 if (z < z2) {
364 if (sni.IsAvailable(STR_SV_STNAME_VALLEY)) return STR_SV_STNAME_VALLEY;
365 } else if (z > z2) {
366 if (sni.IsAvailable(STR_SV_STNAME_HEIGHTS)) return STR_SV_STNAME_HEIGHTS;
367 }
368
369 /* check direction compared to town */
370 if (TileX(tile) < TileX(t->xy)) {
371 sni.SetUsed(STR_SV_STNAME_SOUTH);
372 sni.SetUsed(STR_SV_STNAME_WEST);
373 } else {
374 sni.SetUsed(STR_SV_STNAME_NORTH);
375 sni.SetUsed(STR_SV_STNAME_EAST);
376 }
377 if (TileY(tile) < TileY(t->xy)) {
378 sni.SetUsed(STR_SV_STNAME_SOUTH);
379 sni.SetUsed(STR_SV_STNAME_EAST);
380 } else {
381 sni.SetUsed(STR_SV_STNAME_NORTH);
382 sni.SetUsed(STR_SV_STNAME_WEST);
383 }
384
386 static const StringID fallback_names[] = {
387 STR_SV_STNAME_NORTH,
388 STR_SV_STNAME_SOUTH,
389 STR_SV_STNAME_EAST,
390 STR_SV_STNAME_WEST,
391 STR_SV_STNAME_TRANSFER,
392 STR_SV_STNAME_HALT,
393 STR_SV_STNAME_EXCHANGE,
394 STR_SV_STNAME_ANNEXE,
395 STR_SV_STNAME_SIDINGS,
396 STR_SV_STNAME_BRANCH,
397 STR_SV_STNAME_UPPER,
398 STR_SV_STNAME_LOWER,
399 };
400 for (auto str : fallback_names) {
401 if (sni.IsAvailable(str)) return str;
402 }
403 return STR_SV_STNAME_FALLBACK;
404}
405
412{
413 uint threshold = 8;
414
415 Station *best_station = nullptr;
416 ForAllStationsRadius(tile, threshold, [&](Station *st) {
417 if (!st->IsInUse() && st->owner == _current_company) {
418 uint cur_dist = DistanceManhattan(tile, st->xy);
419
420 if (cur_dist < threshold) {
421 threshold = cur_dist;
422 best_station = st;
423 } else if (cur_dist == threshold && best_station != nullptr) {
424 /* In case of a tie, lowest station ID wins */
425 if (st->index < best_station->index) best_station = st;
426 }
427 }
428 });
429
430 return best_station;
431}
432
433
435{
436 switch (type) {
437 case StationType::Rail: return this->train_station;
438 case StationType::Airport: return this->airport;
439 case StationType::Truck: return this->truck_station;
440 case StationType::Bus: return this->bus_station;
442 case StationType::Oilrig: return this->docking_station;
443 default: NOT_REACHED();
444 }
445}
446
451{
452 Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
453
454 pt.y -= 32 * ZOOM_BASE;
455 if (this->facilities.Test(StationFacility::Airport) && this->airport.type == AT_OILRIG) pt.y -= 16 * ZOOM_BASE;
456
457 if (this->sign.kdtree_valid) _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeStation(this->index));
458
459 this->sign.UpdatePosition(pt.x, pt.y, GetString(STR_VIEWPORT_STATION, this->index, this->facilities), GetString(STR_STATION_NAME, this->index, this->facilities));
460
461 _viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeStation(this->index));
462
464}
465
471{
472 if (this->xy == new_xy) return;
473
474 _station_kdtree.Remove(this->index);
475
476 this->BaseStation::MoveSign(new_xy);
477
478 _station_kdtree.Insert(this->index);
479}
480
483{
484 for (BaseStation *st : BaseStation::Iterate()) {
485 st->UpdateVirtCoord();
486 }
487}
488
489void BaseStation::FillCachedName() const
490{
491 auto tmp_params = MakeParameters(this->index);
492 this->cached_name = GetStringWithArgs(Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params);
493}
494
495void ClearAllStationCachedNames()
496{
497 for (BaseStation *st : BaseStation::Iterate()) {
498 st->cached_name.clear();
499 }
500}
501
507CargoTypes GetAcceptanceMask(const Station *st)
508{
509 CargoTypes mask = 0;
510
511 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
512 if (it->status.Test(GoodsEntry::State::Acceptance)) SetBit(mask, std::distance(std::begin(st->goods), it));
513 }
514 return mask;
515}
516
522CargoTypes GetEmptyMask(const Station *st)
523{
524 CargoTypes mask = 0;
525
526 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
527 if (it->TotalCount() == 0) SetBit(mask, std::distance(std::begin(st->goods), it));
528 }
529 return mask;
530}
531
538static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
539{
540 StringID msg = reject ? STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_LIST : STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST;
542}
543
552CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
553{
554 CargoArray produced{};
555 FlatSet<IndustryID> industries;
556 TileArea ta = TileArea(north_tile, w, h).Expand(rad);
557
558 /* Loop over all tiles to get the produced cargo of
559 * everything except industries */
560 for (TileIndex tile : ta) {
561 if (IsTileType(tile, TileType::Industry)) industries.insert(GetIndustryIndex(tile));
562 AddProducedCargo(tile, produced);
563 }
564
565 /* Loop over the seen industries. They produce cargo for
566 * anything that is within 'rad' of any one of their tiles.
567 */
568 for (IndustryID industry : industries) {
569 const Industry *i = Industry::Get(industry);
570 /* Skip industry with neutral station */
571 if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) continue;
572
573 for (const auto &p : i->produced) {
574 if (IsValidCargoType(p.cargo)) produced[p.cargo]++;
575 }
576 }
577
578 return produced;
579}
580
589std::pair<CargoArray, CargoTypes> GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad)
590{
591 CargoArray acceptance{};
592 CargoTypes always_accepted{};
593
594 TileArea ta = TileArea(center_tile, w, h).Expand(rad);
595
596 for (TileIndex tile : ta) {
597 /* Ignore industry if it has a neutral station. */
598 if (!_settings_game.station.serve_neutral_industries && IsTileType(tile, TileType::Industry) && Industry::GetByTile(tile)->neutral_station != nullptr) continue;
599
600 AddAcceptedCargo(tile, acceptance, always_accepted);
601 }
602
603 return {acceptance, always_accepted};
604}
605
611static std::pair<CargoArray, CargoTypes> GetAcceptanceAroundStation(const Station *st)
612{
613 CargoArray acceptance{};
614 CargoTypes always_accepted{};
615
617 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
618 AddAcceptedCargo(tile, acceptance, always_accepted);
619 }
620
621 return {acceptance, always_accepted};
622}
623
629void UpdateStationAcceptance(Station *st, bool show_msg)
630{
631 /* old accepted goods types */
632 CargoTypes old_acc = GetAcceptanceMask(st);
633
634 /* And retrieve the acceptance. */
635 CargoArray acceptance{};
636 if (!st->rect.IsEmpty()) {
637 std::tie(acceptance, st->always_accepted) = GetAcceptanceAroundStation(st);
638 }
639
640 /* Adjust in case our station only accepts fewer kinds of goods */
641 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
642 uint amt = acceptance[cargo];
643
644 /* Make sure the station can accept the goods type. */
645 bool is_passengers = IsCargoInClass(cargo, CargoClass::Passengers);
646 if ((!is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::TruckStop, StationFacility::Airport, StationFacility::Dock})) ||
647 (is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::BusStop, StationFacility::Airport, StationFacility::Dock}))) {
648 amt = 0;
649 }
650
651 GoodsEntry &ge = st->goods[cargo];
654 (*LinkGraph::Get(ge.link_graph))[ge.node].SetDemand(amt / 8);
655 }
656 }
657
658 /* Only show a message in case the acceptance was actually changed. */
659 CargoTypes new_acc = GetAcceptanceMask(st);
660 if (old_acc == new_acc) return;
661
662 /* show a message to report that the acceptance was changed? */
663 if (show_msg && st->owner == _local_company && st->IsInUse()) {
664 /* Combine old and new masks to get changes */
665 CargoTypes accepts = new_acc & ~old_acc;
666 CargoTypes rejects = ~new_acc & old_acc;
667
668 /* Show news message if there are any changes */
669 if (accepts != 0) ShowRejectOrAcceptNews(st, accepts, false);
670 if (rejects != 0) ShowRejectOrAcceptNews(st, rejects, true);
671 }
672
673 /* redraw the station view since acceptance changed */
675}
676
677static void UpdateStationSignCoord(BaseStation *st)
678{
679 const StationRect *r = &st->rect;
680
681 if (r->IsEmpty()) return; // no tiles belong to this station
682
683 /* clamp sign coord to be inside the station rect */
684 TileIndex new_xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
685 st->MoveSign(new_xy);
686
687 if (!Station::IsExpected(st)) return;
688 Station *full_station = Station::From(st);
689 for (const GoodsEntry &ge : full_station->goods) {
690 LinkGraphID lg = ge.link_graph;
691 if (!LinkGraph::IsValidID(lg)) continue;
692 (*LinkGraph::Get(lg))[ge.node].UpdateLocation(st->xy);
693 }
694}
695
705static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
706{
707 /* Find a deleted station close to us */
708 if (*st == nullptr && reuse) *st = GetClosestDeletedStation(area.tile);
709
710 if (*st != nullptr) {
711 if ((*st)->owner != _current_company) {
712 return CommandCost(CMD_ERROR);
713 }
714
715 CommandCost ret = (*st)->rect.BeforeAddRect(area.tile, area.w, area.h, StationRect::ADD_TEST);
716 if (ret.Failed()) return ret;
717 } else {
718 /* allocate and initialize new station */
719 if (!Station::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_STATIONS_LOADING);
720
721 if (flags.Test(DoCommandFlag::Execute)) {
722 *st = Station::Create(area.tile);
723 _station_kdtree.Insert((*st)->index);
724
725 (*st)->town = ClosestTownFromTile(area.tile, UINT_MAX);
726 (*st)->string_id = GenerateStationName(*st, area.tile, name_class);
727
729 (*st)->town->have_ratings.Set(_current_company);
730 }
731 }
732 }
733 return CommandCost();
734}
735
743{
744 if (!st->IsInUse()) {
745 st->delete_ctr = 0;
747 }
748 /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
749 UpdateStationSignCoord(st);
750}
751
758{
759 this->UpdateVirtCoord();
761
762 if (adding) {
763 this->RecomputeCatchment();
764 MarkCatchmentTilesDirty();
766 } else {
767 MarkCatchmentTilesDirty();
768 }
769
770 switch (type) {
773 break;
775 break;
777 case StationType::Bus:
779 break;
782 break;
783 default: NOT_REACHED();
784 }
785
786 if (adding) {
787 UpdateStationAcceptance(this, false);
789 } else {
791 this->RecomputeCatchment();
792 }
793
794}
795
796CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags);
797
807CommandCost CheckBuildableTile(TileIndex tile, DiagDirections invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge = true)
808{
809 if (check_bridge && IsBridgeAbove(tile)) {
810 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
811 }
812
814 if (ret.Failed()) return ret;
815
816 auto [tileh, z] = GetTileSlopeZ(tile);
817
818 /* Prohibit building if
819 * 1) The tile is "steep" (i.e. stretches two height levels).
820 * 2) The tile is non-flat and the build_on_slopes switch is disabled.
821 */
822 if ((!allow_steep && IsSteepSlope(tileh)) ||
823 ((!_settings_game.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
824 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
825 }
826
828 int flat_z = z + GetSlopeMaxZ(tileh);
829 if (tileh != SLOPE_FLAT) {
830 /* Forbid building if the tile faces a slope in a invalid direction. */
831 for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) {
832 if (invalid_dirs.Test(dir) && !CanBuildDepotByTileh(dir, tileh)) {
833 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
834 }
835 }
837 }
838
839 /* The level of this tile must be equal to allowed_z. */
840 if (allowed_z < 0) {
841 /* First tile. */
842 allowed_z = flat_z;
843 } else if (allowed_z != flat_z) {
844 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
845 }
846
847 return cost;
848}
849
856static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCommandFlags flags)
857{
859 int allowed_z = -1;
860
861 for (; tile_iter != INVALID_TILE; ++tile_iter) {
862 CommandCost ret = CheckBuildableTile(tile_iter, {}, allowed_z, true);
863 if (ret.Failed()) return ret;
864 cost.AddCost(ret.GetCost());
865
866 ret = Command<Commands::LandscapeClear>::Do(flags, tile_iter);
867 if (ret.Failed()) return ret;
868 cost.AddCost(ret.GetCost());
869 }
870
871 return cost;
872}
873
880{
881 static constexpr std::array<StringID, to_underlying(StationType::End)> too_low_msgs = {
882 STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION, // Rail
883 INVALID_STRING_ID, // Airport
884 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Truck
885 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Bus
886 INVALID_STRING_ID, // Oilrig
887 STR_ERROR_BRIDGE_TOO_LOW_FOR_DOCK, // Dock
888 STR_ERROR_BRIDGE_TOO_LOW_FOR_BUOY, // Buoy
889 STR_ERROR_BRIDGE_TOO_LOW_FOR_RAIL_WAYPOINT, // RailWaypoint
890 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROAD_WAYPOINT, // RoadWaypoint
891 };
892 return too_low_msgs[to_underlying(type)];
893};
894
905static CommandCost IsStationBridgeAboveOk(TileIndex tile, std::span<const BridgeableTileInfo> bridgeable_info, StationType type, StationGfx layout, int bridge_height, StringID disallowed_msg = INVALID_STRING_ID)
906{
907 int height = layout < std::size(bridgeable_info) ? bridgeable_info[layout].height : 0;
908
909 if (height == 0) {
910 if (disallowed_msg != INVALID_STRING_ID) return CommandCost{disallowed_msg};
911 /* Get normal error message associated with clearing the tile. */
912 return Command<Commands::LandscapeClear>::Do(DoCommandFlag::Auto, tile);
913 }
914 if (GetTileMaxZ(tile) + height > bridge_height) {
915 int height_diff = (GetTileMaxZ(tile) + height - bridge_height) * TILE_HEIGHT_STEP;
917 }
918
919 return CommandCost{};
920}
921
927static std::span<const BridgeableTileInfo> GetStationBridgeableTileInfo(StationType type)
928{
929 return _station_bridgeable_info[to_underlying(type)];
930}
931
941{
942 if (!IsBridgeAbove(tile)) return CommandCost();
943
944 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
945 auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info;
946 return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
947}
948
958{
959 if (!IsBridgeAbove(tile)) return CommandCost();
960
961 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
962 auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info;
963 return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
964}
965
973{
974 if (!IsBridgeAbove(tile)) return CommandCost();
975
976 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
977 auto bridgeable_info = GetStationBridgeableTileInfo(StationType::Dock);
978 return IsStationBridgeAboveOk(tile, bridgeable_info, StationType::Dock, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
979}
980
987{
988 if (!IsBridgeAbove(tile)) return CommandCost();
989
990 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
991 auto bridgeable_info = GetStationBridgeableTileInfo(StationType::Buoy);
992 return IsStationBridgeAboveOk(tile, bridgeable_info, StationType::Buoy, 0, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
993}
994
1011static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_tile, int &allowed_z, DoCommandFlags flags, Axis axis, StationID *station, RailType rt, std::vector<Train *> &affected_vehicles, StationClassID spec_class, uint16_t spec_index, uint8_t plat_len, uint8_t numtracks)
1012{
1014 DiagDirections invalid_dirs = AxisToDiagDirs(axis);
1015
1016 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1017 bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
1018
1019 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, false);
1020 if (ret.Failed()) return ret;
1021 cost.AddCost(ret.GetCost());
1022
1023 if (slope_cb) {
1024 /* Do slope check if requested. */
1025 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
1026 if (ret.Failed()) return ret;
1027 }
1028
1029 /* if station is set, then we have special handling to allow building on top of already existing stations.
1030 * so station points to StationID::Invalid() if we can build on any station.
1031 * Or it points to a station if we're only allowed to build on exactly that station. */
1032 if (station != nullptr && IsTileType(tile_cur, TileType::Station)) {
1033 if (!IsRailStation(tile_cur)) {
1034 return ClearTile_Station(tile_cur, DoCommandFlag::Auto); // get error message
1035 } else {
1036 StationID st = GetStationIndex(tile_cur);
1037 if (*station == StationID::Invalid()) {
1038 *station = st;
1039 } else if (*station != st) {
1040 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1041 }
1042 }
1043 } else {
1044 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
1045 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
1046 /* Don't overbuild signals. */
1047 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
1048
1049 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
1050 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
1051 TrackBits tracks = GetTrackBits(tile_cur);
1052 Track track = RemoveFirstTrack(&tracks);
1053 Track expected_track = invalid_dirs.Test(DIAGDIR_NE) ? TRACK_X : TRACK_Y;
1054
1055 /* The existing track must align with the desired station axis. */
1056 if (tracks == TRACK_BIT_NONE && track == expected_track) {
1057 /* Check for trains having a reservation for this tile. */
1058 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
1059 Train *v = GetTrainForReservation(tile_cur, track);
1060 if (v != nullptr) {
1061 affected_vehicles.push_back(v);
1062 }
1063 }
1064 ret = Command<Commands::RemoveRail>::Do(flags, tile_cur, track);
1065 if (ret.Failed()) return ret;
1066 cost.AddCost(ret.GetCost());
1067 /* With DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) CmdLandscapeClear would fail since the rail still exists */
1068 return cost;
1069 }
1070 }
1071 }
1072 ret = Command<Commands::LandscapeClear>::Do(flags, tile_cur);
1073 if (ret.Failed()) return ret;
1074 cost.AddCost(ret.GetCost());
1075 }
1076
1077 return cost;
1078}
1079
1094static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, const RoadStopSpec *spec, DoCommandFlags flags, DiagDirections invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt)
1095{
1097
1098 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, false);
1099 if (ret.Failed()) return ret;
1100 cost.AddCost(ret.GetCost());
1101
1102 ret = IsRoadStationBridgeAboveOk(cur_tile, spec, station_type, is_drive_through ? GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + axis : FindFirstBit(invalid_dirs.base()));
1103 if (ret.Failed()) return ret;
1104
1105 /* If station is set, then we have special handling to allow building on top of already existing stations.
1106 * Station points to StationID::Invalid() if we can build on any station.
1107 * Or it points to a station if we're only allowed to build on exactly that station. */
1108 if (station != nullptr && IsTileType(cur_tile, TileType::Station)) {
1109 if (!IsAnyRoadStop(cur_tile)) {
1110 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1111 } else {
1112 if (station_type != GetStationType(cur_tile) ||
1113 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
1114 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1115 }
1116 /* Drive-through station in the wrong direction. */
1117 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
1118 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1119 }
1120 StationID st = GetStationIndex(cur_tile);
1121 if (*station == StationID::Invalid()) {
1122 *station = st;
1123 } else if (*station != st) {
1124 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1125 }
1126 }
1127 } else {
1128 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
1129 /* Road bits in the wrong direction. */
1130 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
1131 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
1132 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1133 switch (CountBits(rb)) {
1134 case 1:
1135 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1136
1137 case 2:
1138 if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1139 return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
1140
1141 default: // 3 or 4
1142 return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
1143 }
1144 }
1145
1146 if (build_over_road) {
1147 /* There is a road, check if we can build road+tram stop over it. */
1148 RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
1149 if (road_rt != INVALID_ROADTYPE) {
1150 Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
1151 if (road_owner == OWNER_TOWN) {
1152 if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
1153 } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
1154 ret = CheckOwnership(road_owner);
1155 if (ret.Failed()) return ret;
1156 }
1157 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
1158
1159 if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1160
1161 if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN && road_owner != OWNER_NONE) {
1162 ret = CheckOwnership(road_owner);
1163 if (ret.Failed()) return ret;
1164 }
1165
1166 cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
1167 } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
1168 cost.AddCost(RoadBuildCost(rt) * 2);
1169 }
1170
1171 /* There is a tram, check if we can build road+tram stop over it. */
1172 RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
1173 if (tram_rt != INVALID_ROADTYPE) {
1174 Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
1175 if (Company::IsValidID(tram_owner) &&
1176 (!_settings_game.construction.road_stop_on_competitor_road ||
1177 /* Disallow breaking end-of-line of someone else
1178 * so trams can still reverse on this tile. */
1179 HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
1180 ret = CheckOwnership(tram_owner);
1181 if (ret.Failed()) return ret;
1182 }
1183 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
1184
1185 if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1186
1187 cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
1188 } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
1189 cost.AddCost(RoadBuildCost(rt) * 2);
1190 }
1191 } else if (rt == INVALID_ROADTYPE) {
1192 return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
1193 } else {
1194 ret = Command<Commands::LandscapeClear>::Do(flags, cur_tile);
1195 if (ret.Failed()) return ret;
1196 cost.AddCost(ret.GetCost());
1197 cost.AddCost(RoadBuildCost(rt) * 2);
1198 }
1199 }
1200
1201 return cost;
1202}
1203
1211{
1212 TileArea cur_ta = st->train_station;
1213
1214 /* determine new size of train station region.. */
1215 int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
1216 int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
1217 new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
1218 new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
1219 new_ta.tile = TileXY(x, y);
1220
1221 /* make sure the final size is not too big. */
1222 if (new_ta.w > _settings_game.station.station_spread || new_ta.h > _settings_game.station.station_spread) {
1223 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1224 }
1225
1226 return CommandCost();
1227}
1228
1229RailStationTileLayout::RailStationTileLayout(const StationSpec *spec, uint8_t platforms, uint8_t length) : platforms(platforms), length(length)
1230{
1231 if (spec == nullptr) return;
1232
1233 /* Look for a predefined layout for the required size. */
1234 auto found = spec->layouts.find(GetStationLayoutKey(platforms, length));
1235 if (found != std::end(spec->layouts)) this->layout = found->second;
1236}
1237
1238StationGfx RailStationTileLayout::Iterator::operator*() const
1239{
1240 /* Use predefined layout if it exists. Mask bit zero which will indicate axis. */
1241 if (!stl.layout.empty()) return this->stl.layout[this->position] & ~1;
1242
1243 if (this->stl.length == 1) {
1244 /* Special case for 1-long platforms, all bare platforms except one small building. */
1245 return this->position == ((this->stl.platforms - 1) / 2) ? 2 : 0;
1246 }
1247
1248 if ((this->position < this->stl.length && (this->stl.platforms % 2 == 1))) {
1249 /* Number of tracks is odd, make the first platform bare with a small building. */
1250 return this->position == ((this->stl.length - 1) / 2) ? 2 : 0;
1251 }
1252
1253 if (this->stl.length > 4 && ((this->position % this->stl.length) == 0 || (this->position % this->stl.length) == this->stl.length - 1)) {
1254 /* Station is longer than 4 tiles, place bare platforms at either end. */
1255 return 0;
1256 }
1257
1258 /* None of the above so must be north or south part of larger station. */
1259 return (((this->position / this->stl.length) % 2) == (this->stl.platforms % 2)) ? 4 : 6;
1260}
1261
1275template <class T, StringID error_message, class F>
1276CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1277{
1278 assert(*st == nullptr);
1279 bool check_surrounding = true;
1280
1281 if (existing_station != StationID::Invalid()) {
1282 if (adjacent && existing_station != station_to_join) {
1283 /* You can't build an adjacent station over the top of one that
1284 * already exists. */
1285 return CommandCost(error_message);
1286 } else {
1287 /* Extend the current station, and don't check whether it will
1288 * be near any other stations. */
1289 T *candidate = T::GetIfValid(existing_station);
1290 if (candidate != nullptr && filter(candidate)) *st = candidate;
1291 check_surrounding = (*st == nullptr);
1292 }
1293 } else {
1294 /* There's no station here. Don't check the tiles surrounding this
1295 * one if the company wanted to build an adjacent station. */
1296 if (adjacent) check_surrounding = false;
1297 }
1298
1299 if (check_surrounding) {
1300 /* Make sure there is no more than one other station around us that is owned by us. */
1301 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1302 if (ret.Failed()) return ret;
1303 }
1304
1305 /* Distant join */
1306 if (*st == nullptr && station_to_join != StationID::Invalid()) *st = T::GetIfValid(station_to_join);
1307
1308 return CommandCost();
1309}
1310
1320static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1321{
1322 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1323}
1324
1335CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1336{
1337 if (is_road) {
1338 return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_ROADWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp, [](const Waypoint *wp) -> bool { return HasBit(wp->waypoint_flags, WPF_ROAD); });
1339 } else {
1340 return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp, [](const Waypoint *wp) -> bool { return !HasBit(wp->waypoint_flags, WPF_ROAD); });
1341 }
1342}
1343
1355
1367
1382static CommandCost CalculateRailStationCost(TileArea tile_area, DoCommandFlags flags, Axis axis, StationID *station, RailType rt, std::vector<Train *> &affected_vehicles, StationClassID spec_class, uint16_t spec_index, uint8_t plat_len, uint8_t numtracks)
1383{
1385 bool length_price_ready = true;
1386 uint8_t tracknum = 0;
1387 int allowed_z = -1;
1388 for (TileIndex cur_tile : tile_area) {
1389 /* Clear the land below the station. */
1390 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1391 if (ret.Failed()) return ret;
1392
1393 /* Only add _price[Price::BuildStationRailLength] once for each valid plat_len. */
1394 if (tracknum == numtracks) {
1395 length_price_ready = true;
1396 tracknum = 0;
1397 } else {
1398 tracknum++;
1399 }
1400
1401 /* AddCost for new or rotated rail stations. */
1402 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1403 cost.AddCost(ret.GetCost());
1405 cost.AddCost(RailBuildCost(rt));
1406
1407 if (length_price_ready) {
1409 length_price_ready = false;
1410 }
1411 }
1412 }
1413
1414 return cost;
1415}
1416
1423static StationSpec::TileFlags GetStationTileFlags(StationGfx gfx, const StationSpec *statspec)
1424{
1425 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1426 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlag::Pylons : StationSpec::TileFlags{};
1427 return statspec->tileflags[gfx];
1428}
1429
1436{
1437 const auto flags = GetStationTileFlags(GetStationGfx(tile), statspec);
1441}
1442
1457CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailType rt, Axis axis, uint8_t numtracks, uint8_t plat_len, StationClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
1458{
1459 /* Does the authority allow this? */
1460 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1461 if (ret.Failed()) return ret;
1462
1463 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1464
1465 /* Check if the given station class is valid */
1466 if (spec_class.base() >= StationClass::GetClassCount()) return CMD_ERROR;
1467 const StationClass *cls = StationClass::Get(spec_class);
1468 if (IsWaypointClass(*cls)) return CMD_ERROR;
1469 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1470 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1471
1472 int w_org, h_org;
1473 if (axis == AXIS_X) {
1474 w_org = plat_len;
1475 h_org = numtracks;
1476 } else {
1477 h_org = plat_len;
1478 w_org = numtracks;
1479 }
1480
1481 /* Check if the first tile and the last tile are valid */
1482 if (!IsValidTile(tile_org) || TileAddWrap(tile_org, w_org - 1, h_org - 1) == INVALID_TILE) return CMD_ERROR;
1483
1484 bool reuse = (station_to_join != NEW_STATION);
1485 if (!reuse) station_to_join = StationID::Invalid();
1486 bool distant_join = (station_to_join != StationID::Invalid());
1487
1488 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1489
1490 if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR;
1491
1492 /* these values are those that will be stored in train_tile and station_platforms */
1493 TileArea new_location(tile_org, w_org, h_org);
1494
1495 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1496 StationID est = StationID::Invalid();
1497 std::vector<Train *> affected_vehicles;
1498 /* Add construction and clearing expenses. */
1499 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1500 if (cost.Failed()) return cost;
1501
1502 Station *st = nullptr;
1503 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1504 if (ret.Failed()) return ret;
1505
1506 ret = BuildStationPart(&st, flags, reuse, new_location, StationNaming::Rail);
1507 if (ret.Failed()) return ret;
1508
1509 if (st != nullptr && st->train_station.tile != INVALID_TILE) {
1510 ret = CanExpandRailStation(st, new_location);
1511 if (ret.Failed()) return ret;
1512 }
1513
1514 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1515 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1516 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1517
1518 RailStationTileLayout stl{statspec, numtracks, plat_len};
1519 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1520 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1521 /* Don't check the layout if there's no bridge above anyway. */
1522 if (!IsBridgeAbove(tile)) continue;
1523
1524 StationGfx gfx = *it + axis;
1525 if (statspec != nullptr) {
1526 uint32_t platinfo = GetPlatformInfo(AXIS_X, gfx, plat_len, numtracks, j, i, false);
1527 /* As the station is not yet completely finished, the station does not yet exist. */
1528 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, INVALID_TILE);
1529 if (callback != CALLBACK_FAILED && callback <= UINT8_MAX) gfx = (callback & ~1) + axis;
1530 }
1531
1532 ret = IsRailStationBridgeAboveOk(tile, statspec, StationType::Rail, gfx);
1533 if (ret.Failed()) return ret;
1534 }
1535 }
1536
1537 /* Check if we can allocate a custom stationspec to this station */
1538 auto specindex = AllocateSpecToStation(statspec, st);
1539 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1540
1541 if (statspec != nullptr) {
1542 /* Perform NewStation checks */
1543
1544 /* Check if the station size is permitted */
1545 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1546 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1547
1548 /* Check if the station is buildable */
1550 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1552 }
1553 }
1554
1555 if (flags.Test(DoCommandFlag::Execute)) {
1556 st->train_station = new_location;
1557 st->AddFacility(StationFacility::Train, new_location.tile);
1558
1559 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
1560
1561 if (specindex.has_value()) AssignSpecToStation(statspec, st, *specindex);
1562 if (statspec != nullptr) {
1563 /* Include this station spec's animation trigger bitmask
1564 * in the station's cached copy. */
1565 st->cached_anim_triggers.Set(statspec->animation.triggers);
1566 }
1567
1568 Track track = AxisToTrack(axis);
1569 Company *c = Company::Get(st->owner);
1570 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1571 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1572 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1573 /* Check for trains having a reservation for this tile. */
1575 if (v != nullptr) {
1576 affected_vehicles.push_back(v);
1578 }
1579 }
1580
1581 /* Railtype can change when overbuilding. */
1582 if (IsRailStationTile(tile)) {
1583 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1585 }
1586
1587 /* Remove animation if overbuilding */
1588 DeleteAnimatedTile(tile);
1589 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1590
1591 MakeRailStation(tile, st->owner, st->index, axis, *it, rt);
1592 /* Free the spec if we overbuild something */
1593 DeallocateSpecFromStation(st, old_specindex);
1594 if (statspec == nullptr) DeleteNewGRFInspectWindow(GSF_STATIONS, tile);
1595
1596 SetCustomStationSpecIndex(tile, *specindex);
1597 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1598 SetAnimationFrame(tile, 0);
1599
1600 if (statspec != nullptr) {
1601 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1602 uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks, j, i, false);
1603
1604 /* As the station is not yet completely finished, the station does not yet exist. */
1605 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1606 if (callback != CALLBACK_FAILED) {
1607 if (callback <= UINT8_MAX) {
1608 SetStationGfx(tile, (callback & ~1) + axis);
1609 } else {
1611 }
1612 }
1613
1614 /* Trigger station animation -- after building? */
1615 TriggerStationAnimation(st, tile, StationAnimationTrigger::Built);
1616 }
1617
1618 SetRailStationTileFlags(tile, statspec);
1619
1620 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1622 }
1623 AddTrackToSignalBuffer(tile_track, track, _current_company);
1624 YapfNotifyTrackLayoutChange(tile_track, track);
1625 }
1626
1627 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1628 /* Restore reservations of trains. */
1629 RestoreTrainReservation(affected_vehicles[i]);
1630 }
1631
1632 /* Check whether we need to expand the reservation of trains already on the station. */
1633 TileArea update_reservation_area;
1634 if (axis == AXIS_X) {
1635 update_reservation_area = TileArea(tile_org, 1, numtracks);
1636 } else {
1637 update_reservation_area = TileArea(tile_org, numtracks, 1);
1638 }
1639
1640 for (TileIndex tile : update_reservation_area) {
1641 /* Don't even try to make eye candy parts reserved. */
1642 if (IsStationTileBlocked(tile)) continue;
1643
1644 DiagDirection dir = AxisToDiagDir(axis);
1645 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1646 TileIndex platform_begin = tile;
1647 TileIndex platform_end = tile;
1648
1649 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1650 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1651 platform_begin = next_tile;
1652 }
1653 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1654 platform_end = next_tile;
1655 }
1656
1657 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1658 bool reservation = false;
1659 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1660 reservation = HasStationReservation(t);
1661 }
1662
1663 if (reservation) {
1664 SetRailStationPlatformReservation(platform_begin, dir, true);
1665 }
1666 }
1667
1668 st->MarkTilesDirty(false);
1670 }
1671
1672 return cost;
1673}
1674
1675static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1676{
1677restart:
1678
1679 /* too small? */
1680 if (ta.w != 0 && ta.h != 0) {
1681 /* check the left side, x = constant, y changes */
1682 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1683 /* the left side is unused? */
1684 if (++i == ta.h) {
1685 ta.tile += TileDiffXY(1, 0);
1686 ta.w--;
1687 goto restart;
1688 }
1689 }
1690
1691 /* check the right side, x = constant, y changes */
1692 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1693 /* the right side is unused? */
1694 if (++i == ta.h) {
1695 ta.w--;
1696 goto restart;
1697 }
1698 }
1699
1700 /* check the upper side, y = constant, x changes */
1701 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1702 /* the left side is unused? */
1703 if (++i == ta.w) {
1704 ta.tile += TileDiffXY(0, 1);
1705 ta.h--;
1706 goto restart;
1707 }
1708 }
1709
1710 /* check the lower side, y = constant, x changes */
1711 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1712 /* the left side is unused? */
1713 if (++i == ta.w) {
1714 ta.h--;
1715 goto restart;
1716 }
1717 }
1718 } else {
1719 ta.Clear();
1720 }
1721
1722 return ta;
1723}
1724
1725static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1726{
1727 return st->TileBelongsToRailStation(tile);
1728}
1729
1730static void MakeRailStationAreaSmaller(BaseStation *st)
1731{
1732 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1733}
1734
1735static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1736{
1737 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1738}
1739
1740static void MakeShipStationAreaSmaller(Station *st)
1741{
1742 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1743 UpdateStationDockingTiles(st);
1744}
1745
1746static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1747{
1748 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1749}
1750
1751void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1752{
1753 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1754}
1755
1766template <class T>
1767CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
1768{
1769 /* Count of the number of tiles removed */
1770 int quantity = 0;
1772 /* Accumulator for the errors seen during clearing. If no errors happen,
1773 * and the quantity is 0 there is no station. Otherwise it will be one
1774 * of the other error that got accumulated. */
1775 CommandCost error;
1776
1777 /* Do the action for every tile into the area */
1778 for (TileIndex tile : ta) {
1779 /* Make sure the specified tile is a rail station */
1780 if (!HasStationTileRail(tile)) continue;
1781
1782 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1784 error.AddCost(std::move(ret));
1785 if (error.Failed()) continue;
1786
1787 /* Check ownership of station */
1788 T *st = T::GetByTile(tile);
1789 if (st == nullptr) continue;
1790
1792 ret = CheckOwnership(st->owner);
1793 error.AddCost(std::move(ret));
1794 if (error.Failed()) continue;
1795 }
1796
1797 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1798 quantity++;
1799
1800 if (keep_rail || IsStationTileBlocked(tile)) {
1801 /* Don't refund the 'steel' of the track when we keep the
1802 * rail, or when the tile didn't have any rail at all. */
1803 total_cost.AddCost(-_price[Price::ClearRail]);
1804 }
1805
1806 if (flags.Test(DoCommandFlag::Execute)) {
1807 /* read variables before the station tile is removed */
1808 uint specindex = GetCustomStationSpecIndex(tile);
1809 Track track = GetRailStationTrack(tile);
1810 Owner owner = GetTileOwner(tile);
1811 RailType rt = GetRailType(tile);
1812 Train *v = nullptr;
1813
1814 if (HasStationReservation(tile)) {
1815 v = GetTrainForReservation(tile, track);
1816 if (v != nullptr) FreeTrainReservation(v);
1817 }
1818
1819 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1820 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1821
1822 DoClearSquare(tile);
1823 DeleteNewGRFInspectWindow(GSF_STATIONS, tile.base());
1824 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
1825 Company::Get(owner)->infrastructure.station--;
1827
1828 st->tile_waiting_random_triggers.erase(tile);
1829 st->rect.AfterRemoveTile(st, tile);
1830 AddTrackToSignalBuffer(tile, track, owner);
1831 YapfNotifyTrackLayoutChange(tile, track);
1832
1833 DeallocateSpecFromStation(st, specindex);
1834
1835 include(affected_stations, st);
1836
1837 if (v != nullptr) RestoreTrainReservation(v);
1838 }
1839 }
1840
1841 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1842
1843 for (T *st : affected_stations) {
1844
1845 /* now we need to make the "spanned" area of the railway station smaller
1846 * if we deleted something at the edges.
1847 * we also need to adjust train_tile. */
1848 MakeRailStationAreaSmaller(st);
1849 UpdateStationSignCoord(st);
1850
1851 /* if we deleted the whole station, delete the train facility. */
1852 if (st->train_station.tile == INVALID_TILE) {
1856 MarkCatchmentTilesDirty();
1857 st->UpdateVirtCoord();
1859 }
1860 }
1861
1862 total_cost.AddCost(quantity * removal_cost);
1863 return total_cost;
1864}
1865
1875CommandCost CmdRemoveFromRailStation(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
1876{
1877 if (end == 0) end = start;
1878 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1879
1880 TileArea ta(start, end);
1881 std::vector<Station *> affected_stations;
1882
1883 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[Price::ClearStationRail], keep_rail);
1884 if (ret.Failed()) return ret;
1885
1886 /* Do all station specific functions here. */
1887 for (Station *st : affected_stations) {
1888
1890 st->MarkTilesDirty(false);
1891 MarkCatchmentTilesDirty();
1892 st->RecomputeCatchment();
1893 }
1894
1895 /* Now apply the rail cost to the number that we deleted */
1896 return ret;
1897}
1898
1908CommandCost CmdRemoveFromRailWaypoint(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
1909{
1910 if (end == 0) end = start;
1911 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1912
1913 TileArea ta(start, end);
1914 std::vector<Waypoint *> affected_stations;
1915
1916 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[Price::ClearWaypointRail], keep_rail);
1917}
1918
1919
1928template <class T>
1929CommandCost RemoveRailStation(T *st, DoCommandFlags flags, Money removal_cost)
1930{
1931 /* Current company owns the station? */
1933 CommandCost ret = CheckOwnership(st->owner);
1934 if (ret.Failed()) return ret;
1935 }
1936
1937 /* determine width and height of platforms */
1938 TileArea ta = st->train_station;
1939
1940 assert(ta.w != 0 && ta.h != 0);
1941
1943 /* clear all areas of the station */
1944 for (TileIndex tile : ta) {
1945 /* only remove tiles that are actually train station tiles */
1946 if (st->TileBelongsToRailStation(tile)) {
1947 std::vector<T*> affected_stations; // dummy
1948 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1949 if (ret.Failed()) return ret;
1950 cost.AddCost(ret.GetCost());
1951 }
1952 }
1953
1954 return cost;
1955}
1956
1963static CommandCost RemoveRailStation(TileIndex tile, DoCommandFlags flags)
1964{
1965 /* if there is flooding, remove platforms tile by tile */
1967 return Command<Commands::RemoveFromRailStation>::Do(DoCommandFlag::Execute, tile, TileIndex{}, false);
1968 }
1969
1970 Station *st = Station::GetByTile(tile);
1972
1974
1975 return cost;
1976}
1977
1984static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlags flags)
1985{
1986 /* if there is flooding, remove waypoints tile by tile */
1988 return Command<Commands::RemoveFromRailWaypoint>::Do(DoCommandFlag::Execute, tile, TileIndex{}, false);
1989 }
1990
1992}
1993
1994
2000static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
2001{
2002 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
2003
2004 if (*primary_stop == nullptr) {
2005 /* we have no roadstop of the type yet, so write a "primary stop" */
2006 return primary_stop;
2007 } else {
2008 /* there are stops already, so append to the end of the list */
2009 RoadStop *stop = *primary_stop;
2010 while (stop->next != nullptr) stop = stop->next;
2011 return &stop->next;
2012 }
2013}
2014
2015static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
2016CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
2017
2027static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
2028{
2029 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
2030}
2031
2046CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *station, RoadType rt, Money unit_cost)
2047{
2048 DiagDirections invalid_dirs{};
2049 if (is_drive_through) {
2050 invalid_dirs.Set(AxisToDiagDir(axis));
2051 invalid_dirs.Set(ReverseDiagDir(AxisToDiagDir(axis)));
2052 } else {
2053 invalid_dirs.Set(ddir);
2054 }
2055
2056 /* Check every tile in the area. */
2057 int allowed_z = -1;
2059 for (TileIndex cur_tile : tile_area) {
2060 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, roadstopspec, flags, invalid_dirs, is_drive_through, station_type, axis, station, rt);
2061 if (ret.Failed()) return ret;
2062
2063 bool is_preexisting_roadstop = IsTileType(cur_tile, TileType::Station) && IsAnyRoadStop(cur_tile);
2064
2065 /* Only add costs if a stop doesn't already exist in the location */
2066 if (!is_preexisting_roadstop) {
2067 cost.AddCost(ret.GetCost());
2068 cost.AddCost(unit_cost);
2069 }
2070 }
2071
2072 return cost;
2073}
2074
2091CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
2092 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
2093{
2094 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= RoadStopType::End) return CMD_ERROR;
2095 bool reuse = (station_to_join != NEW_STATION);
2096 if (!reuse) station_to_join = StationID::Invalid();
2097 bool distant_join = (station_to_join != StationID::Invalid());
2098
2099 /* Check if the given station class is valid */
2100 if (spec_class.base() >= RoadStopClass::GetClassCount()) return CMD_ERROR;
2101 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
2102 if (IsWaypointClass(*cls)) return CMD_ERROR;
2103 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
2104
2105 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
2106 if (roadstopspec != nullptr) {
2107 if (stop_type == RoadStopType::Truck && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2108 if (stop_type == RoadStopType::Bus && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2109 if (!is_drive_through && roadstopspec->flags.Test(RoadStopSpecFlag::DriveThroughOnly)) return CMD_ERROR;
2110 }
2111
2112 /* Check if the requested road stop is too big */
2113 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2114 /* Check for incorrect width / length. */
2115 if (width == 0 || length == 0) return CMD_ERROR;
2116 /* Check if the first tile and the last tile are valid */
2117 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
2118
2119 TileArea roadstop_area(tile, width, length);
2120
2121 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2122
2123 /* Trams only have drive through stops */
2124 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
2125
2126 Axis axis = DiagDirToAxis(ddir);
2127
2129 if (ret.Failed()) return ret;
2130
2131 bool is_truck_stop = stop_type != RoadStopType::Bus;
2132
2133 /* Total road stop cost. */
2134 Money unit_cost;
2135 if (roadstopspec != nullptr) {
2136 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? Price::BuildStationTruck : Price::BuildStationBus);
2137 } else {
2138 unit_cost = _price[is_truck_stop ? Price::BuildStationTruck : Price::BuildStationBus];
2139 }
2140 StationID est = StationID::Invalid();
2141 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, roadstopspec, axis, ddir, &est, rt, unit_cost);
2142 if (cost.Failed()) return cost;
2143
2144 Station *st = nullptr;
2145 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2146 if (ret.Failed()) return ret;
2147
2148 /* Check if this number of road stops can be allocated. */
2149 if (!RoadStop::CanAllocateItem(static_cast<size_t>(roadstop_area.w) * roadstop_area.h)) return CommandCost(is_truck_stop ? STR_ERROR_TOO_MANY_TRUCK_STOPS : STR_ERROR_TOO_MANY_BUS_STOPS);
2150
2151 ret = BuildStationPart(&st, flags, reuse, roadstop_area, StationNaming::Road);
2152 if (ret.Failed()) return ret;
2153
2154 /* Check if we can allocate a custom stationspec to this station */
2155 auto specindex = AllocateSpecToRoadStop(roadstopspec, st);
2156 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2157
2158 if (roadstopspec != nullptr) {
2159 /* Perform NewGRF checks */
2160
2161 /* Check if the road stop is buildable */
2162 if (roadstopspec->callback_mask.Test(RoadStopCallbackMask::Avail)) {
2163 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? StationType::Truck : StationType::Bus, 0);
2164 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2165 }
2166 }
2167
2168 if (flags.Test(DoCommandFlag::Execute)) {
2169 if (specindex.has_value()) AssignSpecToRoadStop(roadstopspec, st, *specindex);
2170 /* Check every tile in the area. */
2171 for (TileIndex cur_tile : roadstop_area) {
2172 /* Get existing road types and owners before any tile clearing */
2173 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
2174 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
2175 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
2176 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
2177
2178 if (IsTileType(cur_tile, TileType::Station) && IsStationRoadStop(cur_tile)) {
2179 RemoveRoadStop(cur_tile, flags, *specindex);
2180 }
2181
2182 if (roadstopspec != nullptr) {
2183 /* Include this road stop spec's animation trigger bitmask
2184 * in the station's cached copy. */
2185 st->cached_roadstop_anim_triggers.Set(roadstopspec->animation.triggers);
2186 }
2187
2188 RoadStop *road_stop = RoadStop::Create(cur_tile);
2189 /* Insert into linked list of RoadStops. */
2190 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2191 *currstop = road_stop;
2192
2193 if (is_truck_stop) {
2194 st->truck_station.Add(cur_tile);
2195 } else {
2196 st->bus_station.Add(cur_tile);
2197 }
2198
2199 /* Initialize an empty station. */
2200 st->AddFacility(is_truck_stop ? StationFacility::TruckStop : StationFacility::BusStop, cur_tile);
2201
2202 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
2203
2204 RoadStopType rs_type = is_truck_stop ? RoadStopType::Truck : RoadStopType::Bus;
2205 if (is_drive_through) {
2206 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2207 * bits first. */
2208 if (IsNormalRoadTile(cur_tile)) {
2209 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
2210 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
2211 }
2212
2213 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2214 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2215
2216 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == RoadStopType::Bus ? StationType::Bus : StationType::Truck), road_rt, tram_rt, axis);
2217 road_stop->MakeDriveThrough();
2218 } else {
2219 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2220 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2221 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2222 }
2225 Company::Get(st->owner)->infrastructure.station++;
2226
2227 SetCustomRoadStopSpecIndex(cur_tile, *specindex);
2228 if (roadstopspec != nullptr) {
2229 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2230 TriggerRoadStopAnimation(st, cur_tile, StationAnimationTrigger::Built);
2231 }
2232
2233 MarkTileDirtyByTile(cur_tile);
2234 }
2235
2236 if (st != nullptr) {
2238 }
2239 }
2240 return cost;
2241}
2242
2250static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2251{
2252 Station *st = Station::GetByTile(tile);
2253
2255 CommandCost ret = CheckOwnership(st->owner);
2256 if (ret.Failed()) return ret;
2257 }
2258
2259 bool is_truck = IsTruckStop(tile);
2260
2261 RoadStop **primary_stop;
2262 RoadStop *cur_stop;
2263 if (is_truck) { // truck stop
2264 primary_stop = &st->truck_stops;
2265 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Truck);
2266 } else {
2267 primary_stop = &st->bus_stops;
2268 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Bus);
2269 }
2270
2271 assert(cur_stop != nullptr);
2272
2273 /* don't do the check for drive-through road stops when company bankrupts */
2275 /* remove the 'going through road stop' status from all vehicles on that tile */
2276 if (flags.Test(DoCommandFlag::Execute)) {
2277 for (Vehicle *v : VehiclesOnTile(tile)) {
2278 if (v->type != VEH_ROAD) continue;
2279 /* Okay... we are a road vehicle on a drive through road stop.
2280 * But that road stop has just been removed, so we need to make
2281 * sure we are in a valid state... however, vehicles can also
2282 * turn on road stop tiles, so only clear the 'road stop' state
2283 * bits and only when the state was 'in road stop', otherwise
2284 * we'll end up clearing the turn around bits. */
2287 }
2288 }
2289 } else {
2291 if (ret.Failed()) return ret;
2292 }
2293
2294 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2295
2296 if (flags.Test(DoCommandFlag::Execute)) {
2297 if (*primary_stop == cur_stop) {
2298 /* removed the first stop in the list */
2299 *primary_stop = cur_stop->next;
2300 /* removed the only stop? */
2301 if (*primary_stop == nullptr) {
2304 }
2305 } else {
2306 /* tell the predecessor in the list to skip this stop */
2307 RoadStop *pred = *primary_stop;
2308 while (pred->next != cur_stop) pred = pred->next;
2309 pred->next = cur_stop->next;
2310 }
2311
2312 /* Update company infrastructure counts. */
2313 for (RoadTramType rtt : _roadtramtypes) {
2314 RoadType rt = GetRoadType(tile, rtt);
2316 }
2317
2318 Company::Get(st->owner)->infrastructure.station--;
2320
2321 uint specindex = GetCustomRoadStopSpecIndex(tile);
2322
2323 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2324
2325 if (IsDriveThroughStopTile(tile)) {
2326 /* Clears the tile for us */
2327 cur_stop->ClearDriveThrough();
2328 DeleteAnimatedTile(tile);
2329 } else {
2330 DoClearSquare(tile);
2331 }
2332
2333 delete cur_stop;
2334
2335 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2336 * this station, then look for any currently heading to the tile. */
2337 StationID station_id = st->index;
2339 [](const Vehicle *v) { return v->type == VEH_ROAD; },
2340 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2341 [station_id, tile](Vehicle *v) {
2342 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2343 v->SetDestTile(v->GetOrderStationLocation(station_id));
2344 }
2345 }
2346 );
2347
2348 st->rect.AfterRemoveTile(st, tile);
2349
2350 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
2351
2352 st->tile_waiting_random_triggers.erase(tile);
2353 st->RemoveRoadStopTileData(tile);
2354 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2355
2356 /* Update the tile area of the truck/bus stop */
2357 if (is_truck) {
2358 st->truck_station.Clear();
2359 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2360 } else {
2361 st->bus_station.Clear();
2362 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2363 }
2364 }
2365
2367 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2368}
2369
2377CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2378{
2379 Waypoint *wp = Waypoint::GetByTile(tile);
2380
2382 CommandCost ret = CheckOwnership(wp->owner);
2383 if (ret.Failed()) return ret;
2384 }
2385
2386 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2387 if (!flags.Test(DoCommandFlag::Bankrupt)) {
2389 if (ret.Failed()) return ret;
2390 }
2391
2392 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2393
2394 if (flags.Test(DoCommandFlag::Execute)) {
2395 /* Update company infrastructure counts. */
2396 for (RoadTramType rtt : _roadtramtypes) {
2397 RoadType rt = GetRoadType(tile, rtt);
2399 }
2400
2401 Company::Get(wp->owner)->infrastructure.station--;
2403
2404 uint specindex = GetCustomRoadStopSpecIndex(tile);
2405
2406 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2407
2408 DoClearSquare(tile);
2409
2410 wp->rect.AfterRemoveTile(wp, tile);
2411
2412 wp->tile_waiting_random_triggers.erase(tile);
2413 wp->RemoveRoadStopTileData(tile);
2414 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2415
2416 if (replacement_spec_index < 0) {
2417 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2418
2419 UpdateStationSignCoord(wp);
2420
2421 /* if we deleted the whole waypoint, delete the road facility. */
2425 wp->UpdateVirtCoord();
2427 }
2428 }
2429 }
2430
2432}
2433
2442static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
2443{
2445 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2446 bool had_success = false;
2447
2448 for (TileIndex cur_tile : roadstop_area) {
2449 /* Make sure the specified tile is a road stop of the correct type */
2450 if (!IsTileType(cur_tile, TileType::Station) || !IsAnyRoadStop(cur_tile) || IsRoadWaypoint(cur_tile) != road_waypoint) continue;
2451
2452 /* Save information on to-be-restored roads before the stop is removed. */
2453 RoadBits road_bits = ROAD_NONE;
2454 RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
2455 Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
2456 if (IsDriveThroughStopTile(cur_tile)) {
2457 for (RoadTramType rtt : _roadtramtypes) {
2458 road_type[rtt] = GetRoadType(cur_tile, rtt);
2459 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2460 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2461 /* If we don't want to preserve our roads then restore only roads of others. */
2462 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2463 }
2464 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2465 }
2466
2467 CommandCost ret;
2468 if (road_waypoint) {
2469 ret = RemoveRoadWaypointStop(cur_tile, flags);
2470 } else {
2471 ret = RemoveRoadStop(cur_tile, flags);
2472 }
2473 if (ret.Failed()) {
2474 last_error = std::move(ret);
2475 continue;
2476 }
2477 cost.AddCost(ret.GetCost());
2478 had_success = true;
2479
2480 /* Restore roads. */
2481 if (flags.Test(DoCommandFlag::Execute) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
2482 MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2483 road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
2484
2485 /* Update company infrastructure counts. */
2486 int count = CountBits(road_bits);
2487 UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
2488 UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
2489 }
2490 }
2491
2492 return had_success ? cost : last_error;
2493}
2494
2505CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2506{
2507 if (stop_type >= RoadStopType::End) return CMD_ERROR;
2508 /* Check for incorrect width / height. */
2509 if (width == 0 || height == 0) return CMD_ERROR;
2510 /* Check if the first tile and the last tile are valid */
2511 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2512 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2513 if (remove_road && flags.Test(DoCommandFlag::Bankrupt)) return CMD_ERROR;
2514
2515 TileArea roadstop_area(tile, width, height);
2516
2517 return RemoveGenericRoadStop(flags, roadstop_area, false, remove_road);
2518}
2519
2528{
2529 if (end == 0) end = start;
2530 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2531
2532 TileArea roadstop_area(start, end);
2533
2534 return RemoveGenericRoadStop(flags, roadstop_area, true, false);
2535}
2536
2545uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2546{
2547 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2548 * So no need to go any further*/
2549 if (as->noise_level < 2) return as->noise_level;
2550
2551 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2552 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2553 * Basically, it says that the less tolerant a town is, the bigger the distance before
2554 * an actual decrease can be granted */
2555 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2556
2557 /* now, we want to have the distance segmented using the distance judged bareable by town
2558 * This will give us the coefficient of reduction the distance provides. */
2559 uint noise_reduction = distance / town_tolerance_distance;
2560
2561 /* If the noise reduction equals the airport noise itself, don't give it for free.
2562 * Otherwise, simply reduce the airport's level. */
2563 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2564}
2565
2576Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2577{
2578 assert(Town::GetNumItems() > 0);
2579
2580 Town *nearest = nullptr;
2581
2582 auto width = as->size_x;
2583 auto height = as->size_y;
2584 if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
2585
2586 uint perimeter_min_x = TileX(tile);
2587 uint perimeter_min_y = TileY(tile);
2588 uint perimeter_max_x = perimeter_min_x + width - 1;
2589 uint perimeter_max_y = perimeter_min_y + height - 1;
2590
2591 mindist = UINT_MAX - 1; // prevent overflow
2592
2593 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2594 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2595 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2596 if (TileX(cur_tile) == perimeter_min_x || TileX(cur_tile) == perimeter_max_x || TileY(cur_tile) == perimeter_min_y || TileY(cur_tile) == perimeter_max_y) {
2597 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2598 if (t == nullptr) continue;
2599
2600 uint dist = DistanceManhattan(t->xy, cur_tile);
2601 if (dist == mindist && t->index < nearest->index) nearest = t;
2602 if (dist < mindist) {
2603 nearest = t;
2604 mindist = dist;
2605 }
2606 }
2607 }
2608
2609 return nearest;
2610}
2611
2619static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2620{
2622}
2623
2624
2627{
2628 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2629
2630 for (const Station *st : Station::Iterate()) {
2631 if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
2632 uint dist;
2633 Town *nearest = AirportGetNearestTown(st, dist);
2634 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2635 }
2636 }
2637}
2638
2649CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2650{
2651 bool reuse = (station_to_join != NEW_STATION);
2652 if (!reuse) station_to_join = StationID::Invalid();
2653 bool distant_join = (station_to_join != StationID::Invalid());
2654
2655 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2656
2657 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2658
2660 if (ret.Failed()) return ret;
2661
2662 /* Check if a valid, buildable airport was chosen for construction */
2663 const AirportSpec *as = AirportSpec::Get(airport_type);
2664 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2665 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2666
2667 Direction rotation = as->layouts[layout].rotation;
2668 int w = as->size_x;
2669 int h = as->size_y;
2670 if (rotation == DIR_E || rotation == DIR_W) std::swap(w, h);
2671 TileArea airport_area = TileArea(tile, w, h);
2672
2673 if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
2674 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2675 }
2676
2677 AirportTileTableIterator tile_iter(as->layouts[layout].tiles, tile);
2678 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2679 if (cost.Failed()) return cost;
2680
2681 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2682 uint dist;
2683 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2684 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2685
2686 /* Check if local auth would allow a new airport */
2687 StringID authority_refuse_message = STR_NULL;
2688 Town *authority_refuse_town = nullptr;
2689
2690 if (_settings_game.economy.station_noise_level) {
2691 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2692 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2693 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2694 authority_refuse_town = nearest;
2695 }
2696 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2697 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2698 uint num = 0;
2699 for (const Station *st : Station::Iterate()) {
2700 if (st->town == t && st->facilities.Test(StationFacility::Airport) && st->airport.type != AT_OILRIG) num++;
2701 }
2702 if (num >= 2) {
2703 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2704 authority_refuse_town = t;
2705 }
2706 }
2707
2708 if (authority_refuse_message != STR_NULL) {
2709 return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index);
2710 }
2711
2712 Station *st = nullptr;
2713 ret = FindJoiningStation(StationID::Invalid(), station_to_join, allow_adjacent, airport_area, &st);
2714 if (ret.Failed()) return ret;
2715
2716 /* Distant join */
2717 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2718
2719 ret = BuildStationPart(&st, flags, reuse, airport_area, GetAirport(airport_type)->flags.Test(AirportFTAClass::Flag::Airplanes) ? StationNaming::Airport : StationNaming::Heliport);
2720 if (ret.Failed()) return ret;
2721
2722 if (st != nullptr && st->airport.tile != INVALID_TILE) {
2723 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2724 }
2725
2726 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2728 }
2729
2730 if (flags.Test(DoCommandFlag::Execute)) {
2731 /* Always add the noise, so there will be no need to recalculate when option toggles */
2732 nearest->noise_reached += newnoise_level;
2733
2735 st->airport.type = airport_type;
2736 st->airport.layout = layout;
2737 st->airport.blocks = {};
2738 st->airport.rotation = rotation;
2739
2740 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
2741
2742 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2743 Tile t(iter);
2744 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WaterClass::Invalid);
2745 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2746 st->airport.Add(iter);
2747
2748 if (AirportTileSpec::Get(GetTranslatedAirportTileID(iter.GetStationGfx()))->animation.status != AnimationStatus::NoAnimation) AddAnimatedTile(t);
2749 }
2750
2751 /* Only call the animation trigger after all tiles have been built */
2752 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2753 TriggerAirportTileAnimation(st, iter, AirportAnimationTrigger::Built);
2754 }
2755
2757
2758 Company::Get(st->owner)->infrastructure.airport++;
2759
2761 InvalidateWindowData(WC_STATION_VIEW, st->index, -1);
2762
2763 if (_settings_game.economy.station_noise_level) {
2764 SetWindowDirty(WC_TOWN_VIEW, nearest->index);
2765 }
2766 }
2767
2768 return cost;
2769}
2770
2777static CommandCost RemoveAirport(TileIndex tile, DoCommandFlags flags)
2778{
2779 Station *st = Station::GetByTile(tile);
2780
2782 CommandCost ret = CheckOwnership(st->owner);
2783 if (ret.Failed()) return ret;
2784 }
2785
2786 tile = st->airport.tile;
2787
2789
2790 for (const Aircraft *a : Aircraft::Iterate()) {
2791 if (!a->IsNormalAircraft()) continue;
2792 if (a->targetairport == st->index && a->state != FLYING) {
2793 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2794 }
2795 }
2796
2797 if (flags.Test(DoCommandFlag::Execute)) {
2798 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2799 TileIndex tile_cur = st->airport.GetHangarTile(i);
2800 OrderBackup::Reset(tile_cur, false);
2802 }
2803
2804 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2805 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2806 * need of recalculation */
2807 uint dist;
2808 Town *nearest = AirportGetNearestTown(st, dist);
2810
2811 if (_settings_game.economy.station_noise_level) {
2812 SetWindowDirty(WC_TOWN_VIEW, nearest->index);
2813 }
2814 }
2815
2816 for (TileIndex tile_cur : st->airport) {
2817 if (!st->TileBelongsToAirport(tile_cur)) continue;
2818
2819 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2820 if (ret.Failed()) return ret;
2821
2823
2824 if (flags.Test(DoCommandFlag::Execute)) {
2825 DoClearSquare(tile_cur);
2826 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur.base());
2827 }
2828 }
2829
2830 if (flags.Test(DoCommandFlag::Execute)) {
2831 /* Clear the persistent storage. */
2832 delete st->airport.psa;
2833
2834 st->rect.AfterRemoveRect(st, st->airport);
2835
2836 st->airport.Clear();
2839
2840 InvalidateWindowData(WC_STATION_VIEW, st->index, -1);
2841
2842 Company::Get(st->owner)->infrastructure.airport--;
2843
2845
2846 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
2847 }
2848
2849 return cost;
2850}
2851
2858CommandCost CmdOpenCloseAirport(DoCommandFlags flags, StationID station_id)
2859{
2860 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2861 Station *st = Station::Get(station_id);
2862
2864
2865 CommandCost ret = CheckOwnership(st->owner);
2866 if (ret.Failed()) return ret;
2867
2868 if (flags.Test(DoCommandFlag::Execute)) {
2871 }
2872 return CommandCost();
2873}
2874
2882bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2883{
2884 for (const OrderList *orderlist : OrderList::Iterate()) {
2885 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2886 assert(v != nullptr);
2887 if ((v->owner == company) != include_company) continue;
2888
2889 for (const Order &order : orderlist->GetOrders()) {
2890 if (order.GetDestination() == station && (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT))) {
2891 return true;
2892 }
2893 }
2894 }
2895 return false;
2896}
2897
2898static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
2899 {-1, 0},
2900 { 0, 0},
2901 { 0, 0},
2902 { 0, -1}
2903};
2904static const uint8_t _dock_w_chk[4] = { 2, 1, 2, 1 };
2905static const uint8_t _dock_h_chk[4] = { 1, 2, 1, 2 };
2906
2915CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
2916{
2917 bool reuse = (station_to_join != NEW_STATION);
2918 if (!reuse) station_to_join = StationID::Invalid();
2919 bool distant_join = (station_to_join != StationID::Invalid());
2920
2921 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2922
2924 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2925 direction = ReverseDiagDir(direction);
2926
2927 /* Docks cannot be placed on rapids */
2928 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2929
2931 if (ret.Failed()) return ret;
2932
2933 ret = IsDockBridgeAboveOk(tile, to_underlying(direction));
2934 if (ret.Failed()) return ret;
2935
2937 ret = Command<Commands::LandscapeClear>::Do(flags, tile);
2938 if (ret.Failed()) return ret;
2939 cost.AddCost(ret.GetCost());
2940
2941 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2942
2943 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2944 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2945 }
2946
2948 if (ret.Failed()) return ret;
2949
2950 /* Get the water class of the water tile before it is cleared.*/
2951 WaterClass wc = GetWaterClass(tile_cur);
2952
2953 bool add_cost = !IsWaterTile(tile_cur);
2954 ret = Command<Commands::LandscapeClear>::Do(flags, tile_cur);
2955 if (ret.Failed()) return ret;
2956 if (add_cost) cost.AddCost(ret.GetCost());
2957
2958 tile_cur += TileOffsByDiagDir(direction);
2959 if (!IsTileType(tile_cur, TileType::Water) || !IsTileFlat(tile_cur)) {
2960 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2961 }
2962
2963 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2964 _dock_w_chk[direction], _dock_h_chk[direction]);
2965
2966 /* middle */
2967 Station *st = nullptr;
2968 ret = FindJoiningStation(StationID::Invalid(), station_to_join, adjacent, dock_area, &st);
2969 if (ret.Failed()) return ret;
2970
2971 /* Distant join */
2972 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2973
2974 ret = BuildStationPart(&st, flags, reuse, dock_area, StationNaming::Dock);
2975 if (ret.Failed()) return ret;
2976
2977 if (flags.Test(DoCommandFlag::Execute)) {
2978 st->ship_station.Add(tile);
2979 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2980 st->ship_station.Add(flat_tile);
2982
2983 st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
2984
2985 /* If the water part of the dock is on a canal, update infrastructure counts.
2986 * This is needed as we've cleared that tile before.
2987 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2988 * See: MakeWaterKeepingClass() */
2989 if (wc == WaterClass::Canal && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WaterClass::Canal && IsTileOwner(flat_tile, _current_company))) {
2990 Company::Get(st->owner)->infrastructure.water++;
2991 }
2992 Company::Get(st->owner)->infrastructure.station += 2;
2993
2994 MakeDock(tile, st->owner, st->index, direction, wc);
2995 UpdateStationDockingTiles(st);
2996
2998 }
2999
3000 return cost;
3001}
3002
3003void RemoveDockingTile(TileIndex t)
3004{
3005 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
3006 TileIndex tile = t + TileOffsByDiagDir(d);
3007 if (!IsValidTile(tile)) continue;
3008
3009 if (IsTileType(tile, TileType::Station)) {
3010 Station *st = Station::GetByTile(tile);
3011 if (st != nullptr) UpdateStationDockingTiles(st);
3012 } else if (IsTileType(tile, TileType::Industry)) {
3014 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
3015 }
3016 }
3017}
3018
3025{
3026 assert(IsValidTile(tile));
3027
3028 /* Clear and maybe re-set docking tile */
3029 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
3030 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
3031 if (!IsValidTile(docking_tile)) continue;
3032
3033 if (IsPossibleDockingTile(docking_tile)) {
3034 SetDockingTile(docking_tile, false);
3035 CheckForDockingTile(docking_tile);
3036 }
3037 }
3038}
3039
3046{
3047 assert(IsDockTile(t));
3048
3049 StationGfx gfx = GetStationGfx(t);
3050 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
3051
3052 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
3053 TileIndex tile = t + TileOffsByDiagDir(d);
3054 if (!IsValidTile(tile)) continue;
3055 if (!IsDockTile(tile)) continue;
3056 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
3057 }
3058
3059 return INVALID_TILE;
3060}
3061
3068static CommandCost RemoveDock(TileIndex tile, DoCommandFlags flags)
3069{
3070 Station *st = Station::GetByTile(tile);
3071 CommandCost ret = CheckOwnership(st->owner);
3072 if (ret.Failed()) return ret;
3073
3074 if (!IsDockTile(tile)) return CMD_ERROR;
3075
3076 TileIndex tile1 = FindDockLandPart(tile);
3077 if (tile1 == INVALID_TILE) return CMD_ERROR;
3078 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
3079
3080 ret = EnsureNoVehicleOnGround(tile1);
3081 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
3082 if (ret.Failed()) return ret;
3083
3084 if (flags.Test(DoCommandFlag::Execute)) {
3085 DoClearSquare(tile1);
3086 MarkTileDirtyByTile(tile1);
3087 MakeWaterKeepingClass(tile2, st->owner);
3088
3089 st->rect.AfterRemoveTile(st, tile1);
3090 st->rect.AfterRemoveTile(st, tile2);
3091
3092 MakeShipStationAreaSmaller(st);
3093 if (st->ship_station.tile == INVALID_TILE) {
3094 st->ship_station.Clear();
3095 st->docking_station.Clear();
3098 }
3099
3100 Company::Get(st->owner)->infrastructure.station -= 2;
3101
3103
3106
3107 for (Ship *s : Ship::Iterate()) {
3108 /* Find all ships going to our dock. */
3109 if (s->current_order.GetDestination() != st->index) {
3110 continue;
3111 }
3112
3113 /* Find ships that are marked as "loading" but are no longer on a
3114 * docking tile. Force them to leave the station (as they were loading
3115 * on the removed dock). */
3116 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
3117 s->LeaveStation();
3118 }
3119
3120 /* If we no longer have a dock, mark the order as invalid and send
3121 * the ship to the next order (or, if there is none, make it
3122 * wander the world). */
3123 if (s->current_order.IsType(OT_GOTO_STATION) && !st->facilities.Test(StationFacility::Dock)) {
3124 s->SetDestTile(s->GetOrderStationLocation(st->index));
3125 }
3126 }
3127 }
3128
3130}
3131
3139{
3140 const auto &layouts = _station_display_datas[to_underlying(st)];
3141 if (gfx >= layouts.size()) gfx &= 1;
3142 return layouts.data() + gfx;
3143}
3144
3154bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3155{
3156 bool snow_desert;
3157 switch (*ground) {
3158 case SPR_RAIL_TRACK_X:
3159 case SPR_MONO_TRACK_X:
3160 case SPR_MGLV_TRACK_X:
3161 snow_desert = false;
3162 *overlay_offset = RTO_X;
3163 break;
3164
3165 case SPR_RAIL_TRACK_Y:
3166 case SPR_MONO_TRACK_Y:
3167 case SPR_MGLV_TRACK_Y:
3168 snow_desert = false;
3169 *overlay_offset = RTO_Y;
3170 break;
3171
3172 case SPR_RAIL_TRACK_X_SNOW:
3173 case SPR_MONO_TRACK_X_SNOW:
3174 case SPR_MGLV_TRACK_X_SNOW:
3175 snow_desert = true;
3176 *overlay_offset = RTO_X;
3177 break;
3178
3179 case SPR_RAIL_TRACK_Y_SNOW:
3180 case SPR_MONO_TRACK_Y_SNOW:
3181 case SPR_MGLV_TRACK_Y_SNOW:
3182 snow_desert = true;
3183 *overlay_offset = RTO_Y;
3184 break;
3185
3186 default:
3187 return false;
3188 }
3189
3190 if (ti != nullptr) {
3191 /* Decide snow/desert from tile */
3192 switch (_settings_game.game_creation.landscape) {
3194 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3195 break;
3196
3198 snow_desert = GetTropicZone(ti->tile) == TROPICZONE_DESERT;
3199 break;
3200
3201 default:
3202 break;
3203 }
3204 }
3205
3206 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3207 return true;
3208}
3209
3216static BridgePillarFlags GetStationBlockedPillars(std::span<const BridgeableTileInfo> bridgeable_info, uint8_t layout)
3217{
3218 if (layout < std::size(bridgeable_info)) return bridgeable_info[layout].disallowed_pillars;
3219 return BRIDGEPILLARFLAGS_ALL;
3220}
3221
3231{
3232 if (statspec == nullptr || !statspec->flags.Test(StationSpecFlag::CustomFoundations)) return false;
3233
3234 /* Station has custom foundations.
3235 * Check whether the foundation continues beyond the tile's upper sides. */
3236 uint edge_info = 0;
3237 auto [slope, z] = GetFoundationPixelSlope(ti->tile);
3238 if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
3239 if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
3240
3241 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, gfx, edge_info);
3242 if (image == 0) return false;
3243
3245 /* Station provides extended foundations. */
3246 static constexpr uint8_t foundation_parts[] = {
3247 UINT8_MAX, UINT8_MAX, UINT8_MAX, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3248 UINT8_MAX, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3249 UINT8_MAX, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3250 7, 8, 9, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3251 };
3252 assert(ti->tileh < std::size(foundation_parts));
3253 if (foundation_parts[ti->tileh] == UINT8_MAX) return false;
3254
3255 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3256 } else {
3257 /* Draw simple foundations, built up from 8 possible foundation sprites.
3258 * Each set bit represents one of the eight composite sprites to be drawn.
3259 * 'Invalid' entries will not drawn but are included for completeness. */
3260 static constexpr uint8_t composite_foundation_parts[] = {
3261 0b0000'0000, 0b1101'0001, 0b1110'0100, 0b1110'0000, // Invalid, Invalid, Invalid, SLOPE_SW
3262 0b1100'1010, 0b1100'1001, 0b1100'0100, 0b1100'0000, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3263 0b1101'0010, 0b1001'0001, 0b1110'0100, 0b1010'0000, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3264 0b0100'1010, 0b0000'1001, 0b0100'0100, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3265 };
3266 assert(ti->tileh < std::size(composite_foundation_parts));
3267
3268 uint8_t parts = composite_foundation_parts[ti->tileh];
3269
3270 /* If foundations continue beyond the tile's upper sides then mask out the last two pieces. */
3271 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3272 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3273
3274 /* We always have to draw at least one sprite, so if we have no parts to draw fall back to default foundation. */
3275 if (parts == 0) return false;
3276
3278 for (uint i : SetBitIterator(parts)) {
3279 AddSortableSpriteToDraw(image + i, PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3280 }
3282 }
3283
3284 OffsetGroundSprite(0, -static_cast<int>(TILE_HEIGHT));
3286
3287 return true;
3288}
3289
3292{
3293 const NewGRFSpriteLayout *layout = nullptr;
3294 SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_layout and t
3295 DrawTileSpriteSpan tmp_layout;
3296 const DrawTileSprites *t = nullptr;
3297 int32_t total_offset;
3298 const RailTypeInfo *rti = nullptr;
3299 uint32_t relocation = 0;
3300 uint32_t ground_relocation = 0;
3301 BaseStation *st = nullptr;
3302 const StationSpec *statspec = nullptr;
3303 uint tile_layout = 0;
3304 auto bridgeable_info = GetStationBridgeableTileInfo(GetStationType(ti->tile));
3305
3306 if (HasStationRail(ti->tile)) {
3307 rti = GetRailTypeInfo(GetRailType(ti->tile));
3308 total_offset = rti->GetRailtypeSpriteOffset();
3309
3310 if (IsCustomStationSpecIndex(ti->tile)) {
3311 /* look for customization */
3312 st = BaseStation::GetByTile(ti->tile);
3313 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3314
3315 if (statspec != nullptr) {
3316 tile_layout = GetStationGfx(ti->tile);
3317
3319 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3320 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
3321 }
3322
3323 /* Ensure the chosen tile layout is valid for this custom station */
3324 if (!statspec->renderdata.empty()) {
3325 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3326 if (!layout->NeedsPreprocessing()) {
3327 t = layout;
3328 layout = nullptr;
3329 }
3330 }
3331 }
3332 }
3333 if (statspec != nullptr) bridgeable_info = statspec->bridgeable_info;
3334 } else {
3335 total_offset = 0;
3336 }
3337
3338 StationGfx gfx = GetStationGfx(ti->tile);
3339 if (IsAirport(ti->tile)) {
3340 gfx = GetAirportGfx(ti->tile);
3341 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3342 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3343 if (ats->grf_prop.HasSpriteGroups() && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3344 return;
3345 }
3346 /* No sprite group (or no valid one) found, meaning no graphics associated.
3347 * Use the substitute one instead */
3348 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3349 gfx = ats->grf_prop.subst_id;
3350 }
3351 switch (gfx) {
3352 case APT_RADAR_GRASS_FENCE_SW:
3353 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3354 break;
3355 case APT_GRASS_FENCE_NE_FLAG:
3356 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3357 break;
3358 case APT_RADAR_FENCE_SW:
3359 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3360 break;
3361 case APT_RADAR_FENCE_NE:
3362 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3363 break;
3364 case APT_GRASS_FENCE_NE_FLAG_2:
3365 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3366 break;
3367 }
3368 }
3369
3370 Owner owner = GetTileOwner(ti->tile);
3371
3372 PaletteID palette;
3373 if (Company::IsValidID(owner)) {
3374 palette = GetCompanyPalette(owner);
3375 } else {
3376 /* Some stations are not owner by a company, namely oil rigs */
3377 palette = PALETTE_TO_GREY;
3378 }
3379
3380 if (layout == nullptr && t == nullptr) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3381
3382 /* don't show foundation for docks */
3383 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3384 if (!DrawCustomStationFoundations(statspec, st, ti, tile_layout)) {
3386 }
3387 }
3388
3389 bool draw_ground = false;
3390
3391 if (IsBuoy(ti->tile)) {
3392 DrawWaterClassGround(ti);
3393 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
3394 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3395 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3396 if (ti->tileh == SLOPE_FLAT) {
3397 DrawWaterClassGround(ti);
3398 } else {
3399 assert(IsDock(ti->tile));
3400 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3401 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WaterClass::Invalid;
3402 if (wc == WaterClass::Sea) {
3403 DrawShoreTile(ti->tileh);
3404 } else {
3405 DrawClearLandTile(ti, 3);
3406 }
3407 }
3408 } else if (IsRoadWaypointTile(ti->tile)) {
3410 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3411 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3412 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3413 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3414 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3415 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3416
3417 if (ti->tileh != SLOPE_FLAT) {
3419 }
3420
3421 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3422 } else {
3423 if (layout != nullptr) {
3424 /* Sprite layout which needs preprocessing */
3425 bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround);
3426 processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3427 GetCustomStationRelocation(processor, statspec, st, ti->tile);
3428 tmp_layout = processor.GetLayout();
3429 t = &tmp_layout;
3430 total_offset = 0;
3431 } else if (statspec != nullptr) {
3432 /* Simple sprite layout */
3433 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3435 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3436 }
3437 ground_relocation += rti->fallback_railtype;
3438 }
3439
3440 draw_ground = true;
3441 }
3442
3443 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3444 SpriteID image = t->ground.sprite;
3445 PaletteID pal = t->ground.pal;
3446 RailTrackOffset overlay_offset;
3447 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3448 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
3449 DrawGroundSprite(image, PAL_NONE);
3450 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3451
3452 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3453 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
3454 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3455 }
3456 } else {
3457 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3458 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3459 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3460
3461 /* PBS debugging, draw reserved tracks darker */
3462 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3464 }
3465 }
3466 }
3467
3469
3470 if (IsAnyRoadStop(ti->tile)) {
3471 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3472 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3473 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3474 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3475
3476 StationGfx view = GetStationGfx(ti->tile);
3477 StationType type = GetStationType(ti->tile);
3478
3479 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3480 RoadStopDrawModes stop_draw_mode{};
3481 if (stopspec != nullptr) {
3482 stop_draw_mode = stopspec->draw_mode;
3483 st = BaseStation::GetByTile(ti->tile);
3484 std::array<int32_t, 1> regs100;
3485 auto result = GetRoadStopLayout(ti, stopspec, st, type, view, regs100);
3486 if (result.has_value()) {
3487 if (stopspec->flags.Test(RoadStopSpecFlag::DrawModeRegister)) {
3488 stop_draw_mode = static_cast<RoadStopDrawModes>(regs100[0]);
3489 }
3490 if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) {
3491 draw_ground = true;
3492 }
3493 processor = std::move(*result);
3494 tmp_layout = processor.GetLayout();
3495 t = &tmp_layout;
3496 }
3497 }
3498
3499 /* Draw ground sprite */
3500 if (draw_ground) {
3501 SpriteID image = t->ground.sprite;
3502 PaletteID pal = t->ground.pal;
3503 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3504 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3505 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3506 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3507 }
3508 }
3509
3510 if (IsDriveThroughStopTile(ti->tile)) {
3511 if (type != StationType::RoadWaypoint && (stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Overlay))) {
3512 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
3513 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3514 }
3515 } else {
3516 /* Non-drivethrough road stops are only valid for roads. */
3517 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3518
3519 if ((stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Road)) && road_rti->UsesOverlay()) {
3520 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
3521 DrawGroundSprite(ground + view, PAL_NONE);
3522 }
3523 }
3524 if (stopspec != nullptr) bridgeable_info = stopspec->bridgeable_info;
3525
3526 if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
3527 /* Draw road, tram catenary */
3528 DrawRoadCatenary(ti);
3529 }
3530 }
3531
3532 if (IsRailWaypoint(ti->tile)) {
3533 /* Don't offset the waypoint graphics; they're always the same. */
3534 total_offset = 0;
3535 }
3536
3537 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
3538 DrawBridgeMiddle(ti, GetStationBlockedPillars(bridgeable_info, GetStationGfx(ti->tile)));
3539}
3540
3541void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3542{
3543 int32_t total_offset = 0;
3545 const DrawTileSprites *t = GetStationTileLayout(st, image);
3546 const RailTypeInfo *railtype_info = nullptr;
3547
3548 if (railtype != INVALID_RAILTYPE) {
3549 railtype_info = GetRailTypeInfo(railtype);
3550 total_offset = railtype_info->GetRailtypeSpriteOffset();
3551 }
3552
3553 SpriteID img = t->ground.sprite;
3554 RailTrackOffset overlay_offset;
3555 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3556 SpriteID ground = GetCustomRailSprite(railtype_info, INVALID_TILE, RTSG_GROUND);
3557 DrawSprite(img, PAL_NONE, x, y);
3558 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3559 } else {
3560 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3561 }
3562
3563 if (roadtype != INVALID_ROADTYPE) {
3564 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3565 if (image >= 4) {
3566 /* Drive-through stop */
3567 uint sprite_offset = 5 - image;
3568
3569 /* Road underlay takes precedence over tram */
3570 if (roadtype_info->UsesOverlay()) {
3571 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_GROUND);
3572 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3573
3574 SpriteID overlay = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_OVERLAY);
3575 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3576 } else if (RoadTypeIsTram(roadtype)) {
3577 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3578 }
3579 } else {
3580 /* Bay stop */
3581 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3582 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_ROADSTOP);
3583 DrawSprite(ground + image, PAL_NONE, x, y);
3584 }
3585 }
3586 }
3587
3588 /* Default waypoint has no railtype specific sprites */
3589 DrawRailTileSeqInGUI(x, y, t, (st == StationType::RailWaypoint || st == StationType::RoadWaypoint) ? 0 : total_offset, 0, pal);
3590}
3591
3592
3593static void FillTileDescRoadStop(TileIndex tile, TileDesc &td)
3594{
3595 RoadType road_rt = GetRoadTypeRoad(tile);
3596 RoadType tram_rt = GetRoadTypeTram(tile);
3597 Owner road_owner = INVALID_OWNER;
3598 Owner tram_owner = INVALID_OWNER;
3599 if (road_rt != INVALID_ROADTYPE) {
3600 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3601 td.roadtype = rti->strings.name;
3602 td.road_speed = rti->max_speed / 2;
3603 road_owner = GetRoadOwner(tile, RTT_ROAD);
3604 }
3605
3606 if (tram_rt != INVALID_ROADTYPE) {
3607 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3608 td.tramtype = rti->strings.name;
3609 td.tram_speed = rti->max_speed / 2;
3610 tram_owner = GetRoadOwner(tile, RTT_TRAM);
3611 }
3612
3613 if (IsDriveThroughStopTile(tile)) {
3614 /* Is there a mix of owners? */
3615 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
3616 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
3617 uint i = 1;
3618 if (road_owner != INVALID_OWNER) {
3619 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3620 td.owner[i] = road_owner;
3621 i++;
3622 }
3623 if (tram_owner != INVALID_OWNER) {
3624 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3625 td.owner[i] = tram_owner;
3626 }
3627 }
3628 }
3629}
3630
3631void FillTileDescRailStation(TileIndex tile, TileDesc &td)
3632{
3633 const StationSpec *spec = GetStationSpec(tile);
3634
3635 if (spec != nullptr) {
3637 td.station_name = spec->name;
3638
3639 if (spec->grf_prop.HasGrfFile()) {
3640 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3641 td.grf = gc->GetName();
3642 }
3643 }
3644
3645 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3646 td.rail_speed = rti->max_speed;
3647 td.railtype = rti->strings.name;
3648}
3649
3650void FillTileDescAirport(TileIndex tile, TileDesc &td)
3651{
3652 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3654 td.airport_name = as->name;
3655
3656 const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
3657 td.airport_tile_name = ats->name;
3658
3659 if (as->grf_prop.HasGrfFile()) {
3660 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3661 td.grf = gc->GetName();
3662 } else if (ats->grf_prop.HasGrfFile()) {
3663 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3664 td.grf = gc->GetName();
3665 }
3666}
3667
3670{
3671 td.owner[0] = GetTileOwner(tile);
3673
3674 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3675 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3676 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3677
3678 StringID str;
3679 switch (GetStationType(tile)) {
3680 default: NOT_REACHED();
3681 case StationType::Rail: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3683 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3684 break;
3685 case StationType::Truck: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3686 case StationType::Bus: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3687 case StationType::Oilrig: {
3688 const Industry *i = Station::GetByTile(tile)->industry;
3689 const IndustrySpec *is = GetIndustrySpec(i->type);
3690 td.owner[0] = i->owner;
3691 str = is->name;
3692 if (is->grf_prop.HasGrfFile()) td.grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3693 break;
3694 }
3695 case StationType::Dock: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3696 case StationType::Buoy: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3697 case StationType::RailWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3698 case StationType::RoadWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3699 }
3700 td.str = str;
3701}
3702
3703
3705static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
3706{
3707 TrackBits trackbits = TRACK_BIT_NONE;
3708
3709 switch (mode) {
3710 case TRANSPORT_RAIL:
3711 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3712 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
3713 }
3714 break;
3715
3716 case TRANSPORT_WATER:
3717 /* buoy is coded as a station, it is always on open water */
3718 if (IsBuoy(tile)) {
3719 trackbits = TRACK_BIT_ALL;
3720 /* remove tracks that connect NE map edge */
3721 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
3722 /* remove tracks that connect NW map edge */
3723 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
3724 }
3725 break;
3726
3727 case TRANSPORT_ROAD:
3728 if (IsAnyRoadStop(tile)) {
3729 RoadTramType rtt = (RoadTramType)sub_mode;
3730 if (!HasTileRoadType(tile, rtt)) break;
3731
3732 if (IsBayRoadStopTile(tile)) {
3733 DiagDirection dir = GetBayRoadStopDir(tile);
3734 if (side != INVALID_DIAGDIR && dir != side) break;
3735 trackbits = DiagDirToDiagTrackBits(dir);
3736 } else {
3737 Axis axis = GetDriveThroughStopAxis(tile);
3738 if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
3739 trackbits = AxisToTrackBits(axis);
3740 }
3741 }
3742 break;
3743
3744 default:
3745 break;
3746 }
3747
3749}
3750
3751
3754{
3755 auto *st = BaseStation::GetByTile(tile);
3756 switch (GetStationType(tile)) {
3758 TriggerAirportTileAnimation(Station::From(st), tile, AirportAnimationTrigger::TileLoop);
3759 break;
3760
3761 case StationType::Rail:
3763 TriggerStationAnimation(st, tile, StationAnimationTrigger::TileLoop);
3764 break;
3765
3766 case StationType::Dock:
3767 if (!IsTileFlat(tile)) break; // only handle water part
3768 [[fallthrough]];
3769
3770 case StationType::Oilrig: //(station part)
3771 case StationType::Buoy:
3772 TileLoop_Water(tile);
3773 break;
3774
3775 case StationType::Truck:
3776 case StationType::Bus:
3777 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3778 break;
3779
3781 switch (_settings_game.game_creation.landscape) {
3783 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3785 MarkTileDirtyByTile(tile);
3786 }
3787 break;
3788
3792 MarkTileDirtyByTile(tile);
3793 }
3794 break;
3795
3796 default: break;
3797 }
3798
3799 HouseZone new_zone = HouseZone::TownEdge;
3800 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3801 if (t != nullptr) {
3802 new_zone = GetTownRadiusGroup(t, tile);
3803 }
3804
3805 /* Adjust road ground type depending on 'new_zone' */
3807 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3808
3809 if (new_rs != cur_rs) {
3810 SetRoadWaypointRoadside(tile, cur_rs == Roadside::Barren ? new_rs : Roadside::Barren);
3811 MarkTileDirtyByTile(tile);
3812 }
3813
3814 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3815 break;
3816 }
3817
3818 default: break;
3819 }
3820}
3821
3822
3825{
3826 if (HasStationRail(tile)) {
3827 AnimateStationTile(tile);
3828 return;
3829 }
3830
3831 if (IsAirport(tile)) {
3832 AnimateAirportTile(tile);
3833 return;
3834 }
3835
3836 if (IsAnyRoadStopTile(tile)) {
3837 AnimateRoadStopTile(tile);
3838 return;
3839 }
3840}
3841
3842
3845{
3846 const BaseStation *bst = BaseStation::GetByTile(tile);
3847
3850 } else if (IsHangar(tile)) {
3851 const Station *st = Station::From(bst);
3853 } else {
3854 ShowStationViewWindow(bst->index);
3855 }
3856 return true;
3857}
3858
3860static VehicleEnterTileStates VehicleEnterTile_Station(Vehicle *v, TileIndex tile, int x, int y)
3861{
3862 if (v->type == VEH_TRAIN) {
3863 StationID station_id = GetStationIndex(tile);
3864 if (!v->current_order.ShouldStopAtStation(v, station_id)) return {};
3865 if (!IsRailStation(tile) || !v->IsFrontEngine()) return {};
3866
3867 int station_ahead;
3868 int station_length;
3869 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3870
3871 /* Stop whenever that amount of station ahead + the distance from the
3872 * begin of the platform to the stop location is longer than the length
3873 * of the platform. Station ahead 'includes' the current tile where the
3874 * vehicle is on, so we need to subtract that. */
3875 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return {};
3876
3878
3879 x &= 0xF;
3880 y &= 0xF;
3881
3882 if (DiagDirToAxis(dir) != AXIS_X) std::swap(x, y);
3883 if (y == TILE_SIZE / 2) {
3884 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
3885 stop &= TILE_SIZE - 1;
3886
3887 if (x == stop) {
3888 return VehicleEnterTileState::EnteredStation; // enter station
3889 } else if (x < stop) {
3891 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3892 if (spd < v->cur_speed) v->cur_speed = spd;
3893 }
3894 }
3895 } else if (v->type == VEH_ROAD) {
3897 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
3898 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3899 /* Attempt to allocate a parking bay in a road stop */
3900 if (RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv)) return {};
3902 }
3903 }
3904 }
3905
3906 return {};
3907}
3908
3914{
3915 /* Collect cargoes accepted since the last big tick. */
3916 CargoTypes cargoes = 0;
3917 for (CargoType cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
3918 if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) SetBit(cargoes, cargo_type);
3919 }
3920
3921 /* Anything to do? */
3922 if (cargoes == 0) return;
3923
3924 /* Loop over all houses in the catchment. */
3926 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3927 if (IsTileType(tile, TileType::House)) {
3929 }
3930 }
3931}
3932
3940{
3941 if (!st->IsInUse()) {
3942 if (++st->delete_ctr >= 8) delete st;
3943 return false;
3944 }
3945
3946 if (Station::IsExpected(st)) {
3948
3949 for (GoodsEntry &ge : Station::From(st)->goods) {
3951 }
3952 }
3953
3954
3956
3957 return true;
3958}
3959
3960static inline void byte_inc_sat(uint8_t *p)
3961{
3962 uint8_t b = *p + 1;
3963 if (b != 0) *p = b;
3964}
3965
3972static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3973{
3974 /* If truncating also punish the source stations' ratings to
3975 * decrease the flow of incoming cargo. */
3976
3977 if (!ge->HasData()) return;
3978
3979 StationCargoAmountMap waiting_per_source;
3980 ge->GetData().cargo.Truncate(amount, &waiting_per_source);
3981 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3982 Station *source_station = Station::GetIfValid(i->first);
3983 if (source_station == nullptr) continue;
3984
3985 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3986 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3987 }
3988}
3989
3995{
3996 bool waiting_changed = false;
3997
3998 byte_inc_sat(&st->time_since_load);
3999 byte_inc_sat(&st->time_since_unload);
4000
4001 for (const CargoSpec *cs : CargoSpec::Iterate()) {
4002 GoodsEntry *ge = &st->goods[cs->Index()];
4003
4004 /* The station might not currently be moving this cargo. */
4005 if (!ge->HasRating()) {
4006 /* Slowly increase the rating back to its original level in the case we
4007 * didn't deliver cargo yet to this station. This happens when a bribe
4008 * failed while you didn't moved that cargo yet to a station. */
4009 if (ge->rating < INITIAL_STATION_RATING) ge->rating++;
4010
4011 /* Nothing else to do with this cargo. */
4012 continue;
4013 }
4014
4015 byte_inc_sat(&ge->time_since_pickup);
4016
4017 /* If this cargo hasn't been picked up in a long time, get rid of it. */
4018 if (ge->time_since_pickup == 255 && _settings_game.order.selectgoods) {
4020 ge->last_speed = 0;
4021 TruncateCargo(cs, ge);
4022 waiting_changed = true;
4023 continue;
4024 }
4025
4026 bool skip = false;
4027 int rating = 0;
4028 uint waiting = ge->AvailableCount();
4029
4030 /* num_dests is at least 1 if there is any cargo as
4031 * StationID::Invalid() is also a destination.
4032 */
4033 uint num_dests = ge->HasData() ? static_cast<uint>(ge->GetData().cargo.Packets()->MapSize()) : 0;
4034
4035 /* Average amount of cargo per next hop, but prefer solitary stations
4036 * with only one or two next hops. They are allowed to have more
4037 * cargo waiting per next hop.
4038 * With manual cargo distribution waiting_avg = waiting / 2 as then
4039 * StationID::Invalid() is the only destination.
4040 */
4041 uint waiting_avg = waiting / (num_dests + 1);
4042
4043 if (_cheats.station_rating.value) {
4044 ge->rating = rating = MAX_STATION_RATING;
4045 skip = true;
4046 } else if (cs->callback_mask.Test(CargoCallbackMask::StationRatingCalc)) {
4047 /* Perform custom station rating. If it succeeds the speed, days in transit and
4048 * waiting cargo ratings must not be executed. */
4049
4050 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
4051 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
4052
4053 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
4055 | (ClampTo<uint8_t>(last_speed) << 24);
4056 /* Convert to the 'old' vehicle types */
4057 uint32_t var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
4058 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
4059 if (callback != CALLBACK_FAILED) {
4060 skip = true;
4061 rating = GB(callback, 0, 14);
4062
4063 /* Simulate a 15 bit signed value */
4064 if (HasBit(callback, 14)) rating -= 0x4000;
4065 }
4066 }
4067
4068 if (!skip) {
4069 int b = ge->last_speed - 85;
4070 if (b >= 0) rating += b >> 2;
4071
4072 uint8_t waittime = ge->time_since_pickup;
4073 if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
4074 if (waittime <= 21) rating += 25;
4075 if (waittime <= 12) rating += 25;
4076 if (waittime <= 6) rating += 45;
4077 if (waittime <= 3) rating += 35;
4078
4079 rating -= 90;
4080 if (ge->max_waiting_cargo <= 1500) rating += 55;
4081 if (ge->max_waiting_cargo <= 1000) rating += 35;
4082 if (ge->max_waiting_cargo <= 600) rating += 10;
4083 if (ge->max_waiting_cargo <= 300) rating += 20;
4084 if (ge->max_waiting_cargo <= 100) rating += 10;
4085 }
4086
4087 if (Company::IsValidID(st->owner) && st->town->statues.Test(st->owner)) rating += 26;
4088
4089 uint8_t age = ge->last_age;
4090 if (age < 3) rating += 10;
4091 if (age < 2) rating += 10;
4092 if (age < 1) rating += 13;
4093
4094 {
4095 int or_ = ge->rating; // old rating
4096
4097 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
4098 ge->rating = rating = ClampTo<uint8_t>(or_ + Clamp(rating - or_, -2, 2));
4099
4100 /* if rating is <= 64 and more than 100 items waiting on average per destination,
4101 * remove some random amount of goods from the station */
4102 if (rating <= 64 && waiting_avg >= 100) {
4103 int dec = Random() & 0x1F;
4104 if (waiting_avg < 200) dec &= 7;
4105 waiting -= (dec + 1) * num_dests;
4106 waiting_changed = true;
4107 }
4108
4109 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
4110 if (rating <= 127 && waiting != 0) {
4111 uint32_t r = Random();
4112 if (rating <= (int)GB(r, 0, 7)) {
4113 /* Need to have int, otherwise it will just overflow etc. */
4114 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) + 1) * num_dests), 0);
4115 waiting_changed = true;
4116 }
4117 }
4118
4119 /* At some point we really must cap the cargo. Previously this
4120 * was a strict 4095, but now we'll have a less strict, but
4121 * increasingly aggressive truncation of the amount of cargo. */
4122 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
4123 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
4124 static const uint MAX_WAITING_CARGO = 1 << 15;
4125
4126 if (waiting > WAITING_CARGO_THRESHOLD) {
4127 uint difference = waiting - WAITING_CARGO_THRESHOLD;
4128 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
4129
4130 waiting = std::min(waiting, MAX_WAITING_CARGO);
4131 waiting_changed = true;
4132 }
4133
4134 /* We can't truncate cargo that's already reserved for loading.
4135 * Thus StoredCount() here. */
4136 if (waiting_changed && waiting < ge->AvailableCount()) {
4137 /* Feed back the exact own waiting cargo at this station for the
4138 * next rating calculation. */
4139 ge->max_waiting_cargo = 0;
4140
4141 TruncateCargo(cs, ge, ge->AvailableCount() - waiting);
4142 } else {
4143 /* If the average number per next hop is low, be more forgiving. */
4144 ge->max_waiting_cargo = waiting_avg;
4145 }
4146 }
4147 }
4148
4149 StationID index = st->index;
4150 if (waiting_changed) {
4151 SetWindowDirty(WC_STATION_VIEW, index); // update whole window
4152 } else {
4153 SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
4154 }
4155}
4156
4165void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
4166{
4167 GoodsEntry &ge = st->goods[cargo];
4168
4169 /* Reroute cargo in station. */
4170 if (ge.HasData()) ge.GetData().cargo.Reroute(UINT_MAX, &ge.GetData().cargo, avoid, avoid2, &ge);
4171
4172 /* Reroute cargo staged to be transferred. */
4173 for (Vehicle *v : st->loading_vehicles) {
4174 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4175 if (u->cargo_type != cargo) continue;
4176 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4177 }
4178 }
4179}
4180
4190{
4191 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
4192 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DistributionType::Manual);
4193 GoodsEntry &ge = from->goods[cargo];
4195 if (lg == nullptr) continue;
4196 std::vector<NodeID> to_remove{};
4197 for (Edge &edge : (*lg)[ge.node].edges) {
4198 Station *to = Station::Get((*lg)[edge.dest_node].station);
4199 assert(to->goods[cargo].node == edge.dest_node);
4200 assert(TimerGameEconomy::date >= edge.LastUpdate());
4201 auto timeout = TimerGameEconomy::Date(LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3));
4202 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4203 bool updated = false;
4204
4205 if (auto_distributed) {
4206 /* Have all vehicles refresh their next hops before deciding to
4207 * remove the node. */
4208 std::vector<Vehicle *> vehicles;
4209 for (const OrderList *l : OrderList::Iterate()) {
4210 bool found_from = false;
4211 bool found_to = false;
4212 for (const Order &order : l->GetOrders()) {
4213 if (!order.IsType(OT_GOTO_STATION) && !order.IsType(OT_IMPLICIT)) continue;
4214 if (order.GetDestination() == from->index) {
4215 found_from = true;
4216 if (found_to) break;
4217 } else if (order.GetDestination() == to->index) {
4218 found_to = true;
4219 if (found_from) break;
4220 }
4221 }
4222 if (!found_to || !found_from) continue;
4223 vehicles.push_back(l->GetFirstSharedVehicle());
4224 }
4225
4226 auto iter = vehicles.begin();
4227 while (iter != vehicles.end()) {
4228 Vehicle *v = *iter;
4229 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4231 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4232 }
4233 if (edge.LastUpdate() == TimerGameEconomy::date) {
4234 updated = true;
4235 break;
4236 }
4237
4238 Vehicle *next_shared = v->NextShared();
4239 if (next_shared) {
4240 *iter = next_shared;
4241 ++iter;
4242 } else {
4243 iter = vehicles.erase(iter);
4244 }
4245
4246 if (iter == vehicles.end()) iter = vehicles.begin();
4247 }
4248 }
4249
4250 if (!updated) {
4251 /* If it's still considered dead remove it. */
4252 to_remove.emplace_back(to->goods[cargo].node);
4253 if (ge.HasData()) ge.GetData().flows.DeleteFlows(to->index);
4254 RerouteCargo(from, cargo, to->index, from->index);
4255 }
4256 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4257 edge.Restrict();
4258 if (ge.HasData()) ge.GetData().flows.RestrictFlows(to->index);
4259 RerouteCargo(from, cargo, to->index, from->index);
4260 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4261 edge.Release();
4262 }
4263 }
4264 /* Remove dead edges. */
4265 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4266
4267 assert(TimerGameEconomy::date >= lg->LastCompression());
4269 lg->Compress();
4270 }
4271 }
4272}
4273
4284void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
4285{
4286 GoodsEntry &ge1 = st->goods[cargo];
4287 Station *st2 = Station::Get(next_station_id);
4288 GoodsEntry &ge2 = st2->goods[cargo];
4289 LinkGraph *lg = nullptr;
4290 if (ge1.link_graph == LinkGraphID::Invalid()) {
4291 if (ge2.link_graph == LinkGraphID::Invalid()) {
4293 lg = LinkGraph::Create(cargo);
4295 ge2.link_graph = lg->index;
4296 ge2.node = lg->AddNode(st2);
4297 } else {
4298 Debug(misc, 0, "Can't allocate link graph");
4299 }
4300 } else {
4301 lg = LinkGraph::Get(ge2.link_graph);
4302 }
4303 if (lg) {
4304 ge1.link_graph = lg->index;
4305 ge1.node = lg->AddNode(st);
4306 }
4307 } else if (ge2.link_graph == LinkGraphID::Invalid()) {
4308 lg = LinkGraph::Get(ge1.link_graph);
4309 ge2.link_graph = lg->index;
4310 ge2.node = lg->AddNode(st2);
4311 } else {
4312 lg = LinkGraph::Get(ge1.link_graph);
4313 if (ge1.link_graph != ge2.link_graph) {
4315 if (lg->Size() < lg2->Size()) {
4316 LinkGraphSchedule::instance.Dequeue(lg);
4317 lg2->Merge(lg); // Updates GoodsEntries of lg
4318 lg = lg2;
4319 } else {
4320 LinkGraphSchedule::instance.Dequeue(lg2);
4321 lg->Merge(lg2); // Updates GoodsEntries of lg2
4322 }
4323 }
4324 }
4325 if (lg != nullptr) {
4326 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, modes);
4327 }
4328}
4329
4337void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4338{
4339 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4340 if (v->refit_cap > 0) {
4341 /* The cargo count can indeed be higher than the refit_cap if
4342 * wagons have been auto-replaced and subsequently auto-
4343 * refitted to a higher capacity. The cargo gets redistributed
4344 * among the wagons in that case.
4345 * As usage is not such an important figure anyway we just
4346 * ignore the additional cargo then.*/
4347 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4348 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EdgeUpdateMode::Increase);
4349 }
4350 }
4351}
4352
4353/* called for every station each tick */
4354static void StationHandleSmallTick(BaseStation *st)
4355{
4356 if (st->facilities.Test(StationFacility::Waypoint) || !st->IsInUse()) return;
4357
4358 uint8_t b = st->delete_ctr + 1;
4359 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4360 st->delete_ctr = b;
4361
4362 if (b == 0) UpdateStationRating(Station::From(st));
4363}
4364
4365void OnTick_Station()
4366{
4367 if (_game_mode == GM_EDITOR) return;
4368
4369 for (BaseStation *st : BaseStation::Iterate()) {
4370 StationHandleSmallTick(st);
4371
4372 /* Clean up the link graph about once a week. */
4375 };
4376
4377 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4379 /* Stop processing this station if it was deleted */
4380 if (!StationHandleBigTick(st)) continue;
4381 }
4382
4383 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4385 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4386 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4387 if (Station::IsExpected(st)) TriggerAirportAnimation(Station::From(st), AirportAnimationTrigger::AcceptanceTick);
4388 }
4389 }
4390}
4391
4393static const IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::Trigger::Month, TimerGameEconomy::Priority::Station}, [](auto)
4394{
4395 for (Station *st : Station::Iterate()) {
4396 for (GoodsEntry &ge : st->goods) {
4399 }
4400 }
4401});
4402
4411void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4412{
4413 ForAllStationsRadius(tile, radius, [&](Station *st) {
4414 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4415 for (GoodsEntry &ge : st->goods) {
4416 if (ge.status.Any()) {
4417 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4418 }
4419 }
4420 }
4421 });
4422}
4423
4424static uint UpdateStationWaiting(Station *st, CargoType cargo, uint amount, Source source)
4425{
4426 /* We can't allocate a CargoPacket? Then don't do anything
4427 * at all; i.e. just discard the incoming cargo. */
4428 if (!CargoPacket::CanAllocateItem()) return 0;
4429
4430 GoodsEntry &ge = st->goods[cargo];
4431 amount += ge.amount_fract;
4432 ge.amount_fract = GB(amount, 0, 8);
4433
4434 amount >>= 8;
4435 /* No new "real" cargo item yet. */
4436 if (amount == 0) return 0;
4437
4438 StationID next = ge.GetVia(st->index);
4439 ge.GetOrCreateData().cargo.Append(CargoPacket::Create(st->index, amount, source), next);
4440 LinkGraph *lg = nullptr;
4441 if (ge.link_graph == LinkGraphID::Invalid()) {
4442 if (LinkGraph::CanAllocateItem()) {
4443 lg = LinkGraph::Create(cargo);
4445 ge.link_graph = lg->index;
4446 ge.node = lg->AddNode(st);
4447 } else {
4448 Debug(misc, 0, "Can't allocate link graph");
4449 }
4450 } else {
4451 lg = LinkGraph::Get(ge.link_graph);
4452 }
4453 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4454
4455 if (!ge.HasRating()) {
4458 }
4459
4461 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4462 TriggerAirportAnimation(st, AirportAnimationTrigger::NewCargo, cargo);
4464 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4465
4466
4467 SetWindowDirty(WC_STATION_VIEW, st->index);
4468 st->MarkTilesDirty(true);
4469 return amount;
4470}
4471
4472static bool IsUniqueStationName(const std::string &name)
4473{
4474 for (const Station *st : Station::Iterate()) {
4475 if (!st->name.empty() && st->name == name) return false;
4476 }
4477
4478 return true;
4479}
4480
4488CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
4489{
4490 Station *st = Station::GetIfValid(station_id);
4491 if (st == nullptr) return CMD_ERROR;
4492
4493 CommandCost ret = CheckOwnership(st->owner);
4494 if (ret.Failed()) return ret;
4495
4496 bool reset = text.empty();
4497
4498 if (!reset) {
4500 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4501 }
4502
4503 if (flags.Test(DoCommandFlag::Execute)) {
4504 st->cached_name.clear();
4505 if (reset) {
4506 st->name.clear();
4507 } else {
4508 st->name = text;
4509 }
4510
4511 st->UpdateVirtCoord();
4513 }
4514
4515 return CommandCost();
4516}
4517
4525std::tuple<CommandCost, StationID> CmdMoveStationName(DoCommandFlags flags, StationID station_id, TileIndex tile)
4526{
4527 Station *st = Station::GetIfValid(station_id);
4528 if (st == nullptr) return { CMD_ERROR, StationID::Invalid() };
4529
4530 if (st->owner != OWNER_NONE) {
4531 CommandCost ret = CheckOwnership(st->owner);
4532 if (ret.Failed()) return { ret, StationID::Invalid() };
4533 }
4534
4535 const StationRect *r = &st->rect;
4536 if (!r->PtInExtendedRect(TileX(tile), TileY(tile))) {
4537 return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() };
4538 }
4539
4540 bool other_station = false;
4541 /* Check if the tile is the base tile of another station */
4542 ForAllStationsRadius(tile, 0, [&](BaseStation *s) {
4543 if (s != nullptr) {
4544 if (s != st && s->xy == tile) other_station = true;
4545 }
4546 });
4547 if (other_station) return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() };
4548
4549 if (flags.Test(DoCommandFlag::Execute)) {
4550 st->MoveSign(tile);
4551
4552 st->UpdateVirtCoord();
4553 }
4554 return { CommandCost(), station_id };
4555}
4556
4562void CcMoveStationName(Commands, const CommandCost &result, StationID station_id)
4563 {
4564 if (result.Failed()) return;
4565
4567 Station *st = Station::Get(station_id);
4568 SetViewportStationRect(st, false);
4569 }
4570
4571static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4572{
4573 for (Station *st : nearby) {
4574 if (st->TileIsInCatchment(tile)) stations.insert(st);
4575 }
4576}
4577
4583{
4584 if (this->tile != INVALID_TILE) {
4585 if (IsTileType(this->tile, TileType::House)) {
4586 /* Town nearby stations need to be filtered per tile. */
4587 assert(this->w == 1 && this->h == 1);
4588 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4589 } else {
4590 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4591 this->stations.insert(st);
4592 return true;
4593 });
4594 }
4595 this->tile = INVALID_TILE;
4596 }
4597 return this->stations;
4598}
4599
4600
4601static bool CanMoveGoodsToStation(const Station *st, CargoType cargo)
4602{
4603 /* Is the station reserved exclusively for somebody else? */
4604 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4605
4606 /* Lowest possible rating, better not to give cargo anymore. */
4607 if (st->goods[cargo].rating == 0) return false;
4608
4609 /* Selectively servicing stations, and not this one. */
4610 if (_settings_game.order.selectgoods && !st->goods[cargo].HasVehicleEverTriedLoading()) return false;
4611
4613 /* Passengers are never served by just a truck stop. */
4614 if (st->facilities == StationFacility::TruckStop) return false;
4615 } else {
4616 /* Non-passengers are never served by just a bus stop. */
4617 if (st->facilities == StationFacility::BusStop) return false;
4618 }
4619 return true;
4620}
4621
4622uint MoveGoodsToStation(CargoType cargo, uint amount, Source source, const StationList &all_stations, Owner exclusivity)
4623{
4624 /* Return if nothing to do. Also the rounding below fails for 0. */
4625 if (all_stations.empty()) return 0;
4626 if (amount == 0) return 0;
4627
4628 Station *first_station = nullptr;
4629 typedef std::pair<Station *, uint> StationInfo;
4630 std::vector<StationInfo> used_stations;
4631
4632 for (Station *st : all_stations) {
4633 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4634 if (!CanMoveGoodsToStation(st, cargo)) continue;
4635
4636 /* Avoid allocating a vector if there is only one station to significantly
4637 * improve performance in this common case. */
4638 if (first_station == nullptr) {
4639 first_station = st;
4640 continue;
4641 }
4642 if (used_stations.empty()) {
4643 used_stations.reserve(2);
4644 used_stations.emplace_back(first_station, 0);
4645 }
4646 used_stations.emplace_back(st, 0);
4647 }
4648
4649 /* no stations around at all? */
4650 if (first_station == nullptr) return 0;
4651
4652 if (used_stations.empty()) {
4653 /* only one station around */
4654 amount *= first_station->goods[cargo].rating + 1;
4655 return UpdateStationWaiting(first_station, cargo, amount, source);
4656 }
4657
4658 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_best = {}; // best rating for each company, including OWNER_NONE
4659 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_sum = {}; // sum of ratings for each company
4660 uint best_rating = 0;
4661 uint best_sum = 0; // sum of best ratings for each company
4662
4663 for (auto &p : used_stations) {
4664 auto owner = p.first->owner;
4665 auto rating = p.first->goods[cargo].rating;
4666 if (rating > company_best[owner]) {
4667 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4668 company_best[owner] = rating;
4669 if (rating > best_rating) best_rating = rating;
4670 }
4671 company_sum[owner] += rating;
4672 }
4673
4674 /* From now we'll calculate with fractional cargo amounts.
4675 * First determine how much cargo we really have. */
4676 amount *= best_rating + 1;
4677
4678 uint moving = 0;
4679 for (auto &p : used_stations) {
4680 Owner owner = p.first->owner;
4681 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4682 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4683 p.second = amount * company_best[owner] * p.first->goods[cargo].rating / best_sum / company_sum[owner];
4684 moving += p.second;
4685 }
4686
4687 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4688 if (amount > moving) {
4689 std::stable_sort(used_stations.begin(), used_stations.end(), [cargo](const StationInfo &a, const StationInfo &b) {
4690 return b.first->goods[cargo].rating < a.first->goods[cargo].rating;
4691 });
4692
4693 assert(amount - moving <= used_stations.size());
4694 for (uint i = 0; i < amount - moving; i++) {
4695 used_stations[i].second++;
4696 }
4697 }
4698
4699 uint moved = 0;
4700 for (auto &p : used_stations) {
4701 moved += UpdateStationWaiting(p.first, cargo, p.second, source);
4702 }
4703
4704 return moved;
4705}
4706
4707void UpdateStationDockingTiles(Station *st)
4708{
4709 st->docking_station.Clear();
4710
4711 /* For neutral stations, start with the industry area instead of dock area */
4712 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4713
4714 if (area->tile == INVALID_TILE) return;
4715
4716 int x = TileX(area->tile);
4717 int y = TileY(area->tile);
4718
4719 /* Expand the area by a tile on each side while
4720 * making sure that we remain inside the map. */
4721 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4722 int x1 = std::max<int>(x - 1, 0);
4723
4724 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4725 int y1 = std::max<int>(y - 1, 0);
4726
4727 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4728 for (TileIndex tile : ta) {
4729 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4730 }
4731}
4732
4733void BuildOilRig(TileIndex tile)
4734{
4735 if (!Station::CanAllocateItem()) {
4736 Debug(misc, 0, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4737 return;
4738 }
4739
4740 Station *st = Station::Create(tile);
4741 _station_kdtree.Insert(st->index);
4742 st->town = ClosestTownFromTile(tile, UINT_MAX);
4743
4745
4746 assert(IsTileType(tile, TileType::Industry));
4747 /* Mark industry as associated both ways */
4748 st->industry = Industry::GetByTile(tile);
4749 st->industry->neutral_station = st;
4750 DeleteAnimatedTile(tile);
4751 MakeOilrig(tile, st->index, GetWaterClass(tile));
4752
4753 st->owner = OWNER_NONE;
4754 st->airport.type = AT_OILRIG;
4755 st->airport.rotation = DIR_N;
4756 st->airport.Add(tile);
4757 st->ship_station.Add(tile);
4760 UpdateStationDockingTiles(st);
4761
4762 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
4763
4764 st->UpdateVirtCoord();
4765
4766 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4767 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4768 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4770 StationList nearby = std::move(st->industry->stations_near);
4771 st->industry->stations_near.clear();
4772 for (Station *near : nearby) {
4773 near->RecomputeCatchment(true);
4774 UpdateStationAcceptance(near, true);
4775 }
4776 }
4777
4778 st->RecomputeCatchment();
4779 UpdateStationAcceptance(st, false);
4780}
4781
4782void DeleteOilRig(TileIndex tile)
4783{
4784 Station *st = Station::GetByTile(tile);
4785
4786 MakeWaterKeepingClass(tile, OWNER_NONE);
4787
4788 /* The oil rig station is not supposed to be shared with anything else */
4789 [[maybe_unused]] static constexpr StationFacilities expected_facility{StationFacility::Airport, StationFacility::Dock};
4790 assert(st->facilities == expected_facility && st->airport.type == AT_OILRIG);
4791 if (st->industry != nullptr && st->industry->neutral_station == st) {
4792 /* Don't leave dangling neutral station pointer */
4793 st->industry->neutral_station = nullptr;
4794 }
4795 delete st;
4796}
4797
4799static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4800{
4801 if (IsAnyRoadStopTile(tile)) {
4802 for (RoadTramType rtt : _roadtramtypes) {
4803 /* Update all roadtypes, no matter if they are present */
4804 if (GetRoadOwner(tile, rtt) == old_owner) {
4805 RoadType rt = GetRoadType(tile, rtt);
4806 if (rt != INVALID_ROADTYPE) {
4807 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4808 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4809 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4810 }
4811 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4812 }
4813 }
4814 }
4815
4816 if (!IsTileOwner(tile, old_owner)) return;
4817
4818 if (new_owner != INVALID_OWNER) {
4819 /* Update company infrastructure counts. Only do it here
4820 * if the new owner is valid as otherwise the clear
4821 * command will do it for us. No need to dirty windows
4822 * here, we'll redraw the whole screen anyway.*/
4823 Company *old_company = Company::Get(old_owner);
4824 Company *new_company = Company::Get(new_owner);
4825
4826 /* Update counts for underlying infrastructure. */
4827 switch (GetStationType(tile)) {
4828 case StationType::Rail:
4830 if (!IsStationTileBlocked(tile)) {
4831 old_company->infrastructure.rail[GetRailType(tile)]--;
4832 new_company->infrastructure.rail[GetRailType(tile)]++;
4833 }
4834 break;
4835
4836 case StationType::Bus:
4837 case StationType::Truck:
4839 /* Road stops were already handled above. */
4840 break;
4841
4842 case StationType::Buoy:
4843 case StationType::Dock:
4844 if (GetWaterClass(tile) == WaterClass::Canal) {
4845 old_company->infrastructure.water--;
4846 new_company->infrastructure.water++;
4847 }
4848 break;
4849
4850 default:
4851 break;
4852 }
4853
4854 /* Update station tile count. */
4855 if (!IsBuoy(tile) && !IsAirport(tile)) {
4856 old_company->infrastructure.station--;
4857 new_company->infrastructure.station++;
4858 }
4859
4860 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4861 SetTileOwner(tile, new_owner);
4863 } else {
4864 if (IsDriveThroughStopTile(tile)) {
4865 /* Remove the drive-through road stop */
4866 if (IsRoadWaypoint(tile)) {
4867 Command<Commands::RemoveFromRoadWaypoint>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile, tile);
4868 } else {
4869 Command<Commands::RemoveRoadStop>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile, 1, 1, (GetStationType(tile) == StationType::Truck) ? RoadStopType::Truck : RoadStopType::Bus, false);
4870 }
4871 assert(IsTileType(tile, TileType::Road));
4872 /* Change owner of tile and all roadtypes */
4873 ChangeTileOwner(tile, old_owner, new_owner);
4874 } else {
4875 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
4876 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4877 * Update owner of buoy if it was not removed (was in orders).
4878 * Do not update when owned by OWNER_WATER (sea and rivers). */
4879 if ((IsTileType(tile, TileType::Water) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4880 }
4881 }
4882}
4883
4892static CommandCost CanRemoveRoadWithStop(TileIndex tile, DoCommandFlags flags)
4893{
4894 /* Water flooding can always clear road stops. */
4895 if (_current_company == OWNER_WATER) return CommandCost();
4896
4897 CommandCost ret;
4898
4899 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4900 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
4901 if (tram_owner != OWNER_NONE) {
4902 ret = CheckOwnership(tram_owner);
4903 if (ret.Failed()) return ret;
4904 }
4905 }
4906
4907 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4908 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
4909 if (road_owner == OWNER_TOWN) {
4910 ret = CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags);
4911 if (ret.Failed()) return ret;
4912 } else if (road_owner != OWNER_NONE) {
4913 ret = CheckOwnership(road_owner);
4914 if (ret.Failed()) return ret;
4915 }
4916 }
4917
4918 return CommandCost();
4919}
4920
4922CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags)
4923{
4924 if (flags.Test(DoCommandFlag::Auto)) {
4925 switch (GetStationType(tile)) {
4926 default: break;
4927 case StationType::Rail: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4928 case StationType::RailWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4929 case StationType::Airport: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4930 case StationType::Truck: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4931 case StationType::Bus: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4932 case StationType::RoadWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4933 case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4934 case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4936 return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG);
4937 }
4938 }
4939
4940 switch (GetStationType(tile)) {
4941 case StationType::Rail: return RemoveRailStation(tile, flags);
4942 case StationType::RailWaypoint: return RemoveRailWaypoint(tile, flags);
4943 case StationType::Airport: return RemoveAirport(tile, flags);
4944 case StationType::Truck: [[fallthrough]];
4945 case StationType::Bus:
4946 if (IsDriveThroughStopTile(tile)) {
4947 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4948 if (remove_road.Failed()) return remove_road;
4949 }
4950 return RemoveRoadStop(tile, flags);
4952 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4953 if (remove_road.Failed()) return remove_road;
4954 return RemoveRoadWaypointStop(tile, flags);
4955 }
4956 case StationType::Buoy: return RemoveBuoy(tile, flags);
4957 case StationType::Dock: return RemoveDock(tile, flags);
4958 default: break;
4959 }
4960
4961 return CMD_ERROR;
4962}
4963
4965static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
4966{
4967 if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
4968 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4969 * TTDP does not call it.
4970 */
4971 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4972 switch (GetStationType(tile)) {
4974 case StationType::Rail: {
4975 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4977 }
4978
4981
4982 case StationType::Truck:
4983 case StationType::Bus:
4985 if (IsDriveThroughStopTile(tile)) {
4986 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4987 } else {
4988 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4989 }
4991 }
4992
4993 default: break;
4994 }
4995 }
4996 }
4997 return Command<Commands::LandscapeClear>::Do(flags, tile);
4998}
4999
5005uint FlowStat::GetShare(StationID st) const
5006{
5007 uint32_t prev = 0;
5008 for (const auto &it : this->shares) {
5009 if (it.second == st) {
5010 return it.first - prev;
5011 } else {
5012 prev = it.first;
5013 }
5014 }
5015 return 0;
5016}
5017
5024StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const
5025{
5026 if (this->unrestricted == 0) return StationID::Invalid();
5027 assert(!this->shares.empty());
5028 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
5029 assert(it != this->shares.end() && it->first <= this->unrestricted);
5030 if (it->second != excluded && it->second != excluded2) return it->second;
5031
5032 /* We've hit one of the excluded stations.
5033 * Draw another share, from outside its range. */
5034
5035 uint end = it->first;
5036 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
5037 uint interval = end - begin;
5038 if (interval >= this->unrestricted) return StationID::Invalid(); // Only one station in the map.
5039 uint new_max = this->unrestricted - interval;
5040 uint rand = RandomRange(new_max);
5041 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
5042 this->shares.upper_bound(rand + interval);
5043 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
5044 if (it2->second != excluded && it2->second != excluded2) return it2->second;
5045
5046 /* We've hit the second excluded station.
5047 * Same as before, only a bit more complicated. */
5048
5049 uint end2 = it2->first;
5050 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
5051 uint interval2 = end2 - begin2;
5052 if (interval2 >= new_max) return StationID::Invalid(); // Only the two excluded stations in the map.
5053 new_max -= interval2;
5054 if (begin > begin2) {
5055 std::swap(begin, begin2);
5056 std::swap(end, end2);
5057 std::swap(interval, interval2);
5058 }
5059 rand = RandomRange(new_max);
5060 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
5061 if (rand < begin) {
5062 it3 = this->shares.upper_bound(rand);
5063 } else if (rand < begin2 - interval) {
5064 it3 = this->shares.upper_bound(rand + interval);
5065 } else {
5066 it3 = this->shares.upper_bound(rand + interval + interval2);
5067 }
5068 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
5069 return it3->second;
5070}
5071
5078{
5079 assert(!this->shares.empty());
5080 SharesMap new_shares;
5081 uint i = 0;
5082 for (const auto &it : this->shares) {
5083 new_shares[++i] = it.second;
5084 if (it.first == this->unrestricted) this->unrestricted = i;
5085 }
5086 this->shares.swap(new_shares);
5087 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
5088}
5089
5096void FlowStat::ChangeShare(StationID st, int flow)
5097{
5098 /* We assert only before changing as afterwards the shares can actually
5099 * be empty. In that case the whole flow stat must be deleted then. */
5100 assert(!this->shares.empty());
5101
5102 uint removed_shares = 0;
5103 uint added_shares = 0;
5104 uint last_share = 0;
5105 SharesMap new_shares;
5106 for (const auto &it : this->shares) {
5107 if (it.second == st) {
5108 if (flow < 0) {
5109 uint share = it.first - last_share;
5110 if (flow == INT_MIN || (uint)(-flow) >= share) {
5111 removed_shares += share;
5112 if (it.first <= this->unrestricted) this->unrestricted -= share;
5113 if (flow != INT_MIN) flow += share;
5114 last_share = it.first;
5115 continue; // remove the whole share
5116 }
5117 removed_shares += (uint)(-flow);
5118 } else {
5119 added_shares += (uint)(flow);
5120 }
5121 if (it.first <= this->unrestricted) this->unrestricted += flow;
5122
5123 /* If we don't continue above the whole flow has been added or
5124 * removed. */
5125 flow = 0;
5126 }
5127 new_shares[it.first + added_shares - removed_shares] = it.second;
5128 last_share = it.first;
5129 }
5130 if (flow > 0) {
5131 new_shares[last_share + (uint)flow] = st;
5132 if (this->unrestricted < last_share) {
5133 this->ReleaseShare(st);
5134 } else {
5135 this->unrestricted += flow;
5136 }
5137 }
5138 this->shares.swap(new_shares);
5139}
5140
5146void FlowStat::RestrictShare(StationID st)
5147{
5148 assert(!this->shares.empty());
5149 uint flow = 0;
5150 uint last_share = 0;
5151 SharesMap new_shares;
5152 for (auto &it : this->shares) {
5153 if (flow == 0) {
5154 if (it.first > this->unrestricted) return; // Not present or already restricted.
5155 if (it.second == st) {
5156 flow = it.first - last_share;
5157 this->unrestricted -= flow;
5158 } else {
5159 new_shares[it.first] = it.second;
5160 }
5161 } else {
5162 new_shares[it.first - flow] = it.second;
5163 }
5164 last_share = it.first;
5165 }
5166 if (flow == 0) return;
5167 new_shares[last_share + flow] = st;
5168 this->shares.swap(new_shares);
5169 assert(!this->shares.empty());
5170}
5171
5177void FlowStat::ReleaseShare(StationID st)
5178{
5179 assert(!this->shares.empty());
5180 uint flow = 0;
5181 uint next_share = 0;
5182 bool found = false;
5183 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
5184 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
5185 if (found) {
5186 flow = next_share - it->first;
5187 this->unrestricted += flow;
5188 break;
5189 } else {
5190 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
5191 if (it->second == st) found = true;
5192 }
5193 next_share = it->first;
5194 }
5195 if (flow == 0) return;
5196 SharesMap new_shares;
5197 new_shares[flow] = st;
5198 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
5199 if (it->second != st) {
5200 new_shares[flow + it->first] = it->second;
5201 } else {
5202 flow = 0;
5203 }
5204 }
5205 this->shares.swap(new_shares);
5206 assert(!this->shares.empty());
5207}
5208
5215{
5216 assert(runtime > 0);
5217 SharesMap new_shares;
5218 uint share = 0;
5219 for (auto i : this->shares) {
5220 share = std::max(share + 1, i.first * 30 / runtime);
5221 new_shares[share] = i.second;
5222 if (this->unrestricted == i.first) this->unrestricted = share;
5223 }
5224 this->shares.swap(new_shares);
5225}
5226
5233void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5234{
5235 FlowStatMap::iterator origin_it = this->find(origin);
5236 if (origin_it == this->end()) {
5237 this->emplace(origin, FlowStat(via, flow));
5238 } else {
5239 origin_it->second.ChangeShare(via, flow);
5240 assert(!origin_it->second.GetShares()->empty());
5241 }
5242}
5243
5252void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5253{
5254 FlowStatMap::iterator prev_it = this->find(origin);
5255 if (prev_it == this->end()) {
5256 FlowStat fs(via, flow);
5257 fs.AppendShare(StationID::Invalid(), flow);
5258 this->emplace(origin, fs);
5259 } else {
5260 prev_it->second.ChangeShare(via, flow);
5261 prev_it->second.ChangeShare(StationID::Invalid(), flow);
5262 assert(!prev_it->second.GetShares()->empty());
5263 }
5264}
5265
5271{
5272 for (auto &i : *this) {
5273 FlowStat &fs = i.second;
5274 uint local = fs.GetShare(StationID::Invalid());
5275 if (local > INT_MAX) { // make sure it fits in an int
5276 fs.ChangeShare(self, -INT_MAX);
5277 fs.ChangeShare(StationID::Invalid(), -INT_MAX);
5278 local -= INT_MAX;
5279 }
5280 fs.ChangeShare(self, -(int)local);
5281 fs.ChangeShare(StationID::Invalid(), -(int)local);
5282
5283 /* If the local share is used up there must be a share for some
5284 * remote station. */
5285 assert(!fs.GetShares()->empty());
5286 }
5287}
5288
5295std::vector<StationID> FlowStatMap::DeleteFlows(StationID via)
5296{
5297 std::vector<StationID> ret;
5298 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5299 FlowStat &s_flows = f_it->second;
5300 s_flows.ChangeShare(via, INT_MIN);
5301 if (s_flows.GetShares()->empty()) {
5302 ret.push_back(f_it->first);
5303 this->erase(f_it++);
5304 } else {
5305 ++f_it;
5306 }
5307 }
5308 return ret;
5309}
5310
5316{
5317 for (auto &it : *this) {
5318 it.second.RestrictShare(via);
5319 }
5320}
5321
5326void FlowStatMap::ReleaseFlows(StationID via)
5327{
5328 for (auto &it : *this) {
5329 it.second.ReleaseShare(via);
5330 }
5331}
5332
5338{
5339 uint ret = 0;
5340 for (const auto &it : *this) {
5341 ret += (--(it.second.GetShares()->end()))->first;
5342 }
5343 return ret;
5344}
5345
5351uint FlowStatMap::GetFlowVia(StationID via) const
5352{
5353 uint ret = 0;
5354 for (const auto &it : *this) {
5355 ret += it.second.GetShare(via);
5356 }
5357 return ret;
5358}
5359
5365uint FlowStatMap::GetFlowFrom(StationID from) const
5366{
5367 FlowStatMap::const_iterator i = this->find(from);
5368 if (i == this->end()) return 0;
5369 return (--(i->second.GetShares()->end()))->first;
5370}
5371
5378uint FlowStatMap::GetFlowFromVia(StationID from, StationID via) const
5379{
5380 FlowStatMap::const_iterator i = this->find(from);
5381 if (i == this->end()) return 0;
5382 return i->second.GetShare(via);
5383}
5384
5386static CommandCost CheckBuildAbove_Station(TileIndex tile, [[maybe_unused]] DoCommandFlags flags, [[maybe_unused]] Axis axis, int height)
5387{
5388 StationType type = GetStationType(tile);
5389 auto bridgeable_info = GetStationBridgeableTileInfo(type);
5390
5391 switch (type) {
5392 case StationType::Rail:
5394 if (const StationSpec *spec = GetStationSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5395 break;
5396
5397 case StationType::Bus:
5398 case StationType::Truck:
5400 if (const RoadStopSpec *spec = GetRoadStopSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5401 break;
5402
5403 default: break;
5404 }
5405
5406 return IsStationBridgeAboveOk(tile, bridgeable_info, type, GetStationGfx(tile), height);
5407}
5408
5410extern const TileTypeProcs _tile_type_station_procs = {
5411 .draw_tile_proc = DrawTile_Station,
5412 .get_slope_pixel_z_proc = [](TileIndex tile, uint, uint, bool) { return GetTileMaxPixelZ(tile); },
5413 .clear_tile_proc = ClearTile_Station,
5414 .get_tile_desc_proc = GetTileDesc_Station,
5415 .get_tile_track_status_proc = GetTileTrackStatus_Station,
5416 .click_tile_proc = ClickTile_Station,
5417 .animate_tile_proc = AnimateTile_Station,
5418 .tile_loop_proc = TileLoop_Station,
5419 .change_tile_owner_proc = ChangeTileOwner_Station,
5420 .vehicle_enter_tile_proc = VehicleEnterTile_Station,
5421 .get_foundation_proc = [](TileIndex, Slope tileh) { return FlatteningFoundation(tileh); },
5422 .terraform_tile_proc = TerraformTile_Station,
5423 .check_build_above_proc = CheckBuildAbove_Station,
5424};
Base for aircraft.
void UpdateAirplanesOnNewStation(const Station *st)
Updates the status of the Aircraft heading or in the station.
const AirportFTAClass * GetAirport(const uint8_t airport_type)
Get the finite state machine of an airport type.
Definition airport.cpp:188
static const uint INVALID_AIRPORTTILE
id for an invalid airport tile
Definition airport.h:25
static const uint NEW_AIRPORTTILE_OFFSET
offset of first newgrf airport tile
Definition airport.h:24
@ AirportClosed
Dummy block for indicating a closed airport.
Definition airport.h:130
@ FLYING
Vehicle is flying in the air.
Definition airport.h:77
@ NUM_AIRPORTS
Maximal number of airports in total.
Definition airport.h:41
@ AT_OILRIG
Oilrig airport.
Definition airport.h:38
Enum of the default airport tiles.
void AddAnimatedTile(TileIndex tile, bool mark_dirty)
Add the given tile to the animated tile table (if it does not exist yet).
void DeleteAnimatedTile(TileIndex tile, bool immediate)
Stops animation on the given tile.
Tile animation!
Functions related to autoslope.
bool AutoslopeCheckForAxis(TileIndex tile, int z_new, Slope tileh_new, Axis axis)
Autoslope check for tiles with something built along an axis.
Definition autoslope.h:51
bool AutoslopeCheckForEntranceEdge(TileIndex tile, int z_new, Slope tileh_new, DiagDirection entrance)
Autoslope check for tiles with an entrance on an edge.
Definition autoslope.h:31
bool AutoslopeEnabled()
Tests if autoslope is enabled for _current_company.
Definition autoslope.h:65
constexpr bool HasExactlyOneBit(T value)
Test whether value has exactly 1 bit set.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
Draw the middle bits of a bridge.
TileIndex GetSouthernBridgeEnd(TileIndex t)
Finds the southern end of a bridge starting at a middle tile.
int GetBridgeHeight(TileIndex t)
Get the height ('z') of a bridge.
Map accessor functions for bridges.
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:108
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:73
@ Passengers
Passengers.
Definition cargotype.h:50
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:235
Cheats _cheats
All the cheats.
Definition cheat.cpp:16
Types related to cheating.
Iterator to iterate over all tiles belonging to an airport.
Iterator to iterate over all tiles belonging to an airport spec.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Iterator to iterate over all tiles belonging to a bitmaptilearea.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
Flat set implementation that uses a sorted vector for storage.
std::pair< const_iterator, bool > insert(const Tkey &key)
Insert a key into the set, if it does not already exist.
uint GetFlow() const
Get the sum of all flows from this FlowStatMap.
void PassOnFlow(StationID origin, StationID via, uint amount)
Pass on some flow, remembering it as invalid, for later subtraction from locally consumed flow.
std::vector< StationID > DeleteFlows(StationID via)
Delete all flows at a station for specific cargo and destination.
void AddFlow(StationID origin, StationID via, uint amount)
Add some flow from "origin", going via "via".
uint GetFlowFrom(StationID from) const
Get the sum of flows from a specific station from this FlowStatMap.
void FinalizeLocalConsumption(StationID self)
Subtract invalid flows from locally consumed flow.
void ReleaseFlows(StationID via)
Release all flows at a station for specific cargo and destination.
uint GetFlowFromVia(StationID from, StationID via) const
Get the flow from a specific station via a specific other station.
uint GetFlowVia(StationID via) const
Get the sum of flows via a specific station from this FlowStatMap.
void RestrictFlows(StationID via)
Restrict all flows at a station for specific cargo and destination.
Flow statistics telling how much flow should be sent along a link.
static const SharesMap empty_sharesmap
Static instance of FlowStat::SharesMap.
void ScaleToMonthly(uint runtime)
Scale all shares from link graph's runtime to monthly values.
void RestrictShare(StationID st)
Restrict a flow by moving it to the end of the map and decreasing the amount of unrestricted flow.
uint GetShare(StationID st) const
Get flow for a station.
StationID GetVia() const
Get a station a package can be routed to.
const SharesMap * GetShares() const
Get the actual shares as a const pointer so that they can be iterated over.
SharesMap shares
Shares of flow to be sent via specified station (or consumed locally).
void ReleaseShare(StationID st)
Release ("unrestrict") a flow by moving it to the begin of the map and increasing the amount of unres...
void ChangeShare(StationID st, int flow)
Change share for specified station.
void AppendShare(StationID st, uint flow, bool restricted=false)
Add some flow to the end of the shares map.
void Invalidate()
Reduce all flows to minimum capacity so that they don't get in the way of link usage statistics too m...
uint unrestricted
Limit for unrestricted shares.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
void Insert(const T &element)
Insert a single element in the tree.
Definition kdtree.hpp:452
static LinkGraphSchedule instance
Static instance of LinkGraphSchedule.
void Queue(LinkGraph *lg)
Queue a link graph for execution.
A connected component of a link graph.
Definition linkgraph.h:37
void Merge(LinkGraph *other)
Merge a link graph with another one.
Definition linkgraph.cpp:91
TimerGameEconomy::Date LastCompression() const
Get date of last compression.
Definition linkgraph.h:240
NodeID AddNode(const Station *st)
Add a node to the component and create empty edges associated with it.
NodeID Size() const
Get the current size of the component.
Definition linkgraph.h:234
static const uint MIN_TIMEOUT_DISTANCE
Minimum effective distance for timeout calculation.
Definition linkgraph.h:174
static constexpr TimerGameEconomy::Date STALE_LINK_DEPOT_TIMEOUT
Number of days before deleting links served only by vehicles stopped in depot.
Definition linkgraph.h:177
static constexpr TimerGameEconomy::Date COMPRESSION_INTERVAL
Minimum number of days between subsequent compressions of a LG.
Definition linkgraph.h:180
static void Run(Vehicle *v, bool allow_merge=true, bool is_full_loading=false)
Refresh all links the given vehicle will visit.
Definition refresh.cpp:26
const Tspec * GetSpec(uint index) const
Get a spec from the class at a given index.
uint GetSpecCount() const
Get the number of allocated specs within the class.
static NewGRFClass * Get(StationClassID class_index)
StringID name
Name of this class.
uint position
Position within iterator.
const RailStationTileLayout & stl
Station tile layout being iterated.
uint length
Length of platforms.
std::span< const StationGfx > layout
Predefined tile layout.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:115
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:125
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:220
struct RailTypeInfo::@157247141350136173143103254227157213063052244122 strings
Strings associated with the rail type.
SpriteID single_y
single piece of rail in Y direction, without ground
Definition rail.h:126
StringID name
Name of this rail type.
Definition rail.h:165
uint8_t fallback_railtype
Original railtype number to use when drawing non-newgrf railtypes, or when drawing stations.
Definition rail.h:190
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:287
struct RailTypeInfo::@332027037331076264023214171276243307073252216167 base_sprites
Struct containing the main sprites.
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:116
struct RoadTypeInfo::@070000167274302256150317022075324310363002361255 strings
Strings associated with the rail type.
StringID name
Name of this rail type.
Definition road.h:77
Generate TileIndices around a center tile or tile area, with increasing distance.
Add dynamic register values to a sprite layout.
DrawTileSpriteSpan GetLayout() const
Returns the result spritelayout after preprocessing.
uint Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
Routes packets with station "avoid" as next hop to a different place.
void Append(CargoPacket *cp, StationID next)
Appends the given cargo packet to the range of packets with the same next station.
uint Truncate(uint max_move=UINT_MAX, StationCargoAmountMap *cargo_per_source=nullptr)
Truncates where each destination loses roughly the same percentage of its cargo.
const StationList & GetStations()
Run a tile loop to find stations around a tile, on demand.
static constexpr TimerGameTick::Ticks STATION_LINKGRAPH_TICKS
Cycle duration for cleaning dead links.
static constexpr TimerGameTick::Ticks STATION_ACCEPTANCE_TICKS
Cycle duration for updating station acceptance.
static constexpr TimerGameTick::Ticks STATION_RATING_TICKS
Cycle duration for updating station rating.
Base class for tile iterators.
Wrapper class to abstract away the way the tiles are stored.
Definition map_func.h:25
static Date date
Current date in days (day counter).
static constexpr TimerGame< struct Economy >::Date INVALID_DATE
static Date date
Current date in days (day counter).
static TickCounter counter
Monotonic counter, in ticks, since start of game.
Iterate over all vehicles on a tile.
Functions related to clear (TileType::Clear) land.
CommandCost CommandCostWithParam(StringID str, uint64_t value)
Return an error status, with string and parameter.
Definition command.cpp:416
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Auto
don't allow building on structures
@ Execute
execute the given command
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
Commands
List of commands.
Definition of stuff that is very close to a company, like the company struct itself.
PaletteID GetCompanyPalette(CompanyID company)
Get the palette for recolouring with a company colour.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
void DirtyCompanyInfrastructureWindows(CompanyID company)
Redraw all windows with company infrastructure counts.
GUI Functions related to companies.
static constexpr Owner OWNER_END
Last + 1 owner.
static constexpr Owner OWNER_TOWN
A town owns the tile, or a town is expanding.
static constexpr Owner OWNER_NONE
The tile has no ownership.
static constexpr Owner INVALID_OWNER
An invalid owner.
static constexpr Owner OWNER_WATER
The tile/execution is done by "water".
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh)
Find out if the slope of the tile is suitable to build a depot of given direction.
Definition depot_func.h:27
void ShowDepotWindow(TileIndex tile, VehicleType type)
Opens a depot window.
bool IsValidAxis(Axis d)
Checks if an integer value is a valid Axis.
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
DiagDirections AxisToDiagDirs(Axis a)
Converts an Axis to DiagDirections.
bool IsValidDiagDirection(DiagDirection d)
Checks if an integer value is a valid DiagDirection.
DiagDirection AxisToDiagDir(Axis a)
Converts an Axis to a DiagDirection.
Axis OtherAxis(Axis a)
Select the other axis as provided.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
Direction
Defines the 8 directions on the map.
@ DIR_N
North.
@ DIR_W
West.
@ DIR_E
East.
Axis
Allow incrementing of DiagDirDiff variables.
@ AXIS_X
The X axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_END
Used for iterations.
@ DIAGDIR_BEGIN
Used for iterations.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
@ DIAGDIR_SW
Southwest.
Prices _price
Prices and also the fractional part.
Definition economy.cpp:106
static const uint ROAD_STOP_TRACKBIT_FACTOR
Multiplier for how many regular track bits a bay stop counts.
@ EXPENSES_CONSTRUCTION
Construction costs.
Price
Enumeration of all base prices for use with Prices.
@ BuildStationTruck
Price for building lorry stations.
@ ClearStationBus
Price for destroying bus stops.
@ BuildStationBus
Price for building bus stops.
@ ClearStationAirport
Price for destroying airports.
@ ClearStationTruck
Price for destroying lorry stations.
@ BuildStationAirport
Price for building airports.
@ ClearStationDock
Price for destroying docks.
@ ClearStationRail
Price for destroying rail stations.
@ BuildStationRailLength
Additional price for building rail stations dependent on their length.
@ BuildFoundation
Price for building foundation under other constructions e.g. roads, rails, depots,...
@ BuildStationRail
Price for building rail stations.
@ BuildStationDock
Price for building docks.
@ ClearWaypointRail
Price for destroying rail waypoints.
@ ClearRail
Price for destroying rails.
void DrawRailCatenary(const TileInfo *ti)
Draws overhead wires and pylons for electric railways.
Definition elrail.cpp:559
Header file for electrified rail specific functions.
bool HasRailCatenaryDrawn(RailType rt)
Test if we should draw rail catenary.
Definition elrail_func.h:32
#define T
Climate temperate.
Definition engines.h:91
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Flat set container implementation.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1038
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
uint8_t GetSnowLine()
Get the current snow line, either variable or static.
void MarkTilesDirty(bool cargo_change) const
Marks the tiles of the station as dirty.
Definition station.cpp:250
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
HouseZone
Concentric rings of zoning around the centre of a town.
Definition house.h:57
@ TownEdge
Edge of the town; roads without pavement.
Definition house.h:58
Base of all industries.
bool IsTileForestIndustry(TileIndex tile)
Check whether the tile is a forest.
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
IndustryType GetIndustryType(Tile tile)
Retrieve the type for this industry.
IndustryID GetIndustryIndex(Tile t)
Get the industry ID of the given tile.
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like IT_...
@ Extractive
Like mines.
const TileTypeProcs _tile_type_station_procs
TileTypeProcs definitions for TileType::Station tiles.
Definition landscape.cpp:57
void ChangeTileOwner(TileIndex tile, Owner old_owner, Owner new_owner)
Change the owner of a tile.
void DrawFoundation(TileInfo *ti, Foundation f)
Draw foundation f at tile ti.
std::tuple< Slope, int > GetFoundationPixelSlope(TileIndex tile)
Get slope of a tile on top of a (possible) foundation If a tile does not have a foundation,...
Definition landscape.h:67
uint ApplyPixelFoundationToSlope(Foundation f, Slope &s)
Applies a foundation to a slope.
Definition landscape.h:128
Point RemapCoords2(int x, int y)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:97
Command definitions related to landscape (slopes etc.).
@ Arctic
Landscape with snow levels.
@ Tropic
Landscape with distinct rainforests and deserts,.
Some typedefs for the main game.
@ Increase
Increase capacity.
@ Manual
Manual distribution. No link graph calculations are run.
#define Point
Macro that prevents name conflicts between included headers.
uint DistanceFromEdge(TileIndex tile)
Param the minimum distance to an edge.
Definition map.cpp:229
uint DistanceMax(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles.
Definition map.cpp:201
TileIndex TileAddWrap(TileIndex tile, int addx, int addy)
This function checks if we add addx/addy to tile, if we do wrap around the edges.
Definition map.cpp:120
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:169
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:444
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:392
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:559
static TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:376
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:429
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:419
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:574
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
constexpr To ClampTo(From value)
Clamp the given value down to lie within the requested type.
Types related to the misc widgets.
uint8_t StationGfx
Copy from station_map.h.
StationGfx GetTranslatedAirportTileID(StationGfx gfx)
Do airporttile gfx ID translation for NewGRFs.
NewGRF handling of airport tiles.
@ NoAnimation
There is no animation.
@ DrawTileLayout
Use callback to select a tile layout to use when drawing.
@ Avail
Availability of station in construction window.
@ SlopeCheck
Check slope of new station tiles.
@ StationRatingCalc
custom station rating for this cargo type
@ CBID_STATION_BUILD_TILE_LAYOUT
Called when building a station to customize the tile layout.
@ CBID_STATION_DRAW_TILE_LAYOUT
Choose a tile layout to draw, instead of the standard range.
@ CBID_CARGO_STATION_RATING_CALC
Called to calculate part of a station rating.
@ CBID_STATION_AVAILABILITY
Determine whether a newstation should be made available to build.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
@ Avail
Availability of road stop in construction window.
SpriteID GetCanalSprite(CanalFeature feature, TileIndex tile)
Lookup the base sprite to use for a canal.
Handling of NewGRF canals.
Cargo support for NewGRFs.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
bool Convert8bitBooleanCallback(const GRFFile *grffile, uint16_t cbid, uint16_t cb_res)
Converts a callback result into a boolean.
GRFConfig * GetGRFConfig(uint32_t grfid, uint32_t mask)
Retrieve a NewGRF from the current config by its grfid.
Functions/types related to NewGRF debugging.
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Delete inspect window for a given feature and index.
void TriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, CargoTypes trigger_cargoes)
Run watched cargo accepted callback for a house.
Functions related to NewGRF houses.
SpriteID GetCustomRailSprite(const RailTypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of rail types.
void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger road stop randomisation.
void DeallocateSpecFromRoadStop(BaseStation *st, uint8_t specindex)
Deallocate a RoadStopSpec from a Station.
std::optional< uint8_t > AllocateSpecToRoadStop(const RoadStopSpec *spec, BaseStation *st)
Allocate a RoadStopSpec to a Station.
void AssignSpecToRoadStop(const RoadStopSpec *spec, BaseStation *st, uint8_t specindex)
Assign a previously allocated RoadStopSpec specindex to a Station.
NewGRF definitions and structures for road stops.
@ Overlay
Drive-through stops: Draw the road overlay, e.g. pavement.
@ WaypGround
Waypoints: Draw the sprite layout ground tile (on top of the road).
@ Road
Bay stops: Draw the road itself.
bool IsWaypointClass(const RoadStopClass &cls)
Test if a RoadStopClass is the waypoint class.
PoolID< uint16_t, struct RoadStopClassIDTag, UINT16_MAX, UINT16_MAX > RoadStopClassID
Class IDs for stations.
@ ROADSTOPTYPE_FREIGHT
This RoadStop is for freight (truck) stops.
@ ROADSTOPTYPE_ALL
This RoadStop is for both types of station road stops.
@ ROADSTOPTYPE_PASSENGER
This RoadStop is for passenger (bus) stops.
@ NoCatenary
Do not show catenary.
@ DriveThroughOnly
Stop is drive-through only.
@ DrawModeRegister
Read draw mode from register 0x100.
SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of road types.
uint32_t GetPlatformInfo(Axis axis, uint8_t tile, int platforms, int length, int x, int y, bool centred)
Evaluate a tile's position within a station, and return the result in a bit-stuffed format.
SpriteID GetCustomStationRelocation(const StationSpec *statspec, BaseStation *st, TileIndex tile, uint32_t var10)
Resolve sprites for drawing a station tile.
SpriteID GetCustomStationFoundationRelocation(const StationSpec *statspec, BaseStation *st, TileIndex tile, uint layout, uint edge_info)
Resolve the sprites for custom station foundations.
void AssignSpecToStation(const StationSpec *spec, BaseStation *st, uint8_t specindex)
Assign a previously allocated StationSpec specindex to a Station.
void DeallocateSpecFromStation(BaseStation *st, uint8_t specindex)
Deallocate a StationSpec from a Station.
void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger station randomisation.
CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, uint8_t plat_len, uint8_t numtracks)
Check the slope of a tile of a new station.
std::optional< uint8_t > AllocateSpecToStation(const StationSpec *spec, BaseStation *st)
Allocate a StationSpec to a Station.
Header file for NewGRF stations.
@ CustomFoundations
Draw custom foundations.
@ SeparateGround
Use different sprite set for ground sprites.
@ ExtendedFoundations
Extended foundation block instead of simple.
NewGRFClass< StationSpec, StationClassID > StationClass
Class containing information relating to station classes.
PoolID< uint16_t, struct StationClassIDTag, UINT16_MAX, UINT16_MAX > StationClassID
Class IDs for stations.
uint16_t GetStationLayoutKey(uint8_t platforms, uint8_t length)
Get the station layout key for a given station layout size.
Functions related to news.
void AddNewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1={}, NewsReference ref2={}, std::unique_ptr< NewsAllocatedData > &&data=nullptr, AdviceType advice_type=AdviceType::Invalid)
Add a new newsitem to be shown.
Definition news_gui.cpp:914
@ Acceptance
A type of cargo is (no longer) accepted.
Definition news_type.h:43
@ Small
Small news item. (Information window with text and viewport).
Definition news_type.h:79
@ InColour
News item is shown in colour (otherwise it is shown in black & white).
Definition news_type.h:90
@ LinkGraph
A game paused due to the link graph schedule lagging.
Definition openttd.h:75
Functions related to order backups.
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:343
void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
Set the reservation for a complete station platform.
Definition pbs.cpp:57
PBS support routines.
bool ValParamRailType(const RailType rail)
Validate functions for rail building.
Definition rail.cpp:92
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:429
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition rail.h:377
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:301
@ RTSG_GROUND
Main group of ground images.
Definition rail.h:43
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:42
RailTrackOffset
Offsets for sprites within an overlay/underlay set.
Definition rail.h:61
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:63
@ RTO_X
Piece of rail in X direction.
Definition rail.h:62
Command definitions for rail.
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
static bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
void MakeRailNormal(Tile t, Owner o, TrackBits b, RailType r)
Make the given tile a normal rail.
Definition rail_map.h:614
TrackBits GetRailReservationTrackBits(Tile t)
Returns the reserved track bits of the tile.
Definition rail_map.h:194
bool HasSignals(Tile t)
Checks if a rail tile has signals.
Definition rail_map.h:72
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
Pseudo random number generator.
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
Declaration of link refreshing utility.
bool ValParamRoadType(RoadType roadtype)
Validate functions for rail building.
Definition road.cpp:165
bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
Checks if an engine of the given RoadType got power on a tile with a given RoadType.
Definition road.h:230
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:215
@ ROTSG_OVERLAY
Optional: Images for overlaying track.
Definition road.h:38
@ ROTSG_ROADSTOP
Required: Bay stop surface.
Definition road.h:47
@ ROTSG_GROUND
Required: Main group of ground images.
Definition road.h:39
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:240
void DrawRoadGroundSprites(const TileInfo *ti, RoadBits road, RoadBits tram, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, Roadside roadside, bool snow_or_desert)
Draw road ground sprites.
void DrawRoadCatenary(const TileInfo *ti)
Draws the catenary for the given tile.
void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count)
Update road infrastructure counts for a company.
Definition road_cmd.cpp:183
CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadTramType rtt, DoCommandFlags flags, bool town_check)
Is it allowed to remove the given road bits from the given tile?
Definition road_cmd.cpp:255
void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, uint road_offset, uint tram_offset, bool draw_underlay)
Draw road underlay and overlay sprites.
Functions related to roads.
RoadBits AxisToRoadBits(Axis a)
Create the road-part which belongs to the given Axis.
Definition road_func.h:111
Functions used internally by the roads.
bool MayHaveRoad(Tile t)
Test whether a tile can have road/tram types.
Definition road_map.cpp:21
RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance)
Returns the RoadBits on an arbitrary tile Special behaviour:
Definition road_map.cpp:54
void SetRoadOwner(Tile t, RoadTramType rtt, Owner o)
Set the owner of a specific road type.
Definition road_map.h:261
RoadType GetRoadTypeRoad(Tile t)
Get the road type for RoadTramType being RTT_ROAD.
Definition road_map.h:152
RoadBits GetRoadBits(Tile t, RoadTramType rtt)
Get the present road bits for a specific road type.
Definition road_map.h:112
DisallowedRoadDirections GetDisallowedRoadDirections(Tile t)
Gets the disallowed directions.
Definition road_map.h:311
bool HasTileRoadType(Tile t, RoadTramType rtt)
Check if a tile has a road or a tram road type.
Definition road_map.h:221
RoadType GetRoadTypeTram(Tile t)
Get the road type for RoadTramType being RTT_TRAM.
Definition road_map.h:163
void MakeRoadNormal(Tile t, RoadBits bits, RoadType road_rt, RoadType tram_rt, TownID town, Owner road, Owner tram)
Make a normal road tile.
Definition road_map.h:646
RoadBits GetAllRoadBits(Tile tile)
Get all set RoadBits on the given tile.
Definition road_map.h:125
RoadType GetRoadType(Tile t, RoadTramType rtt)
Get the road type for the given RoadTramType.
Definition road_map.h:175
static bool IsNormalRoadTile(Tile t)
Return whether a tile is a normal road tile.
Definition road_map.h:58
Owner GetRoadOwner(Tile t, RoadTramType rtt)
Get the owner of a specific road type.
Definition road_map.h:244
Roadside
The possible road side decorations.
Definition road_map.h:483
@ Paved
Road with paved sidewalks.
Definition road_map.h:486
@ Barren
Road on barren land.
Definition road_map.h:484
@ Grass
Road on grass.
Definition road_map.h:485
RoadBits
Enumeration for the road parts on a tile.
Definition road_type.h:56
@ ROAD_NONE
No road-part is build.
Definition road_type.h:57
@ ROAD_Y
Full road along the y-axis (north-west + south-east).
Definition road_type.h:63
@ ROAD_X
Full road along the x-axis (south-west + north-east).
Definition road_type.h:62
RoadTramType
The different types of road type.
Definition road_type.h:37
@ RTT_ROAD
Road road type.
Definition road_type.h:38
@ RTT_TRAM
Tram road type.
Definition road_type.h:39
RoadType
The different roadtypes we support.
Definition road_type.h:23
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:28
@ DRD_NONE
None of the directions are disallowed.
Definition road_type.h:78
Base class for roadstops.
Road vehicle states.
@ RVSB_IN_ROAD_STOP
The vehicle is in a road stop.
Definition roadveh.h:49
@ RVSB_ROAD_STOP_TRACKDIR_MASK
Only bits 0 and 3 are used to encode the trackdir for road stops.
Definition roadveh.h:57
@ RVS_IN_DT_ROAD_STOP
The vehicle is in a drive-through road stop.
Definition roadveh.h:46
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Base for ships.
bool IsShipDestinationTile(TileIndex tile, StationID station)
Test if a tile is a docking tile for the given station.
Definition ship_cmd.cpp:617
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:596
@ Enter
signal entering the block found
Definition signal.cpp:262
static constexpr int GetSlopeMaxZ(Slope s)
Returns the height of the highest corner of a slope relative to TileZ (= minimal height).
Definition slope_func.h:160
static constexpr bool IsSteepSlope(Slope s)
Checks if a slope is steep.
Definition slope_func.h:36
Foundation FlatteningFoundation(Slope s)
Returns the foundation needed to flatten a slope.
Definition slope_func.h:369
DiagDirection GetInclinedSlopeDirection(Slope s)
Returns the direction of an inclined slope.
Definition slope_func.h:239
Slope
Enumeration for the slope-type.
Definition slope_type.h:47
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:48
@ FOUNDATION_LEVELED
The tile is leveled up to a flat slope.
Definition slope_type.h:94
void DrawRailTileSeqInGUI(int x, int y, const DrawTileSprites *dts, int32_t total_offset, uint32_t newgrf_offset, PaletteID default_palette)
Draw tile sprite sequence in GUI with railroad specifics.
Definition sprite.h:122
void DrawRailTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, int32_t total_offset, uint32_t newgrf_offset, PaletteID default_palette)
Draw tile sprite sequence on tile with railroad specifics.
Definition sprite.h:108
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:207
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1559
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1549
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
Definition sprites.h:1562
Base classes/functions for stations.
void ForAllStationsAroundTiles(const TileArea &ta, Func func)
Call a function on all stations that have any part of the requested area within their catchment.
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index=-1)
Remove a bus station/truck stop.
std::tuple< CommandCost, StationID > CmdMoveStationName(DoCommandFlags flags, StationID station_id, TileIndex tile)
Move a station name.
static StringID GetBridgeTooLowMessageForStationType(StationType type)
Get station-type-specific string for a bridge that is too low.
static void GetTileDesc_Station(TileIndex tile, TileDesc &td)
Tile callback function signature for obtaining a tile description.
static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
Common part of building various station parts and possibly attaching them to an existing one.
static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlags flags)
Remove a rail waypoint.
void CcMoveStationName(Commands, const CommandCost &result, StationID station_id)
Callback function that is called after a name is moved.
CargoTypes GetEmptyMask(const Station *st)
Get a mask of the cargo types that are empty at the station.
static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
Tile callback function signature for getting the possible tracks that can be taken on a given tile by...
CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *spec, StationType type, StationGfx layout)
Test if a rail station can be built below a bridge.
uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
Get a possible noise reduction factor based on distance from town center.
static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
Tile callback function signature of the terraforming callback.
CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector< T * > &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
Remove a number of tiles from any rail station within the area.
static CommandCost CalculateRailStationCost(TileArea tile_area, DoCommandFlags flags, Axis axis, StationID *station, RailType rt, std::vector< Train * > &affected_vehicles, StationClassID spec_class, uint16_t spec_index, uint8_t plat_len, uint8_t numtracks)
Calculates cost of new rail stations within the area.
static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
Generate a station name for the given station at the given location.
CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta)
Check whether we can expand the rail part of the given station.
void ClearDockingTilesCheckingNeighbours(TileIndex tile)
Clear docking tile status from tiles around a removed dock, if the tile has no neighbours which would...
bool(* CMSAMatcher)(TileIndex tile)
Function to check whether the given tile matches some criterion.
CommandCost IsRoadStationBridgeAboveOk(TileIndex tile, const RoadStopSpec *spec, StationType type, StationGfx layout)
Test if a road station can be built below a bridge.
bool IsHangar(Tile t)
Check whether the given tile is a hangar.
static Station * GetClosestDeletedStation(TileIndex tile)
Find the closest deleted station of the current company.
CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
Rename a station.
std::pair< CargoArray, CargoTypes > GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad)
Get the acceptance of cargoes around the tile in 1/8.
static CommandCost RemoveAirport(TileIndex tile, DoCommandFlags flags)
Remove an airport.
static void TileLoop_Station(TileIndex tile)
Tile callback function signature for running periodic tile updates.
static bool CMSAMine(TileIndex tile)
Check whether the tile is a mine.
void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
Increase capacity for a link stat given by station cargo and next hop.
static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, const RoadStopSpec *spec, DoCommandFlags flags, DiagDirections invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt)
Checks if a road stop can be built at the given tile.
static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
Remove a tile area of road stop or road waypoints.
static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount=UINT_MAX)
Truncate the cargo by a specific amount.
CommandCost CmdRemoveFromRailWaypoint(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
Remove a single tile from a waypoint.
StationNaming
Station types a station could be named after.
@ Heliport
Standalone heliport.
@ Rail
Railway station.
@ Oilrig
Heliport of an oilrig.
@ Road
Truck or bus stop.
@ Dock
Ship dock.
@ Airport
Airport for fixed wing aircraft.
CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
Find a nearby waypoint that joins this waypoint.
CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
Build a dock/haven.
CommandCost IsBuoyBridgeAboveOk(TileIndex tile)
Test if a buoy can be built below a bridge.
static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCommandFlags flags)
Checks if an airport can be built at the given location and clear the area.
CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index=-1)
Remove a road waypoint.
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
Look for a station owned by the given company around the given tile area.
CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
Remove bus or truck stops.
static VehicleEnterTileStates VehicleEnterTile_Station(Vehicle *v, TileIndex tile, int x, int y)
Tile callback function for a vehicle entering a tile.
static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
Tile callback function signature for changing the owner of a tile.
bool HasStationInUse(StationID station, bool include_company, CompanyID company)
Tests whether the company's vehicles have this station in orders.
static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
Counts the numbers of tiles matching a specific type in the area around.
CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
Build a bus or truck stop.
static StationSpec::TileFlags GetStationTileFlags(StationGfx gfx, const StationSpec *statspec)
Get station tile flags for the given StationGfx.
static CommandCost IsDockBridgeAboveOk(TileIndex tile, StationGfx layout)
Test if a dock can be built below a bridge.
static bool DrawCustomStationFoundations(const StationSpec *statspec, BaseStation *st, TileInfo *ti, StationGfx gfx)
Draw custom station foundations for a NewGRF station if provided.
const DrawTileSprites * GetStationTileLayout(StationType st, uint8_t gfx)
Get station tile layout for a station type and its station gfx.
CommandCost CmdRemoveFromRoadWaypoint(DoCommandFlags flags, TileIndex start, TileIndex end)
Remove road waypoints.
void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
Reroute cargo of type c at station st or in any vehicles unloading there.
CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags)
Tile callback function signature for clearing a tile.
static std::pair< CargoArray, CargoTypes > GetAcceptanceAroundStation(const Station *st)
Get the acceptance of cargoes around the station in.
static void UpdateStationRating(Station *st)
Periodic update of a station's rating.
void UpdateAllStationVirtCoords()
Update the virtual coords needed to draw the station sign for all stations.
static void DrawTile_Station(TileInfo *ti)
Tile callback function signature for drawing a tile and its contents to the screen.
static bool CMSAWater(TileIndex tile)
Check whether the tile is water.
CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
Place an Airport.
void TriggerWatchedCargoCallbacks(Station *st)
Run the watched cargo callback for all houses in the catchment area.
static CommandCost CanRemoveRoadWithStop(TileIndex tile, DoCommandFlags flags)
Check if a drive-through road stop tile can be cleared.
static RoadStop ** FindRoadStopSpot(bool truck_station, Station *st)
CargoTypes GetAcceptanceMask(const Station *st)
Get a mask of the cargo types that the station accepts.
static void RestoreTrainReservation(Train *v)
Restore platform reservation during station building/removing.
void UpdateAirportsNoise()
Recalculate the noise generated by the airports of each town.
static std::span< const BridgeableTileInfo > GetStationBridgeableTileInfo(StationType type)
Get bridgeable tile information for a station type.
static bool CMSATree(TileIndex tile)
Check whether the tile is a tree.
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
Forcibly modify station ratings near a given tile.
CommandCost RemoveRailStation(T *st, DoCommandFlags flags, Money removal_cost)
Remove a rail station/waypoint.
void UpdateStationAcceptance(Station *st, bool show_msg)
Update the acceptance for a station.
static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
Find a nearby station that joins this road stop.
CommandCost CmdOpenCloseAirport(DoCommandFlags flags, StationID station_id)
Open/close an airport to incoming aircraft.
static const IntervalTimer< TimerGameEconomy > _economy_stations_monthly({TimerGameEconomy::Trigger::Month, TimerGameEconomy::Priority::Station}, [](auto) { for(Station *st :Station::Iterate()) { for(GoodsEntry &ge :st->goods) { ge.status.Set(GoodsEntry::State::LastMonth, ge.status.Test(GoodsEntry::State::CurrentMonth));ge.status.Reset(GoodsEntry::State::CurrentMonth);} } })
Economy monthly loop for stations.
static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_tile, int &allowed_z, DoCommandFlags flags, Axis axis, StationID *station, RailType rt, std::vector< Train * > &affected_vehicles, StationClassID spec_class, uint16_t spec_index, uint8_t plat_len, uint8_t numtracks)
Checks if a rail station can be built at the given tile.
static TileIndex FindDockLandPart(TileIndex t)
Find the part of a dock that is land-based.
static CommandCost CheckBuildAbove_Station(TileIndex tile, DoCommandFlags flags, Axis axis, int height)
Tile callback function signature to test if a bridge can be built above a tile.
static void FreeTrainReservation(Train *v)
Clear platform reservation during station building/removing.
static bool ClickTile_Station(TileIndex tile)
Tile callback function signature for clicking a tile.
void SetRailStationTileFlags(TileIndex tile, const StationSpec *statspec)
Set rail station tile flags for the given tile.
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *station, RoadType rt, Money unit_cost)
Calculates cost of new road stops within the area.
static CommandCost RemoveDock(TileIndex tile, DoCommandFlags flags)
Remove a dock.
static void AnimateTile_Station(TileIndex tile)
Tile callback function signature for animating a tile.
bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
Check whether a sprite is a track sprite, which can be replaced by a non-track ground sprite and a ra...
CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
Get the cargo types being produced around the tile (in a rectangle).
CommandCost CmdRemoveFromRailStation(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
Remove a single tile from a rail station.
static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
Find a nearby station that joins this station.
void DeleteStaleLinks(Station *from)
Check all next hops of cargo packets in this station for existence of a a valid link they may use to ...
static BridgePillarFlags GetStationBlockedPillars(std::span< const BridgeableTileInfo > bridgeable_info, uint8_t layout)
Get blocked pillar information for a station tile.
CommandCost CheckBuildableTile(TileIndex tile, DiagDirections invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge=true)
Checks if the given tile is buildable, flat and has a certain height.
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
Find a nearby station that joins this station.
Town * AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
Finds the town nearest to given airport.
static bool StationHandleBigTick(BaseStation *st)
This function is called for each station once every 250 ticks.
static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
Add news item for when a station changes which cargoes it accepts.
CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailType rt, Axis axis, uint8_t numtracks, uint8_t plat_len, StationClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
Build rail station.
static void DeleteStationIfEmpty(BaseStation *st)
This is called right after a station was deleted.
static CommandCost IsStationBridgeAboveOk(TileIndex tile, std::span< const BridgeableTileInfo > bridgeable_info, StationType type, StationGfx layout, int bridge_height, StringID disallowed_msg=INVALID_STRING_ID)
Test if a bridge can be built above a station.
Command definitions related to stations.
Functions related to stations.
void ShowStationViewWindow(StationID station)
Opens StationViewWindow for given station.
Declarations for accessing the k-d tree of stations.
void ForAllStationsRadius(TileIndex center, uint radius, Func func)
Call a function on all stations whose sign is within a radius of a center tile.
Sprites to use and how to display them for station tiles.
Functions related to station layouts.
void MakeAirport(Tile t, Owner o, StationID sid, uint8_t section, WaterClass wc)
Make the given tile an airport tile.
StationType GetStationType(Tile t)
Get the station type of this tile.
Definition station_map.h:44
StationGfx GetStationGfx(Tile t)
Get the station graphics of this tile.
Definition station_map.h:68
void SetStationGfx(Tile t, StationGfx gfx)
Set the station graphics of this tile.
Definition station_map.h:80
void MakeRoadStop(Tile t, Owner o, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, DiagDirection d)
Make the given tile a roadstop tile.
void SetCustomStationSpecIndex(Tile t, uint8_t specindex)
Set the custom station spec for this tile.
void SetStationTileHaveWires(Tile t, bool b)
Set the catenary wires state of the rail station.
bool IsAirport(Tile t)
Is this station tile an airport?
bool IsBayRoadStopTile(Tile t)
Is tile t a bay (non-drive through) road stop station?
uint GetCustomRoadStopSpecIndex(Tile t)
Get the custom road stop spec for this tile.
static const int GFX_DOCK_BASE_WATER_PART
The offset for the water parts.
Definition station_map.h:35
bool IsBuoy(Tile t)
Is tile t a buoy tile?
bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile)
Check if a tile is a valid continuation to a railstation tile.
bool IsRoadWaypoint(Tile t)
Is the station at t a road waypoint?
void MakeDriveThroughRoadStop(Tile t, Owner station, Owner road, Owner tram, StationID sid, StationType rst, RoadType road_rt, RoadType tram_rt, Axis a)
Make the given tile a drivethrough roadstop tile.
bool IsDriveThroughStopTile(Tile t)
Is tile t a drive through road stop station or waypoint?
static Roadside GetRoadWaypointRoadside(Tile tile)
Get the decorations of a road waypoint.
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
Track GetRailStationTrack(Tile t)
Get the rail track of a rail station tile.
static void ToggleRoadWaypointOnSnowOrDesert(Tile t)
Toggle the snow/desert state of a road waypoint tile.
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
bool HasStationTileRail(Tile t)
Has this station tile a rail?
StationGfx GetAirportGfx(Tile t)
Get the station graphics of this airport tile.
uint GetCustomStationSpecIndex(Tile t)
Get the custom station spec for this tile.
bool IsRailWaypoint(Tile t)
Is this station tile a rail waypoint?
bool IsRailStation(Tile t)
Is this station tile a rail station?
Definition station_map.h:92
bool IsDockTile(Tile t)
Is tile t a dock tile?
static void SetRoadWaypointRoadside(Tile tile, Roadside s)
Set the decorations of a road waypoint.
void SetStationTileRandomBits(Tile t, uint8_t random_bits)
Set the random bits for a station tile.
bool IsAnyRoadStop(Tile t)
Is the station at t a road station?
void MakeOilrig(Tile t, StationID sid, WaterClass wc)
Make the given tile an oilrig tile.
DiagDirection GetDockDirection(Tile t)
Get the direction of a dock.
Axis GetRailStationAxis(Tile t)
Get the rail direction of a rail station.
static bool IsRoadWaypointOnSnowOrDesert(Tile t)
Check if a road waypoint tile has snow/desert.
bool IsRoadWaypointTile(Tile t)
Is this tile a station tile and a road waypoint?
bool IsStationTileBlocked(Tile t)
Is tile t a blocked tile?
bool IsTruckStop(Tile t)
Is the station at t a truck stop?
bool IsStationRoadStop(Tile t)
Is the station at t a road station?
bool IsCustomStationSpecIndex(Tile t)
Is there a custom rail station spec on this tile?
bool HasStationRail(Tile t)
Has this station tile a rail?
static const int GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET
The offset for the drive through parts.
Definition station_map.h:36
Axis GetDriveThroughStopAxis(Tile t)
Gets the axis of the drive through stop.
void SetStationTileHavePylons(Tile t, bool b)
Set the catenary pylon state of the rail station.
bool IsOilRig(Tile t)
Is tile t part of an oilrig?
bool IsBuoyTile(Tile t)
Is tile t a buoy tile?
void MakeRailStation(Tile t, Owner o, StationID sid, Axis a, uint8_t section, RailType rt)
Make the given tile a rail station tile.
bool HasStationReservation(Tile t)
Get the reservation state of the rail station.
void MakeDock(Tile t, Owner o, StationID sid, DiagDirection d, WaterClass wc)
Make the given tile a dock tile.
DiagDirection GetBayRoadStopDir(Tile t)
Gets the direction the bay road stop entrance points towards.
bool IsDock(Tile t)
Is tile t a dock tile?
bool IsAnyRoadStopTile(Tile t)
Is tile t a road stop station?
void SetStationTileBlocked(Tile t, bool b)
Set the blocked state of the rail station.
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition station_map.h:56
void SetCustomRoadStopSpecIndex(Tile t, uint8_t specindex)
Set the custom road stop spec for this tile.
RoadStopType
Types of RoadStops.
@ Bus
A standard stop for buses.
@ Truck
A standard stop for trucks.
@ End
End of valid types.
@ Dock
Station with a dock.
@ Waypoint
Station is a waypoint.
@ TruckStop
Station with truck stops.
@ Train
Station with train station.
@ Airport
Station with an airport.
@ BusStop
Station with bus stops.
StationType
Station types.
@ Dock
Ship port.
@ Rail
Railways/train station.
@ Bus
Road stop for busses.
@ Truck
Road stop for trucks.
@ End
End marker.
@ RailWaypoint
Waypoint for trains.
@ Buoy
Waypoint for ships.
@ RoadWaypoint
Waypoint for trucks and busses.
@ Oilrig
Heliport on an oil rig.
@ Airport
Airports and heliports, excluding the ones on oil rigs.
@ NewCargo
Trigger station on new cargo arrival.
std::set< Station *, StationCompare > StationList
List of stations.
@ Built
Trigger tile when built.
@ TileLoop
Trigger in the periodic tile loop.
@ NewCargo
Trigger station on new cargo arrival.
@ AcceptanceTick
Trigger station every 250 ticks.
static const uint MAX_LENGTH_STATION_NAME_CHARS
The maximum length of a station name in characters including '\0'.
@ Built
Triggered when the airport is built (for all tiles at the same time).
@ TileLoop
Triggered in the periodic tile loop.
@ NewCargo
Triggered when new cargo arrives at the station (for all tiles at the same time).
@ AcceptanceTick
Triggered every 250 ticks (for all tiles at the same time).
Types related to the station widgets.
@ WID_SV_CLOSE_AIRPORT
'Close airport' button.
@ WID_SV_ROADVEHS
List of scheduled road vehs button.
@ WID_SV_ACCEPT_RATING_LIST
List of accepted cargoes / rating of cargoes.
@ WID_SV_SHIPS
List of scheduled ships button.
@ WID_SV_TRAINS
List of scheduled trains button.
Definition of base types and functions in a cross-platform compatible way.
size_t Utf8StringLength(std::string_view str)
Get the length of an UTF-8 encoded string in number of characters and thus not the number of bytes th...
Definition string.cpp:351
Functions related to low-level strings.
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:336
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
Functions related to OTTD's strings.
auto MakeParameters(Args &&... args)
Helper to create the StringParameters with its own buffer with the given parameter values.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames).
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:73
@ Airplanes
Can planes land on this airport type?
Definition airport.h:162
Defines the data structure for an airport.
StringID name
name of this airport
SubstituteGRFFileProps grf_prop
Properties related to the grf file.
std::vector< AirportTileLayout > layouts
List of layouts composing the airport.
bool IsWithinMapBounds(uint8_t table, TileIndex index) const
Check if the airport would be within the map bounds at the given tile.
uint8_t size_y
size of airport in y direction
uint8_t size_x
size of airport in x direction
static const AirportSpec * Get(uint8_t type)
Retrieve airport spec for the given airport.
std::span< const HangarTileTable > depots
Position of the depots on the airports.
bool IsAvailable() const
Check whether this airport is available to build.
uint8_t noise_level
noise that this airport generates
Defines the data structure of each individual tile of an airport.
static const AirportTileSpec * Get(StationGfx gfx)
Retrieve airport tile spec for the given airport tile.
SubstituteGRFFileProps grf_prop
properties related the the grf file
StringID name
Tile Subname string, land information on this tile will give you "AirportName (TileSubname)".
static const AirportTileSpec * GetByTile(TileIndex tile)
Retrieve airport tile spec for the given airport tile.
All airport-related information.
AirportBlocks blocks
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
TileIndex GetRotatedTileFromOffset(TileIndexDiffC tidc) const
Add the tileoffset to the base tile of this airport but rotate it first.
uint GetHangarNum(TileIndex tile) const
Get the hangar number of the hangar at a specific tile.
Direction rotation
How this airport is rotated.
uint8_t type
Type of this airport,.
PersistentStorage * psa
Persistent storage for NewGRF airports.
uint GetNumHangars() const
Get the number of hangars on this airport.
TileIndex GetHangarTile(uint hangar_num) const
Get the first tile of the given hangar.
const AirportSpec * GetSpec() const
Get the AirportSpec that from the airport type of this airport.
uint8_t layout
Airport layout number.
Base class for all station-ish types.
StringID string_id
Default name (town area) of station.
TileIndex xy
Base tile of the station.
virtual void MoveSign(TileIndex new_xy)
Move this station's main coordinate somewhere else.
std::vector< SpecMapping< StationSpec > > speclist
List of rail station specs of this station.
StationFacilities facilities
The facilities that this station has.
std::string cached_name
NOSAVE: Cache of the resolved name of the station, if not using a custom name.
TileArea train_station
Tile area the train 'station' part covers.
StationAnimationTriggers cached_anim_triggers
NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
Owner owner
The owner of this station.
virtual void UpdateVirtCoord()=0
Update the coordinated of the sign (as shown in the viewport).
uint8_t delete_ctr
Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is ...
StationAnimationTriggers cached_roadstop_anim_triggers
NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing sh...
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
bool IsInUse() const
Check whether the base station currently is in use; in use means that it is not scheduled for deletio...
Town * town
The town this station is associated with.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
virtual bool TileBelongsToRailStation(TileIndex tile) const =0
Check whether a specific tile belongs to this station.
TrackedViewportSign sign
NOSAVE: Dimensions of sign.
TimerGameCalendar::Date build_date
Date of construction.
std::string name
Custom name.
VehicleType type
Type of vehicle.
Class for storing amounts of cargo.
Definition cargo_type.h:115
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:138
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:108
static IterateWrapper Iterate(size_t from=0)
Returns an iterable ensemble of all valid CargoSpec.
Definition cargotype.h:191
uint32_t station
Count of company owned station tiles.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
uint32_t water
Count of company owned track bits for canals.
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
T y
Y coordinate.
T x
X coordinate.
T z
Z coordinate.
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:76
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:55
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:56
std::string GetName() const
Get the name of this grf.
const struct GRFFile * grffile
grf file that introduced this entity
uint32_t grfid
grfid that introduced this entity.
bool HasGrfFile() const
Test if this entity was introduced by NewGRF.
StationSettings station
settings related to station management
FlowStatMap flows
Planned flows through this station.
StationCargoList cargo
The cargo packets of cargo waiting in this station.
Stores station stats for a single cargo.
uint max_waiting_cargo
Max cargo from this station waiting at any station.
bool HasRating() const
Does this cargo have a rating at this station?
uint8_t last_speed
Maximum speed (up to 255) of the last vehicle that tried to load this cargo.
uint8_t last_age
Age in years (up to 255) of the last vehicle that tried to load this cargo.
uint8_t time_since_pickup
Number of rating-intervals (up to 255) since the last vehicle tried to load this cargo.
States status
Status of this cargo, see State.
NodeID node
ID of node in link graph referring to this goods entry.
@ LastMonth
Set when cargo was delivered for final delivery last month.
@ Acceptance
Set when the station accepts the cargo currently for final deliveries.
@ Rating
This indicates whether a cargo has a rating at the station.
@ AcceptedBigtick
Set when cargo was delivered for final delivery during the current STATION_ACCEPTANCE_TICKS interval.
@ CurrentMonth
Set when cargo was delivered for final delivery this month.
const GoodsEntryData & GetData() const
Get optional cargo packet/flow data.
uint8_t amount_fract
Fractional part of the amount in the cargo list.
LinkGraphID link_graph
Link graph this station belongs to.
bool HasVehicleEverTriedLoading() const
Reports whether a vehicle has ever tried to load the cargo at this station.
uint8_t rating
Station rating for this cargo.
bool HasData() const
Test if this goods entry has optional cargo packet/flow data.
GoodsEntryData & GetOrCreateData()
Get optional cargo packet/flow data.
uint AvailableCount() const
Returns sum of cargo still available for loading at the station.
StationID GetVia(StationID source) const
Get the best next hop for a cargo packet from station source.
Defines the data structure for constructing industry.
SubstituteGRFFileProps grf_prop
properties related to the grf file
StringID name
Displayed name of the industry.
StringID station_name
Default name for nearby station.
bool enabled
entity still available (by default true).newgrf can disable it, though
IndustryLifeTypes life_type
This is also known as Industry production flag, in newgrf specs.
Defines the internal data of a functional industry.
Definition industry.h:62
IndustryType type
type of industry.
Definition industry.h:115
ProducedCargoes produced
produced cargo slots
Definition industry.h:110
Owner owner
owner of the industry. Which SHOULD always be (imho) OWNER_NONE
Definition industry.h:116
static Industry * GetByTile(TileIndex tile)
Get the industry of the given tile.
Definition industry.h:251
TileArea location
Location of the industry.
Definition industry.h:106
Station * neutral_station
Associated neutral station.
Definition industry.h:108
StationList stations_near
NOSAVE: List of nearby stations.
Definition industry.h:123
static uint SizeX()
Get the size of the map along the X.
Definition map_func.h:262
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:271
static uint Size()
Get the size of the map.
Definition map_func.h:280
Tindex class_index
Class index of this spec, invalid until class is allocated.
NewGRF supplied spritelayout.
static void Reset(TileIndex tile=INVALID_TILE, bool from_gui=true)
Reset the OrderBackups from GUI/game logic.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:384
If you change this, keep in mind that it is also saved in 2 other places:
Definition order_base.h:34
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:67
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
uint16_t w
The width of the area.
void Add(TileIndex to_add)
Add a single tile to a tile area; enlarge if needed.
Definition tilearea.cpp:43
void Clear()
Clears the 'tile area', i.e.
TileIndex tile
The base tile of the area.
uint16_t h
The height of the area.
OrthogonalTileArea & Expand(int rad)
Expand a tile area by rad tiles in each direction, keeping within map bounds.
Definition tilearea.cpp:123
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed).
Definition gfx_type.h:24
static Pool::IterateWrapper< BaseStation > Iterate(size_t from=0)
static Industry * Get(auto index)
static T * Create(Targs &&... args)
static LinkGraph * GetIfValid(auto index)
Road stop specification.
Money GetBuildCost(Price category) const
Get the cost for building a road stop of this type.
std::array< BridgeableTileInfo, 6 > bridgeable_info
Per tile layout bridge information.
CargoGRFFileProps grf_prop
Link to NewGRF.
Money GetClearCost(Price category) const
Get the cost for clearing a road stop of this type.
A Stop for a Road Vehicle.
RoadStop * next
Next stop of the given type at this station.
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp:253
void MakeDriveThrough()
Join this road stop to another 'base' road stop if possible; fill all necessary data to become an act...
Definition roadstop.cpp:60
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition roadstop.cpp:122
Buses, trucks and trams belong to this class.
Definition roadveh.h:105
uint8_t state
Definition roadveh.h:107
All ships have this type.
Definition ship.h:32
static Station * Create(Targs &&... args)
static bool IsExpected(const BaseStation *st)
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
static Station * Get(auto index)
static Station * GetIfValid(auto index)
static Station * From(BaseStation *st)
static RoadVehicle * From(Vehicle *v)
T * Last()
Get the last vehicle in the chain.
bool HasSpriteGroups() const
Check whether the entity has sprite groups.
Information to handle station action 0 property 24 correctly.
void SetUsed(StringID str)
Mark the given station name as used.
std::bitset< NUM_INDUSTRYTYPES > indtypes
Bit set indicating when an industry type has been found.
std::bitset< STR_SV_STNAME_FALLBACK - STR_SV_STNAME > used_names
Used default station suffixes.
bool IsAvailable(StringID str) const
Is the given station name available, and not already used?
StationRect - used to track station spread out rectangle - cheaper than scanning whole map.
bool PtInExtendedRect(int x, int y, int distance=0) const
Determines whether a given point (x, y) is within a certain distance of the station rectangle.
Definition station.cpp:567
bool serve_neutral_industries
company stations can serve industries with attached neutral stations
Station specification.
std::vector< BridgeableTileInfo > bridgeable_info
Per tile layout bridge information.
uint8_t disallowed_lengths
Bitmask of platform lengths available for the station.
StringID name
Name of this station.
uint8_t disallowed_platforms
Bitmask of number of platforms available for the station.
CargoGRFFileProps grf_prop
Link to NewGRF.
std::unordered_map< uint16_t, std::vector< uint8_t > > layouts
Custom platform layouts, keyed by platform and length combined.
std::vector< TileFlags > tileflags
List of tile flags.
std::vector< NewGRFSpriteLayout > renderdata
Number of tile layouts.
StationCallbackMasks callback_mask
Bitmask of station callbacks that have to be called.
@ NoWires
Tile should NOT contain catenary wires.
@ Pylons
Tile should contain catenary pylons.
@ Blocked
Tile is blocked to vehicles.
StationSpecFlags flags
Bitmask of flags, bit 0: use different sprite set; bit 1: divide cargo about by station size.
Station data structure.
TileArea GetTileArea(StationType type) const override
Get the tile area for a given station type.
RoadStop * bus_stops
All the road stops.
TileArea ship_station
Tile area the ship 'station' part covers.
IndustryType indtype
Industry type to get the name from.
TileArea docking_station
Tile area the docking tiles cover.
CargoTypes always_accepted
Bitmask of always accepted cargo types (by houses, HQs, industry tiles when industry doesn't accept c...
Industry * industry
NOSAVE: Associated industry for neutral stations. (Rebuilt on load from Industry->st).
void MoveSign(TileIndex new_xy) override
Move the station main coordinate somewhere else.
std::array< GoodsEntry, NUM_CARGO > goods
Goods at this station.
TileArea bus_station
Tile area the bus 'station' part covers.
BitmapTileArea catchment_tiles
NOSAVE: Set of individual tiles covered by catchment area.
void RecomputeCatchment(bool no_clear_nearby_lists=false)
Recompute tiles covered in our catchment area.
Definition station.cpp:469
void AfterStationTileSetChange(bool adding, StationType type)
After adding/removing tiles to station, update some station-related stuff.
Airport airport
Tile area the airport covers.
void UpdateVirtCoord() override
Update the virtual coords needed to draw the station sign.
TileArea truck_station
Tile area the truck 'station' part covers.
RoadStop * truck_stops
All the truck stops.
void AddFacility(StationFacility new_facility_bit, TileIndex facil_xy)
Called when new facility is built on the station.
Definition station.cpp:233
uint16_t subst_id
The id of the entity to replace.
Tile description for the 'land area information' tool.
Definition tile_cmd.h:38
uint16_t rail_speed
Speed limit of rail (bridges and track).
Definition tile_cmd.h:51
std::optional< std::string > grf
newGRF used for the tile contents
Definition tile_cmd.h:49
StringID station_name
Type of station within the class.
Definition tile_cmd.h:45
StringID str
Description of the tile.
Definition tile_cmd.h:39
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:43
std::array< Owner, 4 > owner
Name of the owner(s).
Definition tile_cmd.h:41
StringID airport_class
Name of the airport class.
Definition tile_cmd.h:46
StringID airport_name
Name of the airport.
Definition tile_cmd.h:47
uint16_t tram_speed
Speed limit of tram (bridges and track).
Definition tile_cmd.h:55
StringID roadtype
Type of road on the tile.
Definition tile_cmd.h:52
StringID tramtype
Type of tram on the tile.
Definition tile_cmd.h:54
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:50
uint16_t road_speed
Speed limit of road (bridges and track).
Definition tile_cmd.h:53
std::array< StringID, 4 > owner_type
Type of each owner.
Definition tile_cmd.h:42
StringID airport_tile_name
Name of the airport tile.
Definition tile_cmd.h:48
StringID station_class
Class of station.
Definition tile_cmd.h:44
Tile information, used while rendering the tile.
Definition tile_cmd.h:32
Slope tileh
Slope of the tile.
Definition tile_cmd.h:33
TileIndex tile
Tile index.
Definition tile_cmd.h:34
Town data structure.
Definition town.h:63
CompanyMask statues
which companies have a statue?
Definition town.h:81
TileIndex xy
town center tile
Definition town.h:64
uint16_t noise_reached
level of noise that all the airports are generating
Definition town.h:79
uint16_t MaxTownNoise() const
Calculate the max town noise.
Definition town.h:177
CompanyID exclusivity
which company has exclusivity
Definition town.h:86
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:87
'Train' is either a loco or a wagon.
Definition train.h:97
Trackdir GetVehicleTrackdir() const override
Get the tracks of the train vehicle.
Vehicle data structure.
Direction direction
facing
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
virtual void SetDestTile(TileIndex tile)
Set the destination of this vehicle.
VehStates vehstatus
Status.
virtual TileIndex GetOrderStationLocation(StationID station)
Determine the location for the station where the vehicle goes to next.
Order current_order
The current order (+ status, like: loading).
Vehicle * Next() const
Get the next vehicle of this vehicle.
Vehicle * NextShared() const
Get the next vehicle of the shared vehicle chain.
uint16_t cur_speed
current speed
bool IsFrontEngine() const
Check if the vehicle is a front engine.
TileIndex tile
Current tile index.
TileIndex dest_tile
Heading for this tile.
Owner owner
Which company owns the vehicle?
Representation of a waypoint.
TileArea road_waypoint_area
Tile area the road waypoint part covers.
uint16_t waypoint_flags
Waypoint flags, see WaypointFlags.
void UpdateVirtCoord() override
Update the virtual coords needed to draw the waypoint sign.
@ Source
town/industry is source of subsidised path
@ EnteredStation
The vehicle entered a station.
Definition tile_cmd.h:24
@ CannotEnter
The vehicle cannot enter the tile.
Definition tile_cmd.h:26
void AddProducedCargo(TileIndex tile, CargoArray &produced)
Obtain the produced cargo of a tile.
Definition tile_cmd.h:255
void AddAcceptedCargo(TileIndex tile, CargoArray &acceptance, CargoTypes &always_accepted)
Obtain cargo acceptance of a tile.
Definition tile_cmd.h:243
bool IsTileFlat(TileIndex tile, int *h)
Check if a given tile is flat.
Definition tile_map.cpp:94
std::tuple< Slope, int > GetTileSlopeZ(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.cpp:55
int GetTileMaxZ(TileIndex t)
Get top height of the tile inside the map.
Definition tile_map.cpp:135
int GetTileZ(TileIndex tile)
Get bottom height of the tile.
Definition tile_map.cpp:115
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
void SetTileOwner(Tile tile, Owner owner)
Sets the owner of a tile.
Definition tile_map.h:198
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
uint8_t GetAnimationFrame(Tile t)
Get the current animation frame.
Definition tile_map.h:250
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
TropicZone GetTropicZone(Tile tile)
Get the tropic zone.
Definition tile_map.h:238
void SetAnimationFrame(Tile t, uint8_t frame)
Set a new animation frame.
Definition tile_map.h:262
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:83
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static constexpr uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
@ Water
Water tile.
Definition tile_type.h:55
@ Station
A tile of a station or airport.
Definition tile_type.h:54
@ Industry
Part of an industry.
Definition tile_type.h:57
@ Trees
Tile with one or more trees.
Definition tile_type.h:53
@ House
A house by a town.
Definition tile_type.h:52
@ Road
A tile with road and/or tram tracks.
Definition tile_type.h:51
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
Definition of Interval and OneShot timers.
Definition of the game-calendar-timer.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.
Base of the town class.
Town * ClosestTownFromTile(TileIndex tile, uint threshold)
Return the town closest (in distance or ownership) to a given tile, within a given threshold.
Town * CalcClosestTownFromTile(TileIndex tile, uint threshold=UINT_MAX)
Return the town closest to the given tile within threshold.
HouseZone GetTownRadiusGroup(const Town *t, TileIndex tile)
Returns the bit corresponding to the town zone of the specified tile.
CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlags flags)
Checks whether the local authority allows construction of a new station (rail, road,...
TrackBits TrackToTrackBits(Track track)
Maps a Track to the corresponding TrackBits value.
Definition track_func.h:77
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
bool IsReversingRoadTrackdir(Trackdir dir)
Checks whether the trackdir means that we are reversing.
Definition track_func.h:678
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:247
TrackStatus CombineTrackStatus(TrackdirBits trackdirbits, TrackdirBits red_signals)
Builds a TrackStatus.
Definition track_func.h:390
TrackBits AxisToTrackBits(Axis a)
Maps an Axis to the corresponding TrackBits value.
Definition track_func.h:88
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:529
Track AxisToTrack(Axis a)
Convert an Axis to the corresponding Track AXIS_X -> TRACK_X AXIS_Y -> TRACK_Y Uses the fact that the...
Definition track_func.h:66
Track RemoveFirstTrack(TrackBits *tracks)
Removes first Track from TrackBits and returns it.
Definition track_func.h:131
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:441
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:39
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:41
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:38
@ TRACK_BIT_NONE
No track.
Definition track_type.h:36
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:37
@ TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:50
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:42
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
Track
These are used to specify a single track.
Definition track_type.h:19
@ TRACK_Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ TRACK_X
Track along the x-axis (north-east to south-west).
Definition track_type.h:21
Base for the train class.
bool TryPathReserve(Train *v, bool mark_as_stuck=false, bool first_tile_okay=false)
Try to reserve a path to a safe position.
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length)
Get the stop location of (the center) of the front vehicle of a train at a platform of a station.
@ TO_BUILDINGS
company buildings - depots, stations, HQ, ...
TransportType
Available types of transport.
@ TRANSPORT_RAIL
Transport by train.
@ TRANSPORT_ROAD
Transport by road vehicle.
@ TRANSPORT_WATER
Transport over water.
Functions that have tunnels and bridges in common.
CommandCost EnsureNoVehicleOnGround(TileIndex tile)
Ensure there is no vehicle at the ground at the given position.
Definition vehicle.cpp:556
@ TrainSlowing
Train is slowing down.
Functions related to vehicles.
@ VEH_INVALID
Non-existing type of vehicle.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
Functions and type for generating vehicle lists.
void FindVehiclesWithOrder(VehiclePredicate veh_pred, OrderPredicate ord_pred, VehicleFunc veh_func)
Find vehicles matching an order.
void OffsetGroundSprite(int x, int y)
Called when a foundation has been drawn for the current tile.
Definition viewport.cpp:591
void SetViewportStationRect(const Station *st, bool sel)
Select or deselect station for rectangle area highlight.
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:759
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int z, const SpriteBounds &bounds, bool transparent, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:658
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:769
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:579
Functions related to (drawing on) viewports.
static const int TILE_HEIGHT_STEP
One Z unit tile height difference is displayed as 50m.
Declarations for accessing the k-d tree of viewports.
Functions related to water management.
void TileLoop_Water(TileIndex tile)
Tile callback function signature for running periodic tile updates.
void CheckForDockingTile(TileIndex t)
Mark the supplied tile as a docking tile if it is suitable for docking.
bool IsPossibleDockingTile(Tile t)
Check whether it is feasible that the given tile could be a docking tile.
bool HasTileWaterGround(Tile t)
Checks whether the tile has water at the ground.
Definition water_map.h:353
bool IsTileOnWater(Tile t)
Tests if the tile was built on water.
Definition water_map.h:138
WaterClass
classes of water (for WaterTileType::Clear water tile type).
Definition water_map.h:39
@ Invalid
Used for industry tiles on land (also for oilrig if newgrf says so).
Definition water_map.h:43
@ Canal
Canal.
Definition water_map.h:41
@ Sea
Sea.
Definition water_map.h:40
bool HasTileWaterClass(Tile t)
Checks whether the tile has an waterclass associated.
Definition water_map.h:103
WaterClass GetWaterClass(Tile t)
Get the water class at a tile.
Definition water_map.h:114
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:375
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:364
bool IsWater(Tile t)
Is it a plain water tile?
Definition water_map.h:149
bool IsWaterTile(Tile t)
Is it a water tile with plain water?
Definition water_map.h:192
Base of waypoints.
@ WPF_ROAD
This is a road waypoint.
CommandCost RemoveBuoy(TileIndex tile, DoCommandFlags flags)
Remove a buoy.
Command definitions related to waypoints.
Functions related to waypoints.
void ShowWaypointWindow(const Waypoint *wp)
Show the window for the given waypoint.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1209
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3229
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3321
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
Mark a particular widget in a particular window as dirty (in need of repainting).
Definition window.cpp:3215
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3199
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3339
@ WC_STATION_LIST
Station list; Window numbers:
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_SELECT_STATION
Select station (when joining stations); Window numbers:
@ WC_STATION_VIEW
Station view; Window numbers:
@ WC_TOWN_VIEW
Town view; Window numbers:
Entry point for OpenTTD to YAPF's cache.
void YapfNotifyTrackLayoutChange(TileIndex tile, Track track)
Use this function to notify YAPF that track layout (or signal configuration) has change.
@ Station
station encountered (could be a target next time)
Definition yapf_type.hpp:26