OpenTTD Source 20260911-master-gee2b2ac12a
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).base()> 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).base());
242 }
243
249 {
250 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
251 this->used_names.set((str - STR_SV_STNAME).base());
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
463 SetWindowDirty(WindowClass::StationView, this->index);
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
508{
509 CargoTypes mask{};
510
511 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
512 if (it->status.Test(GoodsEntry::State::Acceptance)) mask.Set(static_cast<CargoType>(std::distance(std::begin(st->goods), it)));
513 }
514 return mask;
515}
516
523{
524 CargoTypes mask{};
525
526 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
527 if (it->TotalCount() > 0) mask.Set(static_cast<CargoType>(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->spread.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 : EnumRange(NUM_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 & CargoTypes{old_acc}.Flip();
666 CargoTypes rejects = CargoTypes{new_acc}.Flip() & old_acc;
667
668 /* Show news message if there are any changes */
669 if (accepts.Any()) ShowRejectOrAcceptNews(st, accepts, false);
670 if (rejects.Any()) ShowRejectOrAcceptNews(st, rejects, true);
671 }
672
673 /* redraw the station view since acceptance changed */
674 SetWindowWidgetDirty(WindowClass::StationView, st->index, WID_SV_ACCEPT_RATING_LIST);
675}
676
677static void UpdateStationSignCoord(BaseStation *st)
678{
679 if (st->spread.IsEmpty()) return; // no tiles belong to this station
680
681 /* clamp sign coord to be inside the station rect */
682 if (!st->spread.Contains(st->xy)) {
683 int left = TileX(st->spread.tile);
684 int right = left + st->spread.w - 1;
685 int top = TileY(st->spread.tile);
686 int bottom = top + st->spread.h - 1;
687
688 TileIndex new_xy = TileXY(ClampU(TileX(st->xy), left, right), ClampU(TileY(st->xy), top, bottom));
689 st->MoveSign(new_xy);
690 }
691
692 if (!Station::IsExpected(st)) return;
693 Station *full_station = Station::From(st);
694 for (const GoodsEntry &ge : full_station->goods) {
695 LinkGraphID lg = ge.link_graph;
696 if (!LinkGraph::IsValidID(lg)) continue;
697 (*LinkGraph::Get(lg))[ge.node].UpdateLocation(st->xy);
698 }
699}
700
710static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
711{
712 /* Find a deleted station close to us */
713 if (*st == nullptr && reuse) *st = GetClosestDeletedStation(area.tile);
714
715 if (*st != nullptr) {
716 if ((*st)->owner != _current_company) {
717 return CommandCost(CMD_ERROR);
718 }
719
720 CommandCost ret = CheckStationSpread((*st)->spread, area);
721 if (ret.Failed()) return ret;
722 } else {
723 /* allocate and initialize new station */
724 if (!Station::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_STATIONS_LOADING);
725
726 if (flags.Test(DoCommandFlag::Execute)) {
727 *st = Station::Create(area.tile);
728 _station_kdtree.Insert((*st)->index);
729
730 (*st)->town = ClosestTownFromTile(area.tile, UINT_MAX);
731 (*st)->string_id = GenerateStationName(*st, area.tile, name_class);
732
734 (*st)->town->have_ratings.Set(_current_company);
735 }
736 }
737 }
738 return CommandCost();
739}
740
748{
749 if (!st->IsInUse()) {
750 st->delete_ctr = 0;
751 InvalidateWindowData(WindowClass::StationList, st->owner, 0);
752 }
753 /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
754 UpdateStationSignCoord(st);
755}
756
763{
764 this->UpdateVirtCoord();
766
767 if (adding) {
768 this->RecomputeCatchment();
769 MarkCatchmentTilesDirty();
770 InvalidateWindowData(WindowClass::StationList, this->owner, 0);
771 } else {
772 MarkCatchmentTilesDirty();
773 }
774
775 switch (type) {
777 SetWindowWidgetDirty(WindowClass::StationView, this->index, WID_SV_TRAINS);
778 break;
780 break;
782 case StationType::Bus:
783 SetWindowWidgetDirty(WindowClass::StationView, this->index, WID_SV_ROADVEHS);
784 break;
786 SetWindowWidgetDirty(WindowClass::StationView, this->index, WID_SV_SHIPS);
787 break;
788 default: NOT_REACHED();
789 }
790
791 if (adding) {
792 UpdateStationAcceptance(this, false);
793 InvalidateWindowData(WindowClass::JoinStation, 0, 0);
794 } else {
796 this->RecomputeCatchment();
797 }
798
799}
800
802
812CommandCost CheckBuildableTile(TileIndex tile, DiagDirections invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge = true)
813{
814 if (check_bridge && IsBridgeAbove(tile)) {
815 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
816 }
817
819 if (ret.Failed()) return ret;
820
821 auto [tileh, z] = GetTileSlopeZ(tile);
822
823 /* Prohibit building if
824 * 1) The tile is "steep" (i.e. stretches two height levels).
825 * 2) The tile is non-flat and the build_on_slopes switch is disabled.
826 */
827 if ((!allow_steep && IsSteepSlope(tileh)) ||
828 ((!_settings_game.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
829 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
830 }
831
833 int flat_z = z + GetSlopeMaxZ(tileh);
834 if (tileh != SLOPE_FLAT) {
835 /* Forbid building if the tile faces a slope in a invalid direction. */
837 if (invalid_dirs.Test(dir) && !CanBuildDepotByTileh(dir, tileh)) {
838 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
839 }
840 }
842 }
843
844 /* The level of this tile must be equal to allowed_z. */
845 if (allowed_z < 0) {
846 /* First tile. */
847 allowed_z = flat_z;
848 } else if (allowed_z != flat_z) {
849 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
850 }
851
852 return cost;
853}
854
862{
864 int allowed_z = -1;
865
866 for (; tile_iter != INVALID_TILE; ++tile_iter) {
867 CommandCost ret = CheckBuildableTile(tile_iter, {}, allowed_z, true);
868 if (ret.Failed()) return ret;
869 cost.AddCost(ret.GetCost());
870
871 ret = Command<Commands::LandscapeClear>::Do(flags, tile_iter);
872 if (ret.Failed()) return ret;
873 cost.AddCost(ret.GetCost());
874 }
875
876 return cost;
877}
878
885{
887 STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION, // Rail
888 INVALID_STRING_ID, // Airport
889 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Truck
890 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Bus
891 INVALID_STRING_ID, // Oilrig
892 STR_ERROR_BRIDGE_TOO_LOW_FOR_DOCK, // Dock
893 STR_ERROR_BRIDGE_TOO_LOW_FOR_BUOY, // Buoy
894 STR_ERROR_BRIDGE_TOO_LOW_FOR_RAIL_WAYPOINT, // RailWaypoint
895 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROAD_WAYPOINT, // RoadWaypoint
896 };
897 return too_low_msgs[type];
898};
899
910static CommandCost IsStationBridgeAboveOk(TileIndex tile, std::span<const BridgeableTileInfo> bridgeable_info, StationType type, StationGfx layout, int bridge_height, StringID disallowed_msg = INVALID_STRING_ID)
911{
912 int height = layout < std::size(bridgeable_info) ? bridgeable_info[layout].height : 0;
913
914 if (height == 0) {
915 if (disallowed_msg != INVALID_STRING_ID) return CommandCost{disallowed_msg};
916 /* Get normal error message associated with clearing the tile. */
917 return Command<Commands::LandscapeClear>::Do(DoCommandFlag::Auto, tile);
918 }
919
920 int height_diff = GetTileMaxZ(tile) + height - bridge_height;
921 if (height_diff > 0) return CommandCostWithParam(GetBridgeTooLowMessageForStationType(type), height_diff * TILE_HEIGHT_STEP);
922 return CommandCost{};
923}
924
930static std::span<const BridgeableTileInfo> GetStationBridgeableTileInfo(StationType type)
931{
932 return _station_bridgeable_info[type];
933}
934
944{
945 if (!IsBridgeAbove(tile)) return CommandCost();
946
947 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
948 auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info;
949 return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
950}
951
961{
962 if (!IsBridgeAbove(tile)) return CommandCost();
963
964 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
965 auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info;
966 return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
967}
968
976{
977 if (!IsBridgeAbove(tile)) return CommandCost();
978
979 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
980 auto bridgeable_info = GetStationBridgeableTileInfo(StationType::Dock);
981 return IsStationBridgeAboveOk(tile, bridgeable_info, StationType::Dock, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
982}
983
990{
991 if (!IsBridgeAbove(tile)) return CommandCost();
992
993 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
994 auto bridgeable_info = GetStationBridgeableTileInfo(StationType::Buoy);
995 return IsStationBridgeAboveOk(tile, bridgeable_info, StationType::Buoy, 0, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
996}
997
1014static 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)
1015{
1017 DiagDirections invalid_dirs = AxisToDiagDirs(axis);
1018
1019 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1020 bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
1021
1022 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, false);
1023 if (ret.Failed()) return ret;
1024 cost.AddCost(ret.GetCost());
1025
1026 if (slope_cb) {
1027 /* Do slope check if requested. */
1028 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
1029 if (ret.Failed()) return ret;
1030 }
1031
1032 /* if station is set, then we have special handling to allow building on top of already existing stations.
1033 * so station points to StationID::Invalid() if we can build on any station.
1034 * Or it points to a station if we're only allowed to build on exactly that station. */
1035 if (station != nullptr && IsTileType(tile_cur, TileType::Station)) {
1036 if (!IsRailStation(tile_cur)) {
1037 return ClearTile_Station(tile_cur, DoCommandFlag::Auto); // get error message
1038 } else {
1039 StationID st = GetStationIndex(tile_cur);
1040 if (*station == StationID::Invalid()) {
1041 *station = st;
1042 } else if (*station != st) {
1043 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1044 }
1045 }
1046 } else {
1047 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
1048 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
1049 /* Don't overbuild signals. */
1050 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
1051
1052 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
1053 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
1054 /* The existing track must align with the desired station axis. */
1055 Track track = AxisToTrack(axis);
1056 if (GetTrackBits(tile_cur) == track) {
1057 /* Check for trains having a reservation for this tile. */
1058 if (GetRailReservationTrackBits(tile_cur).Test(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 + to_underlying(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) : RoadBits{};
1131 if (build_over_road && rb.Any(AxisToRoadBits(OtherAxis(axis)))) {
1132 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1133 switch (rb.Count()) {
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, RoadTramType::Road);
1149 if (road_rt != INVALID_ROADTYPE) {
1150 Owner road_owner = GetRoadOwner(cur_tile, RoadTramType::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 = GetRoadBits(cur_tile, RoadTramType::Road).Count();
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).Any() && 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, RoadTramType::Tram);
1173 if (tram_rt != INVALID_ROADTYPE) {
1174 Owner tram_owner = GetRoadOwner(cur_tile, RoadTramType::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 GetRoadBits(cur_tile, RoadTramType::Tram).Count() == 1)) {
1180 ret = CheckOwnership(tram_owner);
1181 if (ret.Failed()) return ret;
1182 }
1183 uint num_pieces = GetRoadBits(cur_tile, RoadTramType::Tram).Count();
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
1205template <>
1207{
1208 /* Use predefined layout if it exists. Mask bit zero which will indicate axis. */
1209 if (!stl.layout.empty()) return this->stl.layout[this->position] & ~1;
1210
1211 if (this->stl.length == 1) {
1212 /* Special case for 1-long platforms, all bare platforms except one small building. */
1213 return this->position == ((this->stl.platforms - 1) / 2) ? 2 : 0;
1214 }
1215
1216 if ((this->position < this->stl.length && (this->stl.platforms % 2 == 1))) {
1217 /* Number of tracks is odd, make the first platform bare with a small building. */
1218 return this->position == ((this->stl.length - 1) / 2) ? 2 : 0;
1219 }
1220
1221 if (this->stl.length > 4 && ((this->position % this->stl.length) == 0 || (this->position % this->stl.length) == this->stl.length - 1)) {
1222 /* Station is longer than 4 tiles, place bare platforms at either end. */
1223 return 0;
1224 }
1225
1226 /* None of the above so must be north or south part of larger station. */
1227 return (((this->position / this->stl.length) % 2) == (this->stl.platforms % 2)) ? 4 : 6;
1228}
1229
1243template <class T, class F>
1244CommandCost FindJoiningBaseStation(StringID error_message, StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1245{
1246 assert(*st == nullptr);
1247 bool check_surrounding = true;
1248
1249 if (existing_station != StationID::Invalid()) {
1250 if (adjacent && existing_station != station_to_join) {
1251 /* You can't build an adjacent station over the top of one that
1252 * already exists. */
1253 return CommandCost(error_message);
1254 } else {
1255 /* Extend the current station, and don't check whether it will
1256 * be near any other stations. */
1257 T *candidate = T::GetIfValid(existing_station);
1258 if (candidate != nullptr && filter(candidate)) *st = candidate;
1259 check_surrounding = (*st == nullptr);
1260 }
1261 } else {
1262 /* There's no station here. Don't check the tiles surrounding this
1263 * one if the company wanted to build an adjacent station. */
1264 if (adjacent) check_surrounding = false;
1265 }
1266
1267 if (check_surrounding) {
1268 /* Make sure there is no more than one other station around us that is owned by us. */
1269 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1270 if (ret.Failed()) return ret;
1271 }
1272
1273 /* Distant join */
1274 if (*st == nullptr && station_to_join != StationID::Invalid()) *st = T::GetIfValid(station_to_join);
1275
1276 return CommandCost();
1277}
1278
1288static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1289{
1290 return FindJoiningBaseStation<Station>(STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST, existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1291}
1292
1303CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1304{
1305 if (is_road) {
1306 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); });
1307 } else {
1308 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); });
1309 }
1310}
1311
1323
1335
1350static 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)
1351{
1353 bool length_price_ready = true;
1354 uint8_t tracknum = 0;
1355 int allowed_z = -1;
1356 for (TileIndex cur_tile : tile_area) {
1357 /* Clear the land below the station. */
1358 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1359 if (ret.Failed()) return ret;
1360
1361 /* Only add _price[Price::BuildStationRailLength] once for each valid plat_len. */
1362 if (tracknum == numtracks) {
1363 length_price_ready = true;
1364 tracknum = 0;
1365 } else {
1366 tracknum++;
1367 }
1368
1369 /* AddCost for new or rotated rail stations. */
1370 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1371 cost.AddCost(ret.GetCost());
1373 cost.AddCost(RailBuildCost(rt));
1374
1375 if (length_price_ready) {
1377 length_price_ready = false;
1378 }
1379 }
1380 }
1381
1382 return cost;
1383}
1384
1392{
1393 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1394 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlag::Pylons : StationSpec::TileFlags{};
1395 return statspec->tileflags[gfx];
1396}
1397
1404{
1405 const auto flags = GetStationTileFlags(GetStationGfx(tile), statspec);
1409}
1410
1425CommandCost 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)
1426{
1427 /* Does the authority allow this? */
1428 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1429 if (ret.Failed()) return ret;
1430
1431 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1432
1433 /* Check if the given station class is valid */
1434 if (spec_class.base() >= StationClass::GetClassCount()) return CMD_ERROR;
1435 const StationClass *cls = StationClass::Get(spec_class);
1436 if (IsWaypointClass(*cls)) return CMD_ERROR;
1437 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1438 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1439
1440 int w_org, h_org;
1441 if (axis == Axis::X) {
1442 w_org = plat_len;
1443 h_org = numtracks;
1444 } else {
1445 h_org = plat_len;
1446 w_org = numtracks;
1447 }
1448
1449 /* Check if the first tile and the last tile are valid */
1450 if (!IsValidTile(tile_org) || TileAddWrap(tile_org, w_org - 1, h_org - 1) == INVALID_TILE) return CMD_ERROR;
1451
1452 bool reuse = (station_to_join != NEW_STATION);
1453 if (!reuse) station_to_join = StationID::Invalid();
1454 bool distant_join = (station_to_join != StationID::Invalid());
1455
1456 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1457
1458 if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR;
1459
1460 /* these values are those that will be stored in train_tile and station_platforms */
1461 TileArea new_location(tile_org, w_org, h_org);
1462 ret = CheckStationSpread({}, new_location);
1463 if (ret.Failed()) return ret;
1464
1465 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1466 StationID est = StationID::Invalid();
1467 std::vector<Train *> affected_vehicles;
1468 /* Add construction and clearing expenses. */
1469 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1470 if (cost.Failed()) return cost;
1471
1472 Station *st = nullptr;
1473 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1474 if (ret.Failed()) return ret;
1475
1476 ret = BuildStationPart(&st, flags, reuse, new_location, StationNaming::Rail);
1477 if (ret.Failed()) return ret;
1478
1479 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1480 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1481 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1482
1483 RailStationTileLayout<StationType::Rail> stl{statspec, numtracks, plat_len};
1484 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1485 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1486 /* Don't check the layout if there's no bridge above anyway. */
1487 if (!IsBridgeAbove(tile)) continue;
1488
1489 StationGfx gfx = *it + to_underlying(axis);
1490 if (statspec != nullptr) {
1491 uint32_t platinfo = GetPlatformInfo(gfx, numtracks, plat_len, i, j, false);
1492 /* As the station is not yet completely finished, the station does not yet exist. */
1493 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, INVALID_TILE);
1494 if (callback != CALLBACK_FAILED && callback <= UINT8_MAX) gfx = (callback & ~1) + to_underlying(axis);
1495 }
1496
1497 ret = IsRailStationBridgeAboveOk(tile, statspec, StationType::Rail, gfx);
1498 if (ret.Failed()) return ret;
1499 }
1500 }
1501
1502 /* Check if we can allocate a custom stationspec to this station */
1503 auto specindex = AllocateSpecToStation(statspec, st);
1504 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1505
1506 if (statspec != nullptr) {
1507 /* Perform NewStation checks */
1508
1509 /* Check if the station size is permitted */
1510 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1511 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1512
1513 /* Check if the station is buildable */
1515 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1517 }
1518 }
1519
1520 if (flags.Test(DoCommandFlag::Execute)) {
1521 st->AddFacility(StationFacility::Train, new_location.tile);
1522
1523 st->train_station.Add(new_location);
1524 st->spread.Add(new_location);
1525
1526 if (specindex.has_value()) AssignSpecToStation(statspec, st, *specindex);
1527 if (statspec != nullptr) {
1528 /* Include this station spec's animation trigger bitmask
1529 * in the station's cached copy. */
1530 st->cached_anim_triggers.Set(statspec->animation.triggers);
1531 }
1532
1533 Track track = AxisToTrack(axis);
1534 Company *c = Company::Get(st->owner);
1535 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1536 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1537 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1538 /* Check for trains having a reservation for this tile. */
1540 if (v != nullptr) {
1541 affected_vehicles.push_back(v);
1543 }
1544 }
1545
1546 /* Railtype can change when overbuilding. */
1547 if (IsRailStationTile(tile)) {
1548 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1550 }
1551
1552 /* Remove animation if overbuilding */
1553 DeleteAnimatedTile(tile);
1554 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1555
1556 MakeRailStation(tile, st->owner, st->index, axis, *it, rt);
1557 /* Free the spec if we overbuild something */
1558 DeallocateSpecFromStation(st, old_specindex);
1559 if (statspec == nullptr) DeleteNewGRFInspectWindow(GrfSpecFeature::Stations, tile);
1560
1561 SetCustomStationSpecIndex(tile, *specindex);
1562 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1563 SetAnimationFrame(tile, 0);
1564
1565 if (statspec != nullptr) {
1566 uint32_t platinfo = GetPlatformInfo(*it + to_underlying(axis), numtracks, plat_len, i, j, false);
1567
1568 /* As the station is not yet completely finished, the station does not yet exist. */
1569 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1570 if (callback != CALLBACK_FAILED) {
1571 if (callback <= UINT8_MAX) {
1572 SetStationGfx(tile, (callback & ~1) + to_underlying(axis));
1573 } else {
1575 }
1576 }
1577
1578 /* Trigger station animation -- after building? */
1579 TriggerStationAnimation(st, tile, StationAnimationTrigger::Built);
1580 }
1581
1582 SetRailStationTileFlags(tile, statspec);
1583
1584 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1586 }
1587 AddTrackToSignalBuffer(tile_track, track, _current_company);
1588 YapfNotifyTrackLayoutChange(tile_track, track);
1589 }
1590
1591 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1592 /* Restore reservations of trains. */
1593 RestoreTrainReservation(affected_vehicles[i]);
1594 }
1595
1596 /* Check whether we need to expand the reservation of trains already on the station. */
1597 TileArea update_reservation_area;
1598 if (axis == Axis::X) {
1599 update_reservation_area = TileArea(tile_org, 1, numtracks);
1600 } else {
1601 update_reservation_area = TileArea(tile_org, numtracks, 1);
1602 }
1603
1604 for (TileIndex tile : update_reservation_area) {
1605 /* Don't even try to make eye candy parts reserved. */
1606 if (IsStationTileBlocked(tile)) continue;
1607
1608 DiagDirection dir = AxisToDiagDir(axis);
1609 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1610 TileIndex platform_begin = tile;
1611 TileIndex platform_end = tile;
1612
1613 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1614 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1615 platform_begin = next_tile;
1616 }
1617 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1618 platform_end = next_tile;
1619 }
1620
1621 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1622 bool reservation = false;
1623 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1624 reservation = HasStationReservation(t);
1625 }
1626
1627 if (reservation) {
1628 SetRailStationPlatformReservation(platform_begin, dir, true);
1629 }
1630 }
1631
1632 st->MarkTilesDirty(false);
1634 }
1635
1636 return cost;
1637}
1638
1639static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1640{
1641restart:
1642
1643 /* too small? */
1644 if (ta.w != 0 && ta.h != 0) {
1645 /* check the left side, x = constant, y changes */
1646 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1647 /* the left side is unused? */
1648 if (++i == ta.h) {
1649 ta.tile += TileDiffXY(1, 0);
1650 ta.w--;
1651 goto restart;
1652 }
1653 }
1654
1655 /* check the right side, x = constant, y changes */
1656 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1657 /* the right side is unused? */
1658 if (++i == ta.h) {
1659 ta.w--;
1660 goto restart;
1661 }
1662 }
1663
1664 /* check the upper side, y = constant, x changes */
1665 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1666 /* the left side is unused? */
1667 if (++i == ta.w) {
1668 ta.tile += TileDiffXY(0, 1);
1669 ta.h--;
1670 goto restart;
1671 }
1672 }
1673
1674 /* check the lower side, y = constant, x changes */
1675 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1676 /* the left side is unused? */
1677 if (++i == ta.w) {
1678 ta.h--;
1679 goto restart;
1680 }
1681 }
1682 } else {
1683 ta.Clear();
1684 }
1685
1686 return ta;
1687}
1688
1694{
1695 static constexpr auto func = [](BaseStation *st, TileIndex tile) { return IsTileType(tile, TileType::Station) && GetStationIndex(tile) == st->index; };
1696 st->spread = MakeStationAreaSmaller(st, st->spread, func);
1697}
1698
1699static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1700{
1701 return st->TileBelongsToRailStation(tile);
1702}
1703
1704static void MakeRailStationAreaSmaller(BaseStation *st)
1705{
1706 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1707}
1708
1709static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1710{
1711 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1712}
1713
1714static void MakeShipStationAreaSmaller(Station *st)
1715{
1716 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1717 UpdateStationDockingTiles(st);
1718}
1719
1720static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1721{
1722 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1723}
1724
1725void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1726{
1727 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1728}
1729
1740template <class T>
1741CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
1742{
1743 /* Count of the number of tiles removed */
1744 int quantity = 0;
1746 /* Accumulator for the errors seen during clearing. If no errors happen,
1747 * and the quantity is 0 there is no station. Otherwise it will be one
1748 * of the other error that got accumulated. */
1749 CommandCost error;
1750
1751 /* Do the action for every tile into the area */
1752 for (TileIndex tile : ta) {
1753 /* Make sure the specified tile is a rail station */
1754 if (!HasStationTileRail(tile)) continue;
1755
1756 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1758 error.AddCost(std::move(ret));
1759 if (error.Failed()) continue;
1760
1761 /* Check ownership of station */
1762 T *st = T::GetByTile(tile);
1763 if (st == nullptr) continue;
1764
1766 ret = CheckOwnership(st->owner);
1767 error.AddCost(std::move(ret));
1768 if (error.Failed()) continue;
1769 }
1770
1771 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1772 quantity++;
1773
1774 if (keep_rail || IsStationTileBlocked(tile)) {
1775 /* Don't refund the 'steel' of the track when we keep the
1776 * rail, or when the tile didn't have any rail at all. */
1777 total_cost.AddCost(-_price[Price::ClearRail]);
1778 }
1779
1780 if (flags.Test(DoCommandFlag::Execute)) {
1781 /* read variables before the station tile is removed */
1782 uint specindex = GetCustomStationSpecIndex(tile);
1783 Track track = GetRailStationTrack(tile);
1784 Owner owner = GetTileOwner(tile);
1785 RailType rt = GetRailType(tile);
1786 Train *v = nullptr;
1787
1788 if (HasStationReservation(tile)) {
1789 v = GetTrainForReservation(tile, track);
1790 if (v != nullptr) FreeTrainReservation(v);
1791 }
1792
1793 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1794 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1795
1796 DoClearSquare(tile);
1798 if (build_rail) MakeRailNormal(tile, owner, track, rt);
1799 Company::Get(owner)->infrastructure.station--;
1801
1802 st->tile_waiting_random_triggers.erase(tile);
1803 AddTrackToSignalBuffer(tile, track, owner);
1804 YapfNotifyTrackLayoutChange(tile, track);
1805
1806 DeallocateSpecFromStation(st, specindex);
1807
1808 include(affected_stations, st);
1809
1810 if (v != nullptr) RestoreTrainReservation(v);
1811 }
1812 }
1813
1814 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1815
1816 for (T *st : affected_stations) {
1817
1818 /* now we need to make the "spanned" area of the railway station smaller
1819 * if we deleted something at the edges.
1820 * we also need to adjust train_tile. */
1821 MakeRailStationAreaSmaller(st);
1823 UpdateStationSignCoord(st);
1824
1825 /* if we deleted the whole station, delete the train facility. */
1826 if (st->train_station.IsEmpty()) {
1828 SetWindowClassesDirty(WindowClass::VehicleOrders);
1829 SetWindowWidgetDirty(WindowClass::StationView, st->index, WID_SV_TRAINS);
1830 MarkCatchmentTilesDirty();
1831 st->UpdateVirtCoord();
1833 }
1834 }
1835
1836 total_cost.AddCost(quantity * removal_cost);
1837 return total_cost;
1838}
1839
1850{
1851 if (end == 0) end = start;
1852 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1853
1854 TileArea ta(start, end);
1855 std::vector<Station *> affected_stations;
1856
1857 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[Price::ClearStationRail], keep_rail);
1858 if (ret.Failed()) return ret;
1859
1860 /* Do all station specific functions here. */
1861 for (Station *st : affected_stations) {
1862
1863 if (st->train_station.IsEmpty()) SetWindowWidgetDirty(WindowClass::StationView, st->index, WID_SV_TRAINS);
1864 st->MarkTilesDirty(false);
1865 MarkCatchmentTilesDirty();
1866 st->RecomputeCatchment();
1867 }
1868
1869 /* Now apply the rail cost to the number that we deleted */
1870 return ret;
1871}
1872
1883{
1884 if (end == 0) end = start;
1885 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1886
1887 TileArea ta(start, end);
1888 std::vector<Waypoint *> affected_stations;
1889
1890 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[Price::ClearWaypointRail], keep_rail);
1891}
1892
1893
1902template <class T>
1903CommandCost RemoveRailStation(T *st, DoCommandFlags flags, Money removal_cost)
1904{
1905 /* Current company owns the station? */
1907 CommandCost ret = CheckOwnership(st->owner);
1908 if (ret.Failed()) return ret;
1909 }
1910
1911 /* determine width and height of platforms */
1912 TileArea ta = st->train_station;
1913
1914 assert(ta.w != 0 && ta.h != 0);
1915
1917 /* clear all areas of the station */
1918 for (TileIndex tile : ta) {
1919 /* only remove tiles that are actually train station tiles */
1920 if (st->TileBelongsToRailStation(tile)) {
1921 std::vector<T*> affected_stations; // dummy
1922 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1923 if (ret.Failed()) return ret;
1924 cost.AddCost(ret.GetCost());
1925 }
1926 }
1927
1928 return cost;
1929}
1930
1938{
1939 /* if there is flooding, remove platforms tile by tile */
1941 return Command<Commands::RemoveFromRailStation>::Do(DoCommandFlag::Execute, tile, TileIndex{}, false);
1942 }
1943
1944 Station *st = Station::GetByTile(tile);
1946
1948
1949 return cost;
1950}
1951
1959{
1960 /* if there is flooding, remove waypoints tile by tile */
1962 return Command<Commands::RemoveFromRailWaypoint>::Do(DoCommandFlag::Execute, tile, TileIndex{}, false);
1963 }
1964
1966}
1967
1968
1974static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
1975{
1976 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1977
1978 if (*primary_stop == nullptr) {
1979 /* we have no roadstop of the type yet, so write a "primary stop" */
1980 return primary_stop;
1981 } else {
1982 /* there are stops already, so append to the end of the list */
1983 RoadStop *stop = *primary_stop;
1984 while (stop->next != nullptr) stop = stop->next;
1985 return &stop->next;
1986 }
1987}
1988
1989static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1990CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1991
2001static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
2002{
2003 return FindJoiningBaseStation<Station>(STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST, existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
2004}
2005
2020CommandCost 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)
2021{
2022 DiagDirections invalid_dirs = is_drive_through ? AxisToDiagDirs(axis) : ddir;
2023
2024 /* Check every tile in the area. */
2025 int allowed_z = -1;
2027 for (TileIndex cur_tile : tile_area) {
2028 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, roadstopspec, flags, invalid_dirs, is_drive_through, station_type, axis, station, rt);
2029 if (ret.Failed()) return ret;
2030
2031 bool is_preexisting_roadstop = IsTileType(cur_tile, TileType::Station) && IsAnyRoadStop(cur_tile);
2032
2033 /* Only add costs if a stop doesn't already exist in the location */
2034 if (!is_preexisting_roadstop) {
2035 cost.AddCost(ret.GetCost());
2036 cost.AddCost(unit_cost);
2037 }
2038 }
2039
2040 return cost;
2041}
2042
2059CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
2060 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
2061{
2062 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= RoadStopType::End) return CMD_ERROR;
2063 bool reuse = (station_to_join != NEW_STATION);
2064 if (!reuse) station_to_join = StationID::Invalid();
2065 bool distant_join = (station_to_join != StationID::Invalid());
2066
2067 /* Check if the given station class is valid */
2068 if (spec_class.base() >= RoadStopClass::GetClassCount()) return CMD_ERROR;
2069 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
2070 if (IsWaypointClass(*cls)) return CMD_ERROR;
2071 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
2072
2073 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
2074 if (roadstopspec != nullptr) {
2075 if (stop_type == RoadStopType::Truck && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2076 if (stop_type == RoadStopType::Bus && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2077 if (!is_drive_through && roadstopspec->flags.Test(RoadStopSpecFlag::DriveThroughOnly)) return CMD_ERROR;
2078 }
2079
2080 /* Check if the requested road stop is too big */
2081 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2082 /* Check for incorrect width / length. */
2083 if (width == 0 || length == 0) return CMD_ERROR;
2084 /* Check if the first tile and the last tile are valid */
2085 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
2086
2087 TileArea roadstop_area(tile, width, length);
2088 if (CommandCost ret = CheckStationSpread({}, roadstop_area); ret.Failed()) return ret;
2089
2090 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2091
2092 /* Trams only have drive through stops */
2093 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
2094
2095 Axis axis = DiagDirToAxis(ddir);
2096
2098 if (ret.Failed()) return ret;
2099
2100 bool is_truck_stop = stop_type != RoadStopType::Bus;
2101
2102 /* Total road stop cost. */
2103 Money unit_cost;
2104 if (roadstopspec != nullptr) {
2105 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? Price::BuildStationTruck : Price::BuildStationBus);
2106 } else {
2107 unit_cost = _price[is_truck_stop ? Price::BuildStationTruck : Price::BuildStationBus];
2108 }
2109 StationID est = StationID::Invalid();
2110 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, roadstopspec, axis, ddir, &est, rt, unit_cost);
2111 if (cost.Failed()) return cost;
2112
2113 Station *st = nullptr;
2114 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2115 if (ret.Failed()) return ret;
2116
2117 /* Check if this number of road stops can be allocated. */
2118 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);
2119
2120 ret = BuildStationPart(&st, flags, reuse, roadstop_area, StationNaming::Road);
2121 if (ret.Failed()) return ret;
2122
2123 /* Check if we can allocate a custom stationspec to this station */
2124 auto specindex = AllocateSpecToRoadStop(roadstopspec, st);
2125 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2126
2127 if (roadstopspec != nullptr) {
2128 /* Perform NewGRF checks */
2129
2130 /* Check if the road stop is buildable */
2131 if (roadstopspec->callback_mask.Test(RoadStopCallbackMask::Avail)) {
2132 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? StationType::Truck : StationType::Bus, 0);
2133 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2134 }
2135 }
2136
2137 if (flags.Test(DoCommandFlag::Execute)) {
2138 if (specindex.has_value()) AssignSpecToRoadStop(roadstopspec, st, *specindex);
2139 /* Check every tile in the area. */
2140 for (TileIndex cur_tile : roadstop_area) {
2141 /* Get existing road types and owners before any tile clearing */
2142 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RoadTramType::Road) : INVALID_ROADTYPE;
2143 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RoadTramType::Tram) : INVALID_ROADTYPE;
2144 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RoadTramType::Road) : _current_company;
2145 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RoadTramType::Tram) : _current_company;
2146
2147 if (IsTileType(cur_tile, TileType::Station) && IsStationRoadStop(cur_tile)) {
2148 RemoveRoadStop(cur_tile, flags, *specindex);
2149 }
2150
2151 if (roadstopspec != nullptr) {
2152 /* Include this road stop spec's animation trigger bitmask
2153 * in the station's cached copy. */
2154 st->cached_roadstop_anim_triggers.Set(roadstopspec->animation.triggers);
2155 }
2156
2157 RoadStop *road_stop = RoadStop::Create(cur_tile);
2158 /* Insert into linked list of RoadStops. */
2159 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2160 *currstop = road_stop;
2161
2162 if (is_truck_stop) {
2163 st->truck_station.Add(cur_tile);
2164 } else {
2165 st->bus_station.Add(cur_tile);
2166 }
2167
2168 /* Initialize an empty station. */
2169 st->AddFacility(is_truck_stop ? StationFacility::TruckStop : StationFacility::BusStop, cur_tile);
2170
2171 st->spread.Add(cur_tile);
2172
2173 RoadStopType rs_type = is_truck_stop ? RoadStopType::Truck : RoadStopType::Bus;
2174 if (is_drive_through) {
2175 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2176 * bits first. */
2177 if (IsNormalRoadTile(cur_tile)) {
2178 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -static_cast<int>(GetRoadBits(cur_tile, RoadTramType::Road).Count()));
2179 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -static_cast<int>(GetRoadBits(cur_tile, RoadTramType::Tram).Count()));
2180 }
2181
2182 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2183 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2184
2185 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == RoadStopType::Bus ? StationType::Bus : StationType::Truck), road_rt, tram_rt, axis);
2186 road_stop->MakeDriveThrough();
2187 } else {
2188 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2189 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2190 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2191 }
2194 Company::Get(st->owner)->infrastructure.station++;
2195
2196 SetCustomRoadStopSpecIndex(cur_tile, *specindex);
2197 if (roadstopspec != nullptr) {
2198 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2199 TriggerRoadStopAnimation(st, cur_tile, StationAnimationTrigger::Built);
2200 }
2201
2202 MarkTileDirtyByTile(cur_tile);
2203 }
2204
2205 if (st != nullptr) {
2207 }
2208 }
2209 return cost;
2210}
2211
2219static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2220{
2221 Station *st = Station::GetByTile(tile);
2222
2224 CommandCost ret = CheckOwnership(st->owner);
2225 if (ret.Failed()) return ret;
2226 }
2227
2228 bool is_truck = IsTruckStop(tile);
2229
2230 RoadStop **primary_stop;
2231 RoadStop *cur_stop;
2232 if (is_truck) { // truck stop
2233 primary_stop = &st->truck_stops;
2234 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Truck);
2235 } else {
2236 primary_stop = &st->bus_stops;
2237 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Bus);
2238 }
2239
2240 assert(cur_stop != nullptr);
2241
2242 /* don't do the check for drive-through road stops when company bankrupts */
2244 /* remove the 'going through road stop' status from all vehicles on that tile */
2245 if (flags.Test(DoCommandFlag::Execute)) {
2246 for (Vehicle *v : VehiclesOnTile(tile)) {
2247 if (v->type != VehicleType::Road) continue;
2248 /* Okay... we are a road vehicle on a drive through road stop.
2249 * But that road stop has just been removed, so we need to make
2250 * sure we are in a valid state... however, vehicles can also
2251 * turn on road stop tiles, so only clear the 'road stop' state
2252 * bits and only when the state was 'in road stop', otherwise
2253 * we'll end up clearing the turn around bits. */
2256 }
2257 }
2258 } else {
2260 if (ret.Failed()) return ret;
2261 }
2262
2263 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2264
2265 if (flags.Test(DoCommandFlag::Execute)) {
2266 if (*primary_stop == cur_stop) {
2267 /* removed the first stop in the list */
2268 *primary_stop = cur_stop->next;
2269 /* removed the only stop? */
2270 if (*primary_stop == nullptr) {
2272 SetWindowClassesDirty(WindowClass::VehicleOrders);
2273 }
2274 } else {
2275 /* tell the predecessor in the list to skip this stop */
2276 RoadStop *pred = *primary_stop;
2277 while (pred->next != cur_stop) pred = pred->next;
2278 pred->next = cur_stop->next;
2279 }
2280
2281 /* Update company infrastructure counts. */
2282 for (RoadTramType rtt : ROADTRAMTYPES_ALL) {
2283 RoadType rt = GetRoadType(tile, rtt);
2285 }
2286
2287 Company::Get(st->owner)->infrastructure.station--;
2289
2290 uint specindex = GetCustomRoadStopSpecIndex(tile);
2291
2293
2294 if (IsDriveThroughStopTile(tile)) {
2295 /* Clears the tile for us */
2296 cur_stop->ClearDriveThrough();
2297 DeleteAnimatedTile(tile);
2298 } else {
2299 DoClearSquare(tile);
2300 }
2301
2302 delete cur_stop;
2303
2304 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2305 * this station, then look for any currently heading to the tile. */
2306 StationID station_id = st->index;
2308 [](const Vehicle *v) { return v->type == VehicleType::Road; },
2309 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2310 [station_id, tile](Vehicle *v) {
2311 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2312 v->SetDestTile(v->GetOrderStationLocation(station_id));
2313 }
2314 }
2315 );
2316
2318
2319 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
2320
2321 st->tile_waiting_random_triggers.erase(tile);
2322 st->RemoveRoadStopTileData(tile);
2323 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2324
2325 /* Update the tile area of the truck/bus stop */
2326 if (is_truck) {
2327 st->truck_station.Clear();
2328 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2329 } else {
2330 st->bus_station.Clear();
2331 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2332 }
2333 }
2334
2336 return CommandCost(ExpensesType::Construction, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2337}
2338
2346CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2347{
2348 Waypoint *wp = Waypoint::GetByTile(tile);
2349
2351 CommandCost ret = CheckOwnership(wp->owner);
2352 if (ret.Failed()) return ret;
2353 }
2354
2355 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2356 if (!flags.Test(DoCommandFlag::Bankrupt)) {
2358 if (ret.Failed()) return ret;
2359 }
2360
2361 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2362
2363 if (flags.Test(DoCommandFlag::Execute)) {
2364 /* Update company infrastructure counts. */
2365 for (RoadTramType rtt : ROADTRAMTYPES_ALL) {
2366 RoadType rt = GetRoadType(tile, rtt);
2368 }
2369
2370 Company::Get(wp->owner)->infrastructure.station--;
2372
2373 uint specindex = GetCustomRoadStopSpecIndex(tile);
2374
2376
2377 DoClearSquare(tile);
2378
2380
2381 wp->tile_waiting_random_triggers.erase(tile);
2382 wp->RemoveRoadStopTileData(tile);
2383 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2384
2385 if (replacement_spec_index < 0) {
2386 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2387
2388 UpdateStationSignCoord(wp);
2389
2390 /* if we deleted the whole waypoint, delete the road facility. */
2391 if (wp->road_waypoint_area.IsEmpty()) {
2393 SetWindowWidgetDirty(WindowClass::StationView, wp->index, WID_SV_ROADVEHS);
2394 wp->UpdateVirtCoord();
2396 }
2397 }
2398 }
2399
2401}
2402
2411static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
2412{
2414 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2415 bool had_success = false;
2416
2417 for (TileIndex cur_tile : roadstop_area) {
2418 /* Make sure the specified tile is a road stop of the correct type */
2419 if (!IsTileType(cur_tile, TileType::Station) || !IsAnyRoadStop(cur_tile) || IsRoadWaypoint(cur_tile) != road_waypoint) continue;
2420
2421 /* Save information on to-be-restored roads before the stop is removed. */
2422 RoadBits road_bits{};
2425 if (IsDriveThroughStopTile(cur_tile)) {
2426 for (RoadTramType rtt : ROADTRAMTYPES_ALL) {
2427 road_type[rtt] = GetRoadType(cur_tile, rtt);
2428 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2429 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2430 /* If we don't want to preserve our roads then restore only roads of others. */
2431 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2432 }
2433 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2434 }
2435
2436 CommandCost ret;
2437 if (road_waypoint) {
2438 ret = RemoveRoadWaypointStop(cur_tile, flags);
2439 } else {
2440 ret = RemoveRoadStop(cur_tile, flags);
2441 }
2442 if (ret.Failed()) {
2443 last_error = std::move(ret);
2444 continue;
2445 }
2446 cost.AddCost(ret.GetCost());
2447 had_success = true;
2448
2449 /* Restore roads. */
2451 MakeRoadNormal(cur_tile, road_bits, road_type[RoadTramType::Road], road_type[RoadTramType::Tram], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2452 road_owner[RoadTramType::Road], road_owner[RoadTramType::Tram]);
2453
2454 /* Update company infrastructure counts. */
2455 int count = road_bits.Count();
2458 }
2459 }
2460
2461 return had_success ? cost : last_error;
2462}
2463
2474CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2475{
2476 if (stop_type >= RoadStopType::End) return CMD_ERROR;
2477 /* Check for incorrect width / height. */
2478 if (width == 0 || height == 0) return CMD_ERROR;
2479 /* Check if the first tile and the last tile are valid */
2480 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2481 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2482 if (remove_road && flags.Test(DoCommandFlag::Bankrupt)) return CMD_ERROR;
2483
2484 TileArea roadstop_area(tile, width, height);
2485
2486 return RemoveGenericRoadStop(flags, roadstop_area, false, remove_road);
2487}
2488
2497{
2498 if (end == 0) end = start;
2499 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2500
2501 TileArea roadstop_area(start, end);
2502
2503 return RemoveGenericRoadStop(flags, roadstop_area, true, false);
2504}
2505
2514uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2515{
2516 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2517 * So no need to go any further*/
2518 if (as->noise_level < 2) return as->noise_level;
2519
2520 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2521 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2522 * Basically, it says that the less tolerant a town is, the bigger the distance before
2523 * an actual decrease can be granted */
2524 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2525
2526 /* now, we want to have the distance segmented using the distance judged bareable by town
2527 * This will give us the coefficient of reduction the distance provides. */
2528 uint noise_reduction = distance / town_tolerance_distance;
2529
2530 /* If the noise reduction equals the airport noise itself, don't give it for free.
2531 * Otherwise, simply reduce the airport's level. */
2532 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2533}
2534
2545Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2546{
2547 assert(Town::GetNumItems() > 0);
2548
2549 Town *nearest = nullptr;
2550
2551 auto width = as->size_x;
2552 auto height = as->size_y;
2553 if (rotation == Direction::E || rotation == Direction::W) std::swap(width, height);
2554
2555 uint perimeter_min_x = TileX(tile);
2556 uint perimeter_min_y = TileY(tile);
2557 uint perimeter_max_x = perimeter_min_x + width - 1;
2558 uint perimeter_max_y = perimeter_min_y + height - 1;
2559
2560 mindist = UINT_MAX - 1; // prevent overflow
2561
2562 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2563 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2564 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2565 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) {
2566 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2567 if (t == nullptr) continue;
2568
2569 uint dist = DistanceManhattan(t->xy, cur_tile);
2570 if (dist == mindist && t->index < nearest->index) nearest = t;
2571 if (dist < mindist) {
2572 nearest = t;
2573 mindist = dist;
2574 }
2575 }
2576 }
2577
2578 return nearest;
2579}
2580
2588static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2589{
2591}
2592
2593
2596{
2597 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2598
2599 for (const Station *st : Station::Iterate()) {
2600 if (!st->airport.IsEmpty() && st->airport.type != AT_OILRIG) {
2601 uint dist;
2602 Town *nearest = AirportGetNearestTown(st, dist);
2603 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2604 }
2605 }
2606}
2607
2618CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2619{
2620 bool reuse = (station_to_join != NEW_STATION);
2621 if (!reuse) station_to_join = StationID::Invalid();
2622 bool distant_join = (station_to_join != StationID::Invalid());
2623
2624 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2625
2626 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2627
2629 if (ret.Failed()) return ret;
2630
2631 /* Check if a valid, buildable airport was chosen for construction */
2632 const AirportSpec *as = AirportSpec::Get(airport_type);
2633 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2634 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2635
2636 Direction rotation = as->layouts[layout].rotation;
2637 int w = as->size_x;
2638 int h = as->size_y;
2639 if (rotation == Direction::E || rotation == Direction::W) std::swap(w, h);
2640 TileArea airport_area = TileArea(tile, w, h);
2641
2642 ret = CheckStationSpread({}, airport_area);
2643 if (ret.Failed()) return ret;
2644
2645 AirportTileTableIterator tile_iter(as->layouts[layout].tiles, tile);
2646 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2647 if (cost.Failed()) return cost;
2648
2649 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2650 uint dist;
2651 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2652 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2653
2654 /* Check if local auth would allow a new airport */
2655 StringID authority_refuse_message = STR_NULL;
2656 Town *authority_refuse_town = nullptr;
2657
2658 if (_settings_game.economy.station_noise_level) {
2659 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2660 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2661 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2662 authority_refuse_town = nearest;
2663 }
2664 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2665 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2666 uint num = 0;
2667 for (const Station *st : Station::Iterate()) {
2668 if (st->town == t && st->facilities.Test(StationFacility::Airport) && st->airport.type != AT_OILRIG) num++;
2669 }
2670 if (num >= 2) {
2671 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2672 authority_refuse_town = t;
2673 }
2674 }
2675
2676 if (authority_refuse_message != STR_NULL) {
2677 return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index);
2678 }
2679
2680 Station *st = nullptr;
2681 ret = FindJoiningStation(StationID::Invalid(), station_to_join, allow_adjacent, airport_area, &st);
2682 if (ret.Failed()) return ret;
2683
2684 /* Distant join */
2685 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2686
2687 ret = BuildStationPart(&st, flags, reuse, airport_area, GetAirport(airport_type)->flags.Test(AirportFTAClass::Flag::Airplanes) ? StationNaming::Airport : StationNaming::Heliport);
2688 if (ret.Failed()) return ret;
2689
2690 if (st != nullptr && !st->airport.IsEmpty()) {
2691 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2692 }
2693
2694 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2696 }
2697
2698 if (flags.Test(DoCommandFlag::Execute)) {
2699 /* Always add the noise, so there will be no need to recalculate when option toggles */
2700 nearest->noise_reached += newnoise_level;
2701
2703 st->airport.type = airport_type;
2704 st->airport.layout = layout;
2705 st->airport.blocks = {};
2706 st->airport.rotation = rotation;
2707
2708 st->spread.Add(airport_area);
2709
2710 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2711 Tile t(iter);
2712 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WaterClass::Invalid);
2713 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2714 st->airport.Add(iter);
2715
2716 if (AirportTileSpec::Get(GetTranslatedAirportTileID(iter.GetStationGfx()))->animation.status != AnimationStatus::NoAnimation) AddAnimatedTile(t);
2717 }
2718
2719 /* Only call the animation trigger after all tiles have been built */
2720 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2721 TriggerAirportTileAnimation(st, iter, AirportAnimationTrigger::Built);
2722 }
2723
2725
2726 Company::Get(st->owner)->infrastructure.airport++;
2727
2729 InvalidateWindowData(WindowClass::StationView, st->index, -1);
2730
2731 if (_settings_game.economy.station_noise_level) {
2732 SetWindowDirty(WindowClass::TownView, nearest->index);
2733 }
2734 }
2735
2736 return cost;
2737}
2738
2746{
2747 Station *st = Station::GetByTile(tile);
2748
2750 CommandCost ret = CheckOwnership(st->owner);
2751 if (ret.Failed()) return ret;
2752 }
2753
2754 tile = st->airport.tile;
2755
2757
2758 for (const Aircraft *a : Aircraft::Iterate()) {
2759 if (!a->IsNormalAircraft()) continue;
2760 if (a->targetairport == st->index && a->state != FLYING) {
2761 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2762 }
2763 }
2764
2765 if (flags.Test(DoCommandFlag::Execute)) {
2766 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2767 TileIndex tile_cur = st->airport.GetHangarTile(i);
2768 OrderBackup::Reset(tile_cur, false);
2769 CloseWindowById(WindowClass::VehicleDepot, tile_cur);
2770 }
2771
2772 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2773 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2774 * need of recalculation */
2775 uint dist;
2776 Town *nearest = AirportGetNearestTown(st, dist);
2778
2779 if (_settings_game.economy.station_noise_level) {
2780 SetWindowDirty(WindowClass::TownView, nearest->index);
2781 }
2782 }
2783
2784 for (TileIndex tile_cur : st->airport) {
2785 if (!st->TileBelongsToAirport(tile_cur)) continue;
2786
2787 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2788 if (ret.Failed()) return ret;
2789
2791
2792 if (flags.Test(DoCommandFlag::Execute)) {
2793 DoClearSquare(tile_cur);
2795 }
2796 }
2797
2798 if (flags.Test(DoCommandFlag::Execute)) {
2799 /* Clear the persistent storage. */
2800 delete st->airport.psa;
2801
2803
2804 st->airport.Clear();
2806 SetWindowClassesDirty(WindowClass::VehicleOrders);
2807
2808 InvalidateWindowData(WindowClass::StationView, st->index, -1);
2809
2810 Company::Get(st->owner)->infrastructure.airport--;
2811
2813
2815 }
2816
2817 return cost;
2818}
2819
2827{
2828 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2829 Station *st = Station::Get(station_id);
2830
2832
2833 CommandCost ret = CheckOwnership(st->owner);
2834 if (ret.Failed()) return ret;
2835
2836 if (flags.Test(DoCommandFlag::Execute)) {
2838 SetWindowWidgetDirty(WindowClass::StationView, st->index, WID_SV_CLOSE_AIRPORT);
2839 }
2840 return CommandCost();
2841}
2842
2850bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2851{
2852 for (const OrderList *orderlist : OrderList::Iterate()) {
2853 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2854 assert(v != nullptr);
2855 if ((v->owner == company) != include_company) continue;
2856
2857 for (const Order &order : orderlist->GetOrders()) {
2858 if (order.GetDestination() == station && (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT))) {
2859 return true;
2860 }
2861 }
2862 }
2863 return false;
2864}
2865
2868 {-1, 0},
2869 { 0, 0},
2870 { 0, 0},
2871 { 0, -1}
2872}}};
2873
2877
2886CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
2887{
2888 bool reuse = (station_to_join != NEW_STATION);
2889 if (!reuse) station_to_join = StationID::Invalid();
2890 bool distant_join = (station_to_join != StationID::Invalid());
2891
2892 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2893
2895 if (direction == DiagDirection::Invalid) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2896 direction = ReverseDiagDir(direction);
2897
2898 /* Docks cannot be placed on rapids */
2899 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2900
2902 if (ret.Failed()) return ret;
2903
2904 ret = IsDockBridgeAboveOk(tile, to_underlying(direction));
2905 if (ret.Failed()) return ret;
2906
2908 ret = Command<Commands::LandscapeClear>::Do(flags, tile);
2909 if (ret.Failed()) return ret;
2910 cost.AddCost(ret.GetCost());
2911
2912 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2913
2914 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2915 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2916 }
2917
2919 if (ret.Failed()) return ret;
2920
2921 /* Get the water class of the water tile before it is cleared.*/
2922 WaterClass wc = GetWaterClass(tile_cur);
2923
2924 bool add_cost = !IsWaterTile(tile_cur);
2925 ret = Command<Commands::LandscapeClear>::Do(flags, tile_cur);
2926 if (ret.Failed()) return ret;
2927 if (add_cost) cost.AddCost(ret.GetCost());
2928
2929 tile_cur += TileOffsByDiagDir(direction);
2930 if (!IsTileType(tile_cur, TileType::Water) || !IsTileFlat(tile_cur)) {
2931 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2932 }
2933
2934 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2935 _dock_w_chk[direction], _dock_h_chk[direction]);
2936
2937 /* middle */
2938 Station *st = nullptr;
2939 ret = FindJoiningStation(StationID::Invalid(), station_to_join, adjacent, dock_area, &st);
2940 if (ret.Failed()) return ret;
2941
2942 /* Distant join */
2943 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2944
2945 ret = BuildStationPart(&st, flags, reuse, dock_area, StationNaming::Dock);
2946 if (ret.Failed()) return ret;
2947
2948 if (flags.Test(DoCommandFlag::Execute)) {
2949 st->ship_station.Add(tile);
2950 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2951 st->ship_station.Add(flat_tile);
2953
2954 st->spread.Add(dock_area);
2955
2956 /* If the water part of the dock is on a canal, update infrastructure counts.
2957 * This is needed as we've cleared that tile before.
2958 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2959 * See: MakeWaterKeepingClass() */
2960 if (wc == WaterClass::Canal && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WaterClass::Canal && IsTileOwner(flat_tile, _current_company))) {
2961 Company::Get(st->owner)->infrastructure.water++;
2962 }
2963 Company::Get(st->owner)->infrastructure.station += 2;
2964
2965 MakeDock(tile, st->owner, st->index, direction, wc);
2966 UpdateStationDockingTiles(st);
2967
2969 }
2970
2971 return cost;
2972}
2973
2974void RemoveDockingTile(TileIndex t)
2975{
2976 for (DiagDirection d : EnumRange(DiagDirection::End)) {
2977 TileIndex tile = t + TileOffsByDiagDir(d);
2978 if (!IsValidTile(tile)) continue;
2979
2980 if (IsTileType(tile, TileType::Station)) {
2981 Station *st = Station::GetByTile(tile);
2982 if (st != nullptr) UpdateStationDockingTiles(st);
2983 } else if (IsTileType(tile, TileType::Industry)) {
2985 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
2986 }
2987 }
2988}
2989
2996{
2997 assert(IsValidTile(tile));
2998
2999 /* Clear and maybe re-set docking tile */
3001 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
3002 if (!IsValidTile(docking_tile)) continue;
3003
3004 if (IsPossibleDockingTile(docking_tile)) {
3005 SetDockingTile(docking_tile, false);
3006 CheckForDockingTile(docking_tile);
3007 }
3008 }
3009}
3010
3017{
3018 assert(IsDockTile(t));
3019
3020 StationGfx gfx = GetStationGfx(t);
3021 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
3022
3024 TileIndex tile = t + TileOffsByDiagDir(d);
3025 if (!IsValidTile(tile)) continue;
3026 if (!IsDockTile(tile)) continue;
3027 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
3028 }
3029
3030 return INVALID_TILE;
3031}
3032
3040{
3041 Station *st = Station::GetByTile(tile);
3042 CommandCost ret = CheckOwnership(st->owner);
3043 if (ret.Failed()) return ret;
3044
3045 if (!IsDockTile(tile)) return CMD_ERROR;
3046
3047 TileIndex tile1 = FindDockLandPart(tile);
3048 if (tile1 == INVALID_TILE) return CMD_ERROR;
3049 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
3050
3051 ret = EnsureNoVehicleOnGround(tile1);
3052 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
3053 if (ret.Failed()) return ret;
3054
3055 if (flags.Test(DoCommandFlag::Execute)) {
3056 DoClearSquare(tile1);
3057 MarkTileDirtyByTile(tile1);
3058 MakeWaterKeepingClass(tile2, st->owner);
3059
3060 MakeShipStationAreaSmaller(st);
3062
3063 if (st->ship_station.IsEmpty()) {
3064 st->docking_station.Clear();
3066 SetWindowClassesDirty(WindowClass::VehicleOrders);
3067 }
3068
3069 Company::Get(st->owner)->infrastructure.station -= 2;
3070
3072
3075
3076 for (Ship *s : Ship::Iterate()) {
3077 /* Find all ships going to our dock. */
3078 if (s->current_order.GetDestination() != st->index) {
3079 continue;
3080 }
3081
3082 /* Find ships that are marked as "loading" but are no longer on a
3083 * docking tile. Force them to leave the station (as they were loading
3084 * on the removed dock). */
3085 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
3086 s->LeaveStation();
3087 }
3088
3089 /* If we no longer have a dock, mark the order as invalid and send
3090 * the ship to the next order (or, if there is none, make it
3091 * wander the world). */
3092 if (s->current_order.IsType(OT_GOTO_STATION) && !st->facilities.Test(StationFacility::Dock)) {
3093 s->SetDestTile(s->GetOrderStationLocation(st->index));
3094 }
3095 }
3096 }
3097
3099}
3100
3108{
3109 const auto &layouts = _station_display_datas[st];
3110 if (gfx >= layouts.size()) gfx &= 1;
3111 return layouts.data() + gfx;
3112}
3113
3123bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3124{
3125 bool snow_desert;
3126 switch (*ground) {
3127 case SPR_RAIL_TRACK_X:
3128 case SPR_MONO_TRACK_X:
3129 case SPR_MGLV_TRACK_X:
3130 snow_desert = false;
3131 *overlay_offset = RTO_X;
3132 break;
3133
3134 case SPR_RAIL_TRACK_Y:
3135 case SPR_MONO_TRACK_Y:
3136 case SPR_MGLV_TRACK_Y:
3137 snow_desert = false;
3138 *overlay_offset = RTO_Y;
3139 break;
3140
3144 snow_desert = true;
3145 *overlay_offset = RTO_X;
3146 break;
3147
3151 snow_desert = true;
3152 *overlay_offset = RTO_Y;
3153 break;
3154
3155 default:
3156 return false;
3157 }
3158
3159 if (ti != nullptr) {
3160 /* Decide snow/desert from tile */
3161 switch (_settings_game.game_creation.landscape) {
3163 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3164 break;
3165
3167 snow_desert = GetTropicZone(ti->tile) == TropicZone::Desert;
3168 break;
3169
3170 default:
3171 break;
3172 }
3173 }
3174
3175 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3176 return true;
3177}
3178
3185static BridgePillarFlags GetStationBlockedPillars(std::span<const BridgeableTileInfo> bridgeable_info, uint8_t layout)
3186{
3187 if (layout < std::size(bridgeable_info)) return bridgeable_info[layout].disallowed_pillars;
3188 return BRIDGEPILLARFLAGS_ALL;
3189}
3190
3200{
3201 if (statspec == nullptr || !statspec->flags.Test(StationSpecFlag::CustomFoundations)) return false;
3202
3203 /* Station has custom foundations.
3204 * Check whether the foundation continues beyond the tile's upper sides. */
3205 uint edge_info = GetFoundationSpriteBlock(ti->tile);
3206 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, gfx, edge_info);
3207 if (image == 0) return false;
3208
3210 /* Station provides extended foundations. */
3211 static constexpr uint8_t foundation_parts[] = {
3212 UINT8_MAX, UINT8_MAX, UINT8_MAX, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3213 UINT8_MAX, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3214 UINT8_MAX, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3215 7, 8, 9, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3216 };
3217 assert(ti->tileh < std::size(foundation_parts));
3218 if (foundation_parts[ti->tileh] == UINT8_MAX) return false;
3219
3220 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3221 } else {
3222 /* Draw simple foundations, built up from 8 possible foundation sprites.
3223 * Each set bit represents one of the eight composite sprites to be drawn.
3224 * 'Invalid' entries will not drawn but are included for completeness. */
3225 static constexpr uint8_t composite_foundation_parts[] = {
3226 0b0000'0000, 0b1101'0001, 0b1110'0100, 0b1110'0000, // Invalid, Invalid, Invalid, SLOPE_SW
3227 0b1100'1010, 0b1100'1001, 0b1100'0100, 0b1100'0000, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3228 0b1101'0010, 0b1001'0001, 0b1110'0100, 0b1010'0000, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3229 0b0100'1010, 0b0000'1001, 0b0100'0100, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3230 };
3231 assert(ti->tileh < std::size(composite_foundation_parts));
3232
3233 uint8_t parts = composite_foundation_parts[ti->tileh];
3234
3235 /* If foundations continue beyond the tile's upper sides then mask out the last two pieces. */
3236 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3237 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3238
3239 /* We always have to draw at least one sprite, so if we have no parts to draw fall back to default foundation. */
3240 if (parts == 0) return false;
3241
3243 for (uint i : SetBitIterator(parts)) {
3244 AddSortableSpriteToDraw(image + i, PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3245 }
3247 }
3248
3249 OffsetGroundSprite(0, -static_cast<int>(TILE_HEIGHT));
3251
3252 return true;
3253}
3254
3257{
3258 const NewGRFSpriteLayout *layout = nullptr;
3259 SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_layout and t
3260 DrawTileSpriteSpan tmp_layout;
3261 const DrawTileSprites *t = nullptr;
3262 int32_t total_offset;
3263 const RailTypeInfo *rti = nullptr;
3264 uint32_t relocation = 0;
3265 uint32_t ground_relocation = 0;
3266 BaseStation *st = nullptr;
3267 const StationSpec *statspec = nullptr;
3268 uint tile_layout = 0;
3269 auto bridgeable_info = GetStationBridgeableTileInfo(GetStationType(ti->tile));
3270
3271 if (HasStationRail(ti->tile)) {
3272 rti = GetRailTypeInfo(GetRailType(ti->tile));
3273 total_offset = rti->GetRailtypeSpriteOffset();
3274
3275 if (IsCustomStationSpecIndex(ti->tile)) {
3276 /* look for customization */
3277 st = BaseStation::GetByTile(ti->tile);
3278 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3279
3280 if (statspec != nullptr) {
3281 tile_layout = GetStationGfx(ti->tile);
3282
3284 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3285 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + to_underlying(GetRailStationAxis(ti->tile));
3286 }
3287
3288 /* Ensure the chosen tile layout is valid for this custom station */
3289 if (!statspec->renderdata.empty()) {
3290 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3291 if (!layout->NeedsPreprocessing()) {
3292 t = layout;
3293 layout = nullptr;
3294 }
3295 }
3296 }
3297 }
3298 if (statspec != nullptr) bridgeable_info = statspec->bridgeable_info;
3299 } else {
3300 total_offset = 0;
3301 }
3302
3303 StationGfx gfx = GetStationGfx(ti->tile);
3304 if (IsAirport(ti->tile)) {
3305 gfx = GetAirportGfx(ti->tile);
3306 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3307 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3308 if (ats->grf_prop.HasSpriteGroups() && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3309 return;
3310 }
3311 /* No sprite group (or no valid one) found, meaning no graphics associated.
3312 * Use the substitute one instead */
3313 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3314 gfx = ats->grf_prop.subst_id;
3315 }
3316 switch (gfx) {
3317 case APT_RADAR_GRASS_FENCE_SW:
3318 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3319 break;
3320 case APT_GRASS_FENCE_NE_FLAG:
3321 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3322 break;
3323 case APT_RADAR_FENCE_SW:
3324 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3325 break;
3326 case APT_RADAR_FENCE_NE:
3327 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3328 break;
3329 case APT_GRASS_FENCE_NE_FLAG_2:
3330 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3331 break;
3332 }
3333 }
3334
3335 Owner owner = GetTileOwner(ti->tile);
3336
3337 PaletteID palette;
3338 if (Company::IsValidID(owner)) {
3339 palette = GetCompanyPalette(owner);
3340 } else {
3341 /* Some stations are not owner by a company, namely oil rigs */
3342 palette = PALETTE_TO_GREY;
3343 }
3344
3345 if (layout == nullptr && t == nullptr) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3346
3347 /* don't show foundation for docks */
3348 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3349 if (!DrawCustomStationFoundations(statspec, st, ti, tile_layout)) {
3351 }
3352 }
3353
3354 bool draw_ground = false;
3355
3356 if (IsBuoy(ti->tile)) {
3357 DrawWaterClassGround(ti);
3359 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3360 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3361 if (ti->tileh == SLOPE_FLAT) {
3362 DrawWaterClassGround(ti);
3363 } else {
3364 assert(IsDock(ti->tile));
3365 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3366 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WaterClass::Invalid;
3367 if (wc == WaterClass::Sea) {
3368 DrawShoreTile(ti->tileh);
3369 } else {
3370 DrawClearLandTile(ti, 3);
3371 }
3372 }
3373 } else if (IsRoadWaypointTile(ti->tile)) {
3375 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3376 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3377 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : RoadBits{};
3378 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : RoadBits{};
3379 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3380 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3381
3382 if (ti->tileh != SLOPE_FLAT) {
3384 }
3385
3386 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3387 } else {
3388 if (layout != nullptr) {
3389 /* Sprite layout which needs preprocessing */
3390 bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround);
3391 processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3392 GetCustomStationRelocation(processor, statspec, st, ti->tile);
3393 tmp_layout = processor.GetLayout();
3394 t = &tmp_layout;
3395 total_offset = 0;
3396 } else if (statspec != nullptr) {
3397 /* Simple sprite layout */
3398 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3400 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3401 }
3402 ground_relocation += rti->fallback_railtype;
3403 }
3404
3405 draw_ground = true;
3406 }
3407
3408 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3409 SpriteID image = t->ground.sprite;
3410 PaletteID pal = t->ground.pal;
3411 RailTrackOffset overlay_offset;
3412 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3414 DrawGroundSprite(image, PAL_NONE);
3415 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3416
3417 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3419 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3420 }
3421 } else {
3422 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3423 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3424 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3425
3426 /* PBS debugging, draw reserved tracks darker */
3427 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3429 }
3430 }
3431 }
3432
3434
3435 if (IsAnyRoadStop(ti->tile)) {
3436 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3437 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3438 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3439 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3440
3441 StationGfx view = GetStationGfx(ti->tile);
3442 StationType type = GetStationType(ti->tile);
3443
3444 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3445 RoadStopDrawModes stop_draw_mode{};
3446 if (stopspec != nullptr) {
3447 stop_draw_mode = stopspec->draw_mode;
3448 st = BaseStation::GetByTile(ti->tile);
3449 std::array<int32_t, 1> regs100;
3450 auto result = GetRoadStopLayout(ti, stopspec, st, type, view, regs100);
3451 if (result.has_value()) {
3452 if (stopspec->flags.Test(RoadStopSpecFlag::DrawModeRegister)) {
3453 stop_draw_mode = static_cast<RoadStopDrawModes>(regs100[0]);
3454 }
3455 if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) {
3456 draw_ground = true;
3457 }
3458 processor = std::move(*result);
3459 tmp_layout = processor.GetLayout();
3460 t = &tmp_layout;
3461 }
3462 }
3463
3464 /* Draw ground sprite */
3465 if (draw_ground) {
3466 SpriteID image = t->ground.sprite;
3467 PaletteID pal = t->ground.pal;
3468 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3469 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3470 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3471 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3472 }
3473 }
3474
3475 if (IsDriveThroughStopTile(ti->tile)) {
3476 if (type != StationType::RoadWaypoint && (stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Overlay))) {
3477 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == Axis::X ? 1 : 0;
3478 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3479 }
3480 } else {
3481 /* Non-drivethrough road stops are only valid for roads. */
3482 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3483
3484 if ((stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Road)) && road_rti->UsesOverlay()) {
3486 DrawGroundSprite(ground + view, PAL_NONE);
3487 }
3488 }
3489 if (stopspec != nullptr) bridgeable_info = stopspec->bridgeable_info;
3490
3491 if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
3492 /* Draw road, tram catenary */
3493 DrawRoadCatenary(ti);
3494 }
3495 }
3496
3497 if (IsRailWaypoint(ti->tile)) {
3498 /* Don't offset the waypoint graphics; they're always the same. */
3499 total_offset = 0;
3500 }
3501
3502 DrawRailTileSeq(ti, t, TransparencyOption::Buildings, total_offset, relocation, palette);
3503 DrawBridgeMiddle(ti, GetStationBlockedPillars(bridgeable_info, GetStationGfx(ti->tile)));
3504}
3505
3506void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3507{
3508 int32_t total_offset = 0;
3510 const DrawTileSprites *t = GetStationTileLayout(st, image);
3511 const RailTypeInfo *railtype_info = nullptr;
3512
3513 if (railtype != INVALID_RAILTYPE) {
3514 railtype_info = GetRailTypeInfo(railtype);
3515 total_offset = railtype_info->GetRailtypeSpriteOffset();
3516 }
3517
3518 SpriteID img = t->ground.sprite;
3519 RailTrackOffset overlay_offset;
3520 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3522 DrawSprite(img, PAL_NONE, x, y);
3523 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3524 } else {
3525 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3526 }
3527
3528 if (roadtype != INVALID_ROADTYPE) {
3529 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3530 if (image >= 4) {
3531 /* Drive-through stop */
3532 uint sprite_offset = 5 - image;
3533
3534 /* Road underlay takes precedence over tram */
3535 if (roadtype_info->UsesOverlay()) {
3537 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3538
3540 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3541 } else if (RoadTypeIsTram(roadtype)) {
3542 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3543 }
3544 } else {
3545 /* Bay stop */
3546 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3548 DrawSprite(ground + image, PAL_NONE, x, y);
3549 }
3550 }
3551 }
3552
3553 /* Default waypoint has no railtype specific sprites */
3554 DrawRailTileSeqInGUI(x, y, t, (st == StationType::RailWaypoint || st == StationType::RoadWaypoint) ? 0 : total_offset, 0, pal);
3555}
3556
3557
3558static void FillTileDescRoadStop(TileIndex tile, TileDesc &td)
3559{
3560 RoadType road_rt = GetRoadTypeRoad(tile);
3561 RoadType tram_rt = GetRoadTypeTram(tile);
3562 Owner road_owner = INVALID_OWNER;
3563 Owner tram_owner = INVALID_OWNER;
3564 if (road_rt != INVALID_ROADTYPE) {
3565 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3566 td.roadtype = rti->strings.name;
3567 td.road_speed = rti->max_speed / 2;
3568 road_owner = GetRoadOwner(tile, RoadTramType::Road);
3569 }
3570
3571 if (tram_rt != INVALID_ROADTYPE) {
3572 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3573 td.tramtype = rti->strings.name;
3574 td.tram_speed = rti->max_speed / 2;
3575 tram_owner = GetRoadOwner(tile, RoadTramType::Tram);
3576 }
3577
3578 if (IsDriveThroughStopTile(tile)) {
3579 /* Is there a mix of owners? */
3580 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
3581 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
3582 uint i = 1;
3583 if (road_owner != INVALID_OWNER) {
3584 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3585 td.owner[i] = road_owner;
3586 i++;
3587 }
3588 if (tram_owner != INVALID_OWNER) {
3589 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3590 td.owner[i] = tram_owner;
3591 }
3592 }
3593 }
3594}
3595
3596void FillTileDescRailStation(TileIndex tile, TileDesc &td)
3597{
3598 const StationSpec *spec = GetStationSpec(tile);
3599
3600 if (spec != nullptr) {
3602 td.station_name = spec->name;
3603
3604 if (spec->grf_prop.HasGrfFile()) {
3605 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3606 td.grf = gc->GetName();
3607 }
3608 }
3609
3610 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3611 td.rail_speed = rti->max_speed;
3612 td.railtype = rti->strings.name;
3613}
3614
3615void FillTileDescAirport(TileIndex tile, TileDesc &td)
3616{
3617 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3619 td.airport_name = as->name;
3620
3621 const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
3622 td.airport_tile_name = ats->name;
3623
3624 if (as->grf_prop.HasGrfFile()) {
3625 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3626 td.grf = gc->GetName();
3627 } else if (ats->grf_prop.HasGrfFile()) {
3628 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3629 td.grf = gc->GetName();
3630 }
3631}
3632
3635{
3636 td.owner[0] = GetTileOwner(tile);
3638
3639 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3640 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3641 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3642
3643 StringID str;
3644 switch (GetStationType(tile)) {
3645 default: NOT_REACHED();
3646 case StationType::Rail: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3648 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3649 break;
3650 case StationType::Truck: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3651 case StationType::Bus: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3652 case StationType::Oilrig: {
3653 const Industry *i = Station::GetByTile(tile)->industry;
3654 const IndustrySpec *is = GetIndustrySpec(i->type);
3655 td.owner[0] = i->owner;
3656 str = is->name;
3657 if (is->grf_prop.HasGrfFile()) td.grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3658 break;
3659 }
3660 case StationType::Dock: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3661 case StationType::Buoy: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3662 case StationType::RailWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3663 case StationType::RoadWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3664 }
3665 td.str = str;
3666}
3667
3668
3671{
3672 TrackBits trackbits{};
3673
3674 switch (mode) {
3676 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3677 trackbits = GetRailStationTrack(tile);
3678 }
3679 break;
3680
3682 /* buoy is coded as a station, it is always on open water */
3683 if (IsBuoy(tile)) {
3684 trackbits = TRACK_BIT_ALL;
3685 /* remove tracks that connect NE map edge */
3686 if (TileX(tile) == 0) trackbits.Reset(TRACK_BIT_3WAY_NE);
3687 /* remove tracks that connect NW map edge */
3688 if (TileY(tile) == 0) trackbits.Reset({Track::Y, Track::Left, Track::Upper});
3689 }
3690 break;
3691
3693 if (IsAnyRoadStop(tile)) {
3694 RoadTramType rtt = (RoadTramType)sub_mode;
3695 if (!HasTileRoadType(tile, rtt)) break;
3696
3697 if (IsBayRoadStopTile(tile)) {
3698 DiagDirection dir = GetBayRoadStopDir(tile);
3699 if (side != DiagDirection::Invalid && dir != side) break;
3700 trackbits = DiagDirToDiagTrack(dir);
3701 } else {
3702 Axis axis = GetDriveThroughStopAxis(tile);
3703 if (side != DiagDirection::Invalid && axis != DiagDirToAxis(side)) break;
3704 trackbits = AxisToTrack(axis);
3705 }
3706 }
3707 break;
3708
3709 default:
3710 break;
3711 }
3712
3713 return {TrackBitsToTrackdirBits(trackbits), {}};
3714}
3715
3716
3719{
3720 auto *st = BaseStation::GetByTile(tile);
3721 switch (GetStationType(tile)) {
3723 TriggerAirportTileAnimation(Station::From(st), tile, AirportAnimationTrigger::TileLoop);
3724 break;
3725
3726 case StationType::Rail:
3728 TriggerStationAnimation(st, tile, StationAnimationTrigger::TileLoop);
3729 break;
3730
3731 case StationType::Dock:
3732 if (!IsTileFlat(tile)) break; // only handle water part
3733 [[fallthrough]];
3734
3735 case StationType::Oilrig: //(station part)
3736 case StationType::Buoy:
3737 TileLoop_Water(tile);
3738 break;
3739
3740 case StationType::Truck:
3741 case StationType::Bus:
3742 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3743 break;
3744
3746 switch (_settings_game.game_creation.landscape) {
3748 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3750 MarkTileDirtyByTile(tile);
3751 }
3752 break;
3753
3757 MarkTileDirtyByTile(tile);
3758 }
3759 break;
3760
3761 default: break;
3762 }
3763
3764 HouseZone new_zone = HouseZone::TownEdge;
3765 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3766 if (t != nullptr) {
3767 new_zone = GetTownRadiusGroup(t, tile);
3768 }
3769
3770 /* Adjust road ground type depending on 'new_zone' */
3772 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3773
3774 if (new_rs != cur_rs) {
3775 SetRoadWaypointRoadside(tile, cur_rs == Roadside::Barren ? new_rs : Roadside::Barren);
3776 MarkTileDirtyByTile(tile);
3777 }
3778
3779 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3780 break;
3781 }
3782
3783 default: break;
3784 }
3785}
3786
3787
3790{
3791 if (HasStationRail(tile)) {
3792 AnimateStationTile(tile);
3793 return;
3794 }
3795
3796 if (IsAirport(tile)) {
3797 AnimateAirportTile(tile);
3798 return;
3799 }
3800
3801 if (IsAnyRoadStopTile(tile)) {
3802 AnimateRoadStopTile(tile);
3803 return;
3804 }
3805}
3806
3807
3810{
3811 const BaseStation *bst = BaseStation::GetByTile(tile);
3812
3815 } else if (IsHangar(tile)) {
3816 const Station *st = Station::From(bst);
3818 } else {
3819 ShowStationViewWindow(bst->index);
3820 }
3821 return true;
3822}
3823
3826{
3827 if (v->type == VehicleType::Train) {
3828 StationID station_id = GetStationIndex(tile);
3829 if (!IsRailStation(tile) || !v->IsMovingFront()) return {};
3830 Vehicle *consist = v->First();
3831 if (!consist->current_order.ShouldStopAtStation(consist, station_id)) return {};
3832
3833 int station_ahead;
3834 int station_length;
3835 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3836
3837 /* Stop whenever that amount of station ahead + the distance from the
3838 * begin of the platform to the stop location is longer than the length
3839 * of the platform. Station ahead 'includes' the current tile where the
3840 * vehicle is on, so we need to subtract that. */
3841 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return {};
3842
3844
3845 x &= 0xF;
3846 y &= 0xF;
3847
3848 if (DiagDirToAxis(dir) != Axis::X) std::swap(x, y);
3849 if (y == TILE_SIZE / 2) {
3850 if (dir != DiagDirection::SE && dir != DiagDirection::SW) x = TILE_SIZE - 1 - x;
3851 stop &= TILE_SIZE - 1;
3852
3853 if (x == stop) {
3854 return VehicleEnterTileState::EnteredStation; // enter station
3855 } else if (x < stop) {
3857 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3858 if (spd < v->cur_speed) v->cur_speed = spd;
3859 }
3860 }
3861 } else if (v->type == VehicleType::Road) {
3863 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir(static_cast<Trackdir>(rv->state)) && rv->frame == 0) {
3864 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3865 /* Attempt to allocate a parking bay in a road stop */
3866 if (RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv)) return {};
3868 }
3869 }
3870 }
3871
3872 return {};
3873}
3874
3880{
3881 /* Collect cargoes accepted since the last big tick. */
3882 CargoTypes cargoes{};
3883 for (CargoType cargo_type : EnumRange(NUM_CARGO)) {
3884 if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) cargoes.Set(cargo_type);
3885 }
3886
3887 /* Anything to do? */
3888 if (cargoes.None()) return;
3889
3890 /* Loop over all houses in the catchment. */
3892 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3893 if (IsTileType(tile, TileType::House)) {
3895 }
3896 }
3897}
3898
3906{
3907 if (!st->IsInUse()) {
3908 if (++st->delete_ctr >= 8) delete st;
3909 return false;
3910 }
3911
3912 if (Station::IsExpected(st)) {
3914
3915 for (GoodsEntry &ge : Station::From(st)->goods) {
3917 }
3918 }
3919
3920
3922
3923 return true;
3924}
3925
3926static inline void byte_inc_sat(uint8_t *p)
3927{
3928 uint8_t b = *p + 1;
3929 if (b != 0) *p = b;
3930}
3931
3938static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3939{
3940 /* If truncating also punish the source stations' ratings to
3941 * decrease the flow of incoming cargo. */
3942
3943 if (!ge->HasData()) return;
3944
3945 StationCargoAmountMap waiting_per_source;
3946 ge->GetData().cargo.Truncate(amount, &waiting_per_source);
3947 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3948 Station *source_station = Station::GetIfValid(i->first);
3949 if (source_station == nullptr) continue;
3950
3951 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3952 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3953 }
3954}
3955
3961{
3962 bool waiting_changed = false;
3963
3964 byte_inc_sat(&st->time_since_load);
3965 byte_inc_sat(&st->time_since_unload);
3966
3967 for (const CargoSpec *cs : CargoSpec::Iterate()) {
3968 GoodsEntry *ge = &st->goods[cs->Index()];
3969
3970 /* The station might not currently be moving this cargo. */
3971 if (!ge->HasRating()) {
3972 /* Slowly increase the rating back to its original level in the case we
3973 * didn't deliver cargo yet to this station. This happens when a bribe
3974 * failed while you didn't moved that cargo yet to a station. */
3975 if (ge->rating < INITIAL_STATION_RATING) ge->rating++;
3976
3977 /* Nothing else to do with this cargo. */
3978 continue;
3979 }
3980
3981 byte_inc_sat(&ge->time_since_pickup);
3982
3983 /* If this cargo hasn't been picked up in a long time, get rid of it. */
3984 if (ge->time_since_pickup == 255 && _settings_game.order.selectgoods) {
3986 ge->last_speed = 0;
3987 TruncateCargo(cs, ge);
3988 waiting_changed = true;
3989 continue;
3990 }
3991
3992 bool skip = false;
3993 int rating = 0;
3994 uint waiting = ge->AvailableCount();
3995
3996 /* num_dests is at least 1 if there is any cargo as
3997 * StationID::Invalid() is also a destination.
3998 */
3999 uint num_dests = ge->HasData() ? static_cast<uint>(ge->GetData().cargo.Packets()->MapSize()) : 0;
4000
4001 /* Average amount of cargo per next hop, but prefer solitary stations
4002 * with only one or two next hops. They are allowed to have more
4003 * cargo waiting per next hop.
4004 * With manual cargo distribution waiting_avg = waiting / 2 as then
4005 * StationID::Invalid() is the only destination.
4006 */
4007 uint waiting_avg = waiting / (num_dests + 1);
4008
4009 if (_cheats.station_rating.value) {
4010 ge->rating = rating = MAX_STATION_RATING;
4011 skip = true;
4012 } else if (cs->callback_mask.Test(CargoCallbackMask::StationRatingCalc)) {
4013 /* Perform custom station rating. If it succeeds the speed, days in transit and
4014 * waiting cargo ratings must not be executed. */
4015
4016 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
4017 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
4018
4019 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
4021 | (ClampTo<uint8_t>(last_speed) << 24);
4022 /* Convert to the 'old' vehicle types */
4023 uint32_t var10 = (st->last_vehicle_type == VehicleType::Invalid) ? 0x0 : (to_underlying(st->last_vehicle_type) + 0x10);
4024 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
4025 if (callback != CALLBACK_FAILED) {
4026 skip = true;
4027 rating = GB(callback, 0, 14);
4028
4029 /* Simulate a 15 bit signed value */
4030 if (HasBit(callback, 14)) rating -= 0x4000;
4031 }
4032 }
4033
4034 if (!skip) {
4035 int b = ge->last_speed - 85;
4036 if (b >= 0) rating += b >> 2;
4037
4038 uint8_t waittime = ge->time_since_pickup;
4039 if (st->last_vehicle_type == VehicleType::Ship) waittime >>= 2;
4040 if (waittime <= 21) rating += 25;
4041 if (waittime <= 12) rating += 25;
4042 if (waittime <= 6) rating += 45;
4043 if (waittime <= 3) rating += 35;
4044
4045 rating -= 90;
4046 if (ge->max_waiting_cargo <= 1500) rating += 55;
4047 if (ge->max_waiting_cargo <= 1000) rating += 35;
4048 if (ge->max_waiting_cargo <= 600) rating += 10;
4049 if (ge->max_waiting_cargo <= 300) rating += 20;
4050 if (ge->max_waiting_cargo <= 100) rating += 10;
4051 }
4052
4053 if (Company::IsValidID(st->owner) && st->town->statues.Test(st->owner)) rating += 26;
4054
4055 uint8_t age = ge->last_age;
4056 if (age < 3) rating += 10;
4057 if (age < 2) rating += 10;
4058 if (age < 1) rating += 13;
4059
4060 {
4061 int or_ = ge->rating; // old rating
4062
4063 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
4064 ge->rating = rating = ClampTo<uint8_t>(or_ + Clamp(rating - or_, -2, 2));
4065
4066 /* if rating is <= 64 and more than 100 items waiting on average per destination,
4067 * remove some random amount of goods from the station */
4068 if (rating <= 64 && waiting_avg >= 100) {
4069 int dec = Random() & 0x1F;
4070 if (waiting_avg < 200) dec &= 7;
4071 waiting -= (dec + 1) * num_dests;
4072 waiting_changed = true;
4073 }
4074
4075 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
4076 if (rating <= 127 && waiting != 0) {
4077 uint32_t r = Random();
4078 if (rating <= (int)GB(r, 0, 7)) {
4079 /* Need to have int, otherwise it will just overflow etc. */
4080 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) + 1) * num_dests), 0);
4081 waiting_changed = true;
4082 }
4083 }
4084
4085 /* At some point we really must cap the cargo. Previously this
4086 * was a strict 4095, but now we'll have a less strict, but
4087 * increasingly aggressive truncation of the amount of cargo. */
4088 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
4089 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
4090 static const uint MAX_WAITING_CARGO = 1 << 15;
4091
4092 if (waiting > WAITING_CARGO_THRESHOLD) {
4093 uint difference = waiting - WAITING_CARGO_THRESHOLD;
4094 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
4095
4096 waiting = std::min(waiting, MAX_WAITING_CARGO);
4097 waiting_changed = true;
4098 }
4099
4100 /* We can't truncate cargo that's already reserved for loading.
4101 * Thus StoredCount() here. */
4102 if (waiting_changed && waiting < ge->AvailableCount()) {
4103 /* Feed back the exact own waiting cargo at this station for the
4104 * next rating calculation. */
4105 ge->max_waiting_cargo = 0;
4106
4107 TruncateCargo(cs, ge, ge->AvailableCount() - waiting);
4108 } else {
4109 /* If the average number per next hop is low, be more forgiving. */
4110 ge->max_waiting_cargo = waiting_avg;
4111 }
4112 }
4113 }
4114
4115 StationID index = st->index;
4116 if (waiting_changed) {
4117 SetWindowDirty(WindowClass::StationView, index); // update whole window
4118 } else {
4119 SetWindowWidgetDirty(WindowClass::StationView, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
4120 }
4121}
4122
4131void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
4132{
4133 GoodsEntry &ge = st->goods[cargo];
4134
4135 /* Reroute cargo in station. */
4136 if (ge.HasData()) ge.GetData().cargo.Reroute(UINT_MAX, &ge.GetData().cargo, avoid, avoid2, &ge);
4137
4138 /* Reroute cargo staged to be transferred. */
4139 for (Vehicle *v : st->loading_vehicles) {
4140 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4141 if (u->cargo_type != cargo) continue;
4142 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4143 }
4144 }
4145}
4146
4156{
4157 for (CargoType cargo : EnumRange(NUM_CARGO)) {
4158 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DistributionType::Manual);
4159 GoodsEntry &ge = from->goods[cargo];
4161 if (lg == nullptr) continue;
4162 std::vector<NodeID> to_remove{};
4163 for (Edge &edge : (*lg)[ge.node].edges) {
4164 Station *to = Station::Get((*lg)[edge.dest_node].station);
4165 assert(to->goods[cargo].node == edge.dest_node);
4166 assert(TimerGameEconomy::date >= edge.LastUpdate());
4168 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4169 bool updated = false;
4170
4171 if (auto_distributed) {
4172 /* Have all vehicles refresh their next hops before deciding to
4173 * remove the node. */
4174 std::vector<Vehicle *> vehicles;
4175 for (const OrderList *l : OrderList::Iterate()) {
4176 bool found_from = false;
4177 bool found_to = false;
4178 for (const Order &order : l->GetOrders()) {
4179 if (!order.IsType(OT_GOTO_STATION) && !order.IsType(OT_IMPLICIT)) continue;
4180 if (order.GetDestination() == from->index) {
4181 found_from = true;
4182 if (found_to) break;
4183 } else if (order.GetDestination() == to->index) {
4184 found_to = true;
4185 if (found_from) break;
4186 }
4187 }
4188 if (!found_to || !found_from) continue;
4189 vehicles.push_back(l->GetFirstSharedVehicle());
4190 }
4191
4192 auto iter = vehicles.begin();
4193 while (iter != vehicles.end()) {
4194 Vehicle *v = *iter;
4195 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4197 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4198 }
4199 if (edge.LastUpdate() == TimerGameEconomy::date) {
4200 updated = true;
4201 break;
4202 }
4203
4204 Vehicle *next_shared = v->NextShared();
4205 if (next_shared) {
4206 *iter = next_shared;
4207 ++iter;
4208 } else {
4209 iter = vehicles.erase(iter);
4210 }
4211
4212 if (iter == vehicles.end()) iter = vehicles.begin();
4213 }
4214 }
4215
4216 if (!updated) {
4217 /* If it's still considered dead remove it. */
4218 to_remove.emplace_back(to->goods[cargo].node);
4219 if (ge.HasData()) ge.GetData().flows.DeleteFlows(to->index);
4220 RerouteCargo(from, cargo, to->index, from->index);
4221 }
4222 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4223 edge.Restrict();
4224 if (ge.HasData()) ge.GetData().flows.RestrictFlows(to->index);
4225 RerouteCargo(from, cargo, to->index, from->index);
4226 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4227 edge.Release();
4228 }
4229 }
4230 /* Remove dead edges. */
4231 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4232
4233 assert(TimerGameEconomy::date >= lg->LastCompression());
4235 lg->Compress();
4236 }
4237 }
4238}
4239
4250void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
4251{
4252 GoodsEntry &ge1 = st->goods[cargo];
4253 Station *st2 = Station::Get(next_station_id);
4254 GoodsEntry &ge2 = st2->goods[cargo];
4255 LinkGraph *lg = nullptr;
4256 if (ge1.link_graph == LinkGraphID::Invalid()) {
4257 if (ge2.link_graph == LinkGraphID::Invalid()) {
4259 lg = LinkGraph::Create(cargo);
4261 ge2.link_graph = lg->index;
4262 ge2.node = lg->AddNode(st2);
4263 } else {
4264 Debug(Facility::Misc, Severity::Critical, "Can't allocate link graph");
4265 }
4266 } else {
4267 lg = LinkGraph::Get(ge2.link_graph);
4268 }
4269 if (lg) {
4270 ge1.link_graph = lg->index;
4271 ge1.node = lg->AddNode(st);
4272 }
4273 } else if (ge2.link_graph == LinkGraphID::Invalid()) {
4274 lg = LinkGraph::Get(ge1.link_graph);
4275 ge2.link_graph = lg->index;
4276 ge2.node = lg->AddNode(st2);
4277 } else {
4278 lg = LinkGraph::Get(ge1.link_graph);
4279 if (ge1.link_graph != ge2.link_graph) {
4281 if (lg->Size() < lg2->Size()) {
4282 LinkGraphSchedule::instance.Dequeue(lg);
4283 lg2->Merge(lg); // Updates GoodsEntries of lg
4284 lg = lg2;
4285 } else {
4286 LinkGraphSchedule::instance.Dequeue(lg2);
4287 lg->Merge(lg2); // Updates GoodsEntries of lg2
4288 }
4289 }
4290 }
4291 if (lg != nullptr) {
4292 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, modes);
4293 }
4294}
4295
4303void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4304{
4305 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4306 if (v->refit_cap > 0) {
4307 /* The cargo count can indeed be higher than the refit_cap if
4308 * wagons have been auto-replaced and subsequently auto-
4309 * refitted to a higher capacity. The cargo gets redistributed
4310 * among the wagons in that case.
4311 * As usage is not such an important figure anyway we just
4312 * ignore the additional cargo then.*/
4313 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4314 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EdgeUpdateMode::Increase);
4315 }
4316 }
4317}
4318
4319/* called for every station each tick */
4320static void StationHandleSmallTick(BaseStation *st)
4321{
4322 if (st->facilities.Test(StationFacility::Waypoint) || !st->IsInUse()) return;
4323
4324 uint8_t b = st->delete_ctr + 1;
4325 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4326 st->delete_ctr = b;
4327
4328 if (b == 0) UpdateStationRating(Station::From(st));
4329}
4330
4331void OnTick_Station()
4332{
4333 if (_game_mode == GameMode::Editor) return;
4334
4335 for (BaseStation *st : BaseStation::Iterate()) {
4336 StationHandleSmallTick(st);
4337
4338 /* Clean up the link graph about once a week. */
4341 };
4342
4343 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4345 /* Stop processing this station if it was deleted */
4346 if (!StationHandleBigTick(st)) continue;
4347 }
4348
4349 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4351 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4352 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4353 if (Station::IsExpected(st)) TriggerAirportAnimation(Station::From(st), AirportAnimationTrigger::AcceptanceTick);
4354 }
4355 }
4356}
4357
4359static const IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::Trigger::Month, TimerGameEconomy::Priority::Station}, [](auto)
4360{
4361 for (Station *st : Station::Iterate()) {
4362 for (GoodsEntry &ge : st->goods) {
4365 }
4366 }
4367});
4368
4377void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4378{
4379 ForAllStationsRadius(tile, radius, [&](Station *st) {
4380 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4381 for (GoodsEntry &ge : st->goods) {
4382 if (ge.status.Any()) {
4383 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4384 }
4385 }
4386 }
4387 });
4388}
4389
4390static uint UpdateStationWaiting(Station *st, CargoType cargo, uint amount, Source source)
4391{
4392 /* We can't allocate a CargoPacket? Then don't do anything
4393 * at all; i.e. just discard the incoming cargo. */
4394 if (!CargoPacket::CanAllocateItem()) return 0;
4395
4396 GoodsEntry &ge = st->goods[cargo];
4397 amount += ge.amount_fract;
4398 ge.amount_fract = GB(amount, 0, 8);
4399
4400 amount >>= 8;
4401 /* No new "real" cargo item yet. */
4402 if (amount == 0) return 0;
4403
4404 StationID next = ge.GetVia(st->index);
4405 ge.GetOrCreateData().cargo.Append(CargoPacket::Create(st->index, amount, source), next);
4406 LinkGraph *lg = nullptr;
4407 if (ge.link_graph == LinkGraphID::Invalid()) {
4408 if (LinkGraph::CanAllocateItem()) {
4409 lg = LinkGraph::Create(cargo);
4411 ge.link_graph = lg->index;
4412 ge.node = lg->AddNode(st);
4413 } else {
4414 Debug(Facility::Misc, Severity::Critical, "Can't allocate link graph");
4415 }
4416 } else {
4417 lg = LinkGraph::Get(ge.link_graph);
4418 }
4419 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4420
4421 if (!ge.HasRating()) {
4422 InvalidateWindowData(WindowClass::StationList, st->owner);
4424 }
4425
4427 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4428 TriggerAirportAnimation(st, AirportAnimationTrigger::NewCargo, cargo);
4430 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4431
4432
4433 SetWindowDirty(WindowClass::StationView, st->index);
4434 st->MarkTilesDirty(true);
4435 return amount;
4436}
4437
4438static bool IsUniqueStationName(const std::string &name)
4439{
4440 for (const Station *st : Station::Iterate()) {
4441 if (!st->name.empty() && st->name == name) return false;
4442 }
4443
4444 return true;
4445}
4446
4454CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
4455{
4456 Station *st = Station::GetIfValid(station_id);
4457 if (st == nullptr) return CMD_ERROR;
4458
4459 CommandCost ret = CheckOwnership(st->owner);
4460 if (ret.Failed()) return ret;
4461
4462 bool reset = text.empty();
4463
4464 if (!reset) {
4466 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4467 }
4468
4469 if (flags.Test(DoCommandFlag::Execute)) {
4470 st->cached_name.clear();
4471 if (reset) {
4472 st->name.clear();
4473 } else {
4474 st->name = text;
4475 }
4476
4477 st->UpdateVirtCoord();
4478 InvalidateWindowData(WindowClass::StationList, st->owner, 1);
4479 }
4480
4481 return CommandCost();
4482}
4483
4491std::tuple<CommandCost, StationID> CmdMoveStationName(DoCommandFlags flags, StationID station_id, TileIndex tile)
4492{
4493 Station *st = Station::GetIfValid(station_id);
4494 if (st == nullptr) return { CMD_ERROR, StationID::Invalid() };
4495
4496 if (st->owner != OWNER_NONE) {
4497 CommandCost ret = CheckOwnership(st->owner);
4498 if (ret.Failed()) return { ret, StationID::Invalid() };
4499 }
4500
4501 if (!st->spread.Contains(tile)) {
4502 return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() };
4503 }
4504
4505 bool other_station = false;
4506 /* Check if the tile is the base tile of another station */
4507 ForAllStationsRadius(tile, 0, [&](BaseStation *s) {
4508 if (s != nullptr) {
4509 if (s != st && s->xy == tile) other_station = true;
4510 }
4511 });
4512 if (other_station) return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() };
4513
4514 if (flags.Test(DoCommandFlag::Execute)) {
4515 st->MoveSign(tile);
4516
4517 st->UpdateVirtCoord();
4518 }
4519 return { CommandCost(), station_id };
4520}
4521
4527void CcMoveStationName(Commands, const CommandCost &result, StationID station_id)
4528 {
4529 if (result.Failed()) return;
4530
4532 Station *st = Station::Get(station_id);
4533 SetViewportStationRect(st, false);
4534 }
4535
4536static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4537{
4538 for (Station *st : nearby) {
4539 if (st->TileIsInCatchment(tile)) stations.insert(st);
4540 }
4541}
4542
4548{
4549 if (this->tile != INVALID_TILE) {
4550 if (IsTileType(this->tile, TileType::House)) {
4551 /* Town nearby stations need to be filtered per tile. */
4552 assert(this->w == 1 && this->h == 1);
4553 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4554 } else {
4555 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4556 this->stations.insert(st);
4557 return true;
4558 });
4559 }
4560 this->tile = INVALID_TILE;
4561 }
4562 return this->stations;
4563}
4564
4565
4566static bool CanMoveGoodsToStation(const Station *st, CargoType cargo)
4567{
4568 /* Is the station reserved exclusively for somebody else? */
4569 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4570
4571 /* Lowest possible rating, better not to give cargo anymore. */
4572 if (st->goods[cargo].rating == 0) return false;
4573
4574 /* Selectively servicing stations, and not this one. */
4575 if (_settings_game.order.selectgoods && !st->goods[cargo].HasVehicleEverTriedLoading()) return false;
4576
4578 /* Passengers are never served by just a truck stop. */
4579 if (st->facilities == StationFacility::TruckStop) return false;
4580 } else {
4581 /* Non-passengers are never served by just a bus stop. */
4582 if (st->facilities == StationFacility::BusStop) return false;
4583 }
4584 return true;
4585}
4586
4587uint MoveGoodsToStation(CargoType cargo, uint amount, Source source, const StationList &all_stations, Owner exclusivity)
4588{
4589 /* Return if nothing to do. Also the rounding below fails for 0. */
4590 if (all_stations.empty()) return 0;
4591 if (amount == 0) return 0;
4592
4593 Station *first_station = nullptr;
4594 typedef std::pair<Station *, uint> StationInfo;
4595 std::vector<StationInfo> used_stations;
4596
4597 for (Station *st : all_stations) {
4598 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4599 if (!CanMoveGoodsToStation(st, cargo)) continue;
4600
4601 /* Avoid allocating a vector if there is only one station to significantly
4602 * improve performance in this common case. */
4603 if (first_station == nullptr) {
4604 first_station = st;
4605 continue;
4606 }
4607 if (used_stations.empty()) {
4608 used_stations.reserve(2);
4609 used_stations.emplace_back(first_station, 0);
4610 }
4611 used_stations.emplace_back(st, 0);
4612 }
4613
4614 /* no stations around at all? */
4615 if (first_station == nullptr) return 0;
4616
4617 if (used_stations.empty()) {
4618 /* only one station around */
4619 amount *= first_station->goods[cargo].rating + 1;
4620 return UpdateStationWaiting(first_station, cargo, amount, source);
4621 }
4622
4623 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_best = {}; // best rating for each company, including OWNER_NONE
4624 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_sum = {}; // sum of ratings for each company
4625 uint best_rating = 0;
4626 uint best_sum = 0; // sum of best ratings for each company
4627
4628 for (auto &p : used_stations) {
4629 auto owner = p.first->owner;
4630 auto rating = p.first->goods[cargo].rating;
4631 if (rating > company_best[owner]) {
4632 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4633 company_best[owner] = rating;
4634 if (rating > best_rating) best_rating = rating;
4635 }
4636 company_sum[owner] += rating;
4637 }
4638
4639 /* From now we'll calculate with fractional cargo amounts.
4640 * First determine how much cargo we really have. */
4641 amount *= best_rating + 1;
4642
4643 uint moving = 0;
4644 for (auto &p : used_stations) {
4645 Owner owner = p.first->owner;
4646 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4647 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4648 p.second = amount * company_best[owner] * p.first->goods[cargo].rating / best_sum / company_sum[owner];
4649 moving += p.second;
4650 }
4651
4652 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4653 if (amount > moving) {
4654 std::stable_sort(used_stations.begin(), used_stations.end(), [cargo](const StationInfo &a, const StationInfo &b) {
4655 return b.first->goods[cargo].rating < a.first->goods[cargo].rating;
4656 });
4657
4658 assert(amount - moving <= used_stations.size());
4659 for (uint i = 0; i < amount - moving; i++) {
4660 used_stations[i].second++;
4661 }
4662 }
4663
4664 uint moved = 0;
4665 for (auto &p : used_stations) {
4666 moved += UpdateStationWaiting(p.first, cargo, p.second, source);
4667 }
4668
4669 return moved;
4670}
4671
4672void UpdateStationDockingTiles(Station *st)
4673{
4674 st->docking_station.Clear();
4675
4676 /* For neutral stations, start with the industry area instead of dock area */
4677 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4678
4679 if (area->tile == INVALID_TILE) return;
4680
4681 int x = TileX(area->tile);
4682 int y = TileY(area->tile);
4683
4684 /* Expand the area by a tile on each side while
4685 * making sure that we remain inside the map. */
4686 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4687 int x1 = std::max<int>(x - 1, 0);
4688
4689 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4690 int y1 = std::max<int>(y - 1, 0);
4691
4692 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4693 for (TileIndex tile : ta) {
4694 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4695 }
4696}
4697
4698void BuildOilRig(TileIndex tile)
4699{
4700 if (!Station::CanAllocateItem()) {
4701 Debug(Facility::Misc, Severity::Critical, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4702 return;
4703 }
4704
4705 Station *st = Station::Create(tile);
4706 _station_kdtree.Insert(st->index);
4707 st->town = ClosestTownFromTile(tile, UINT_MAX);
4708
4710
4711 assert(IsTileType(tile, TileType::Industry));
4712 /* Mark industry as associated both ways */
4713 st->industry = Industry::GetByTile(tile);
4714 st->industry->neutral_station = st;
4715 DeleteAnimatedTile(tile);
4716 MakeOilrig(tile, st->index, GetWaterClass(tile));
4717
4718 st->owner = OWNER_NONE;
4719 st->airport.type = AT_OILRIG;
4721 st->airport.Add(tile);
4722 st->ship_station.Add(tile);
4725 UpdateStationDockingTiles(st);
4726
4727 st->spread.Add(tile);
4728
4729 st->UpdateVirtCoord();
4730
4731 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4732 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4733 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4735 StationList nearby = std::move(st->industry->stations_near);
4736 st->industry->stations_near.clear();
4737 for (Station *near : nearby) {
4738 near->RecomputeCatchment(true);
4739 UpdateStationAcceptance(near, true);
4740 }
4741 }
4742
4743 st->RecomputeCatchment();
4744 UpdateStationAcceptance(st, false);
4745}
4746
4747void DeleteOilRig(TileIndex tile)
4748{
4749 Station *st = Station::GetByTile(tile);
4750
4751 MakeWaterKeepingClass(tile, OWNER_NONE);
4752
4753 /* The oil rig station is not supposed to be shared with anything else */
4754 [[maybe_unused]] static constexpr StationFacilities expected_facility{StationFacility::Airport, StationFacility::Dock};
4755 assert(st->facilities == expected_facility && st->airport.type == AT_OILRIG);
4756 if (st->industry != nullptr && st->industry->neutral_station == st) {
4757 /* Don't leave dangling neutral station pointer */
4758 st->industry->neutral_station = nullptr;
4759 }
4760 delete st;
4761}
4762
4764static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4765{
4766 if (IsAnyRoadStopTile(tile)) {
4767 for (RoadTramType rtt : ROADTRAMTYPES_ALL) {
4768 /* Update all roadtypes, no matter if they are present */
4769 if (GetRoadOwner(tile, rtt) == old_owner) {
4770 RoadType rt = GetRoadType(tile, rtt);
4771 if (rt != INVALID_ROADTYPE) {
4772 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4773 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4774 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4775 }
4776 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4777 }
4778 }
4779 }
4780
4781 if (!IsTileOwner(tile, old_owner)) return;
4782
4783 if (new_owner != INVALID_OWNER) {
4784 /* Update company infrastructure counts. Only do it here
4785 * if the new owner is valid as otherwise the clear
4786 * command will do it for us. No need to dirty windows
4787 * here, we'll redraw the whole screen anyway.*/
4788 Company *old_company = Company::Get(old_owner);
4789 Company *new_company = Company::Get(new_owner);
4790
4791 /* Update counts for underlying infrastructure. */
4792 switch (GetStationType(tile)) {
4793 case StationType::Rail:
4795 if (!IsStationTileBlocked(tile)) {
4796 old_company->infrastructure.rail[GetRailType(tile)]--;
4797 new_company->infrastructure.rail[GetRailType(tile)]++;
4798 }
4799 break;
4800
4801 case StationType::Bus:
4802 case StationType::Truck:
4804 /* Road stops were already handled above. */
4805 break;
4806
4807 case StationType::Buoy:
4808 case StationType::Dock:
4809 if (GetWaterClass(tile) == WaterClass::Canal) {
4810 old_company->infrastructure.water--;
4811 new_company->infrastructure.water++;
4812 }
4813 break;
4814
4815 default:
4816 break;
4817 }
4818
4819 /* Update station tile count. */
4820 if (!IsBuoy(tile) && !IsAirport(tile)) {
4821 old_company->infrastructure.station--;
4822 new_company->infrastructure.station++;
4823 }
4824
4825 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4826 SetTileOwner(tile, new_owner);
4827 InvalidateWindowClassesData(WindowClass::StationList, 0);
4828 } else {
4829 if (IsDriveThroughStopTile(tile)) {
4830 /* Remove the drive-through road stop */
4831 if (IsRoadWaypoint(tile)) {
4832 Command<Commands::RemoveFromRoadWaypoint>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile, tile);
4833 } else {
4834 Command<Commands::RemoveRoadStop>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile, 1, 1, (GetStationType(tile) == StationType::Truck) ? RoadStopType::Truck : RoadStopType::Bus, false);
4835 }
4836 assert(IsTileType(tile, TileType::Road));
4837 /* Change owner of tile and all roadtypes */
4838 ChangeTileOwner(tile, old_owner, new_owner);
4839 } else {
4840 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
4841 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4842 * Update owner of buoy if it was not removed (was in orders).
4843 * Do not update when owned by OWNER_WATER (sea and rivers). */
4844 if ((IsTileType(tile, TileType::Water) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4845 }
4846 }
4847}
4848
4858{
4859 /* Water flooding can always clear road stops. */
4860 if (_current_company == OWNER_WATER) return CommandCost();
4861
4862 CommandCost ret;
4863
4864 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4865 Owner tram_owner = GetRoadOwner(tile, RoadTramType::Tram);
4866 if (tram_owner != OWNER_NONE) {
4867 ret = CheckOwnership(tram_owner);
4868 if (ret.Failed()) return ret;
4869 }
4870 }
4871
4872 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4873 Owner road_owner = GetRoadOwner(tile, RoadTramType::Road);
4874 if (road_owner == OWNER_TOWN) {
4876 if (ret.Failed()) return ret;
4877 } else if (road_owner != OWNER_NONE) {
4878 ret = CheckOwnership(road_owner);
4879 if (ret.Failed()) return ret;
4880 }
4881 }
4882
4883 return CommandCost();
4884}
4885
4888{
4889 if (flags.Test(DoCommandFlag::Auto)) {
4890 switch (GetStationType(tile)) {
4891 default: break;
4892 case StationType::Rail: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4893 case StationType::RailWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4894 case StationType::Airport: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4895 case StationType::Truck: return CommandCost(HasTileRoadType(tile, RoadTramType::Tram) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4896 case StationType::Bus: return CommandCost(HasTileRoadType(tile, RoadTramType::Tram) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4897 case StationType::RoadWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4898 case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4899 case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4901 return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG);
4902 }
4903 }
4904
4905 switch (GetStationType(tile)) {
4906 case StationType::Rail: return RemoveRailStation(tile, flags);
4907 case StationType::RailWaypoint: return RemoveRailWaypoint(tile, flags);
4908 case StationType::Airport: return RemoveAirport(tile, flags);
4909 case StationType::Truck: [[fallthrough]];
4910 case StationType::Bus:
4911 if (IsDriveThroughStopTile(tile)) {
4912 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4913 if (remove_road.Failed()) return remove_road;
4914 }
4915 return RemoveRoadStop(tile, flags);
4917 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4918 if (remove_road.Failed()) return remove_road;
4919 return RemoveRoadWaypointStop(tile, flags);
4920 }
4921 case StationType::Buoy: return RemoveBuoy(tile, flags);
4922 case StationType::Dock: return RemoveDock(tile, flags);
4923 default: break;
4924 }
4925
4926 return CMD_ERROR;
4927}
4928
4930static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
4931{
4932 if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
4933 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4934 * TTDP does not call it.
4935 */
4936 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4937 switch (GetStationType(tile)) {
4939 case StationType::Rail: {
4940 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4942 }
4943
4946
4947 case StationType::Truck:
4948 case StationType::Bus:
4950 if (IsDriveThroughStopTile(tile)) {
4951 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4952 } else {
4953 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4954 }
4956 }
4957
4958 default: break;
4959 }
4960 }
4961 }
4962 return Command<Commands::LandscapeClear>::Do(flags, tile);
4963}
4964
4970uint FlowStat::GetShare(StationID st) const
4971{
4972 uint32_t prev = 0;
4973 for (const auto &it : this->shares) {
4974 if (it.second == st) {
4975 return it.first - prev;
4976 } else {
4977 prev = it.first;
4978 }
4979 }
4980 return 0;
4981}
4982
4989StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const
4990{
4991 if (this->unrestricted == 0) return StationID::Invalid();
4992 assert(!this->shares.empty());
4993 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
4994 assert(it != this->shares.end() && it->first <= this->unrestricted);
4995 if (it->second != excluded && it->second != excluded2) return it->second;
4996
4997 /* We've hit one of the excluded stations.
4998 * Draw another share, from outside its range. */
4999
5000 uint end = it->first;
5001 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
5002 uint interval = end - begin;
5003 if (interval >= this->unrestricted) return StationID::Invalid(); // Only one station in the map.
5004 uint new_max = this->unrestricted - interval;
5005 uint rand = RandomRange(new_max);
5006 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
5007 this->shares.upper_bound(rand + interval);
5008 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
5009 if (it2->second != excluded && it2->second != excluded2) return it2->second;
5010
5011 /* We've hit the second excluded station.
5012 * Same as before, only a bit more complicated. */
5013
5014 uint end2 = it2->first;
5015 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
5016 uint interval2 = end2 - begin2;
5017 if (interval2 >= new_max) return StationID::Invalid(); // Only the two excluded stations in the map.
5018 new_max -= interval2;
5019 if (begin > begin2) {
5020 std::swap(begin, begin2);
5021 std::swap(end, end2);
5022 std::swap(interval, interval2);
5023 }
5024 rand = RandomRange(new_max);
5025 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
5026 if (rand < begin) {
5027 it3 = this->shares.upper_bound(rand);
5028 } else if (rand < begin2 - interval) {
5029 it3 = this->shares.upper_bound(rand + interval);
5030 } else {
5031 it3 = this->shares.upper_bound(rand + interval + interval2);
5032 }
5033 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
5034 return it3->second;
5035}
5036
5043{
5044 assert(!this->shares.empty());
5045 SharesMap new_shares;
5046 uint i = 0;
5047 for (const auto &it : this->shares) {
5048 new_shares[++i] = it.second;
5049 if (it.first == this->unrestricted) this->unrestricted = i;
5050 }
5051 this->shares.swap(new_shares);
5052 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
5053}
5054
5061void FlowStat::ChangeShare(StationID st, int flow)
5062{
5063 /* We assert only before changing as afterwards the shares can actually
5064 * be empty. In that case the whole flow stat must be deleted then. */
5065 assert(!this->shares.empty());
5066
5067 uint removed_shares = 0;
5068 uint added_shares = 0;
5069 uint last_share = 0;
5070 SharesMap new_shares;
5071 for (const auto &it : this->shares) {
5072 if (it.second == st) {
5073 if (flow < 0) {
5074 uint share = it.first - last_share;
5075 if (flow == INT_MIN || (uint)(-flow) >= share) {
5076 removed_shares += share;
5077 if (it.first <= this->unrestricted) this->unrestricted -= share;
5078 if (flow != INT_MIN) flow += share;
5079 last_share = it.first;
5080 continue; // remove the whole share
5081 }
5082 removed_shares += (uint)(-flow);
5083 } else {
5084 added_shares += (uint)(flow);
5085 }
5086 if (it.first <= this->unrestricted) this->unrestricted += flow;
5087
5088 /* If we don't continue above the whole flow has been added or
5089 * removed. */
5090 flow = 0;
5091 }
5092 new_shares[it.first + added_shares - removed_shares] = it.second;
5093 last_share = it.first;
5094 }
5095 if (flow > 0) {
5096 new_shares[last_share + (uint)flow] = st;
5097 if (this->unrestricted < last_share) {
5098 this->ReleaseShare(st);
5099 } else {
5100 this->unrestricted += flow;
5101 }
5102 }
5103 this->shares.swap(new_shares);
5104}
5105
5111void FlowStat::RestrictShare(StationID st)
5112{
5113 assert(!this->shares.empty());
5114 uint flow = 0;
5115 uint last_share = 0;
5116 SharesMap new_shares;
5117 for (auto &it : this->shares) {
5118 if (flow == 0) {
5119 if (it.first > this->unrestricted) return; // Not present or already restricted.
5120 if (it.second == st) {
5121 flow = it.first - last_share;
5122 this->unrestricted -= flow;
5123 } else {
5124 new_shares[it.first] = it.second;
5125 }
5126 } else {
5127 new_shares[it.first - flow] = it.second;
5128 }
5129 last_share = it.first;
5130 }
5131 if (flow == 0) return;
5132 new_shares[last_share + flow] = st;
5133 this->shares.swap(new_shares);
5134 assert(!this->shares.empty());
5135}
5136
5142void FlowStat::ReleaseShare(StationID st)
5143{
5144 assert(!this->shares.empty());
5145 uint flow = 0;
5146 uint next_share = 0;
5147 bool found = false;
5148 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
5149 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
5150 if (found) {
5151 flow = next_share - it->first;
5152 this->unrestricted += flow;
5153 break;
5154 } else {
5155 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
5156 if (it->second == st) found = true;
5157 }
5158 next_share = it->first;
5159 }
5160 if (flow == 0) return;
5161 SharesMap new_shares;
5162 new_shares[flow] = st;
5163 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
5164 if (it->second != st) {
5165 new_shares[flow + it->first] = it->second;
5166 } else {
5167 flow = 0;
5168 }
5169 }
5170 this->shares.swap(new_shares);
5171 assert(!this->shares.empty());
5172}
5173
5180{
5181 assert(runtime > 0);
5182 SharesMap new_shares;
5183 uint share = 0;
5184 for (auto i : this->shares) {
5185 share = std::max(share + 1, i.first * 30 / runtime);
5186 new_shares[share] = i.second;
5187 if (this->unrestricted == i.first) this->unrestricted = share;
5188 }
5189 this->shares.swap(new_shares);
5190}
5191
5198void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5199{
5200 FlowStatMap::iterator origin_it = this->find(origin);
5201 if (origin_it == this->end()) {
5202 this->emplace(origin, FlowStat(via, flow));
5203 } else {
5204 origin_it->second.ChangeShare(via, flow);
5205 assert(!origin_it->second.GetShares()->empty());
5206 }
5207}
5208
5217void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5218{
5219 FlowStatMap::iterator prev_it = this->find(origin);
5220 if (prev_it == this->end()) {
5221 FlowStat fs(via, flow);
5222 fs.AppendShare(StationID::Invalid(), flow);
5223 this->emplace(origin, fs);
5224 } else {
5225 prev_it->second.ChangeShare(via, flow);
5226 prev_it->second.ChangeShare(StationID::Invalid(), flow);
5227 assert(!prev_it->second.GetShares()->empty());
5228 }
5229}
5230
5236{
5237 for (auto &i : *this) {
5238 FlowStat &fs = i.second;
5239 uint local = fs.GetShare(StationID::Invalid());
5240 if (local > INT_MAX) { // make sure it fits in an int
5241 fs.ChangeShare(self, -INT_MAX);
5242 fs.ChangeShare(StationID::Invalid(), -INT_MAX);
5243 local -= INT_MAX;
5244 }
5245 fs.ChangeShare(self, -(int)local);
5246 fs.ChangeShare(StationID::Invalid(), -(int)local);
5247
5248 /* If the local share is used up there must be a share for some
5249 * remote station. */
5250 assert(!fs.GetShares()->empty());
5251 }
5252}
5253
5260std::vector<StationID> FlowStatMap::DeleteFlows(StationID via)
5261{
5262 std::vector<StationID> ret;
5263 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5264 FlowStat &s_flows = f_it->second;
5265 s_flows.ChangeShare(via, INT_MIN);
5266 if (s_flows.GetShares()->empty()) {
5267 ret.push_back(f_it->first);
5268 this->erase(f_it++);
5269 } else {
5270 ++f_it;
5271 }
5272 }
5273 return ret;
5274}
5275
5281{
5282 for (auto &it : *this) {
5283 it.second.RestrictShare(via);
5284 }
5285}
5286
5291void FlowStatMap::ReleaseFlows(StationID via)
5292{
5293 for (auto &it : *this) {
5294 it.second.ReleaseShare(via);
5295 }
5296}
5297
5303{
5304 uint ret = 0;
5305 for (const auto &it : *this) {
5306 ret += (--(it.second.GetShares()->end()))->first;
5307 }
5308 return ret;
5309}
5310
5316uint FlowStatMap::GetFlowVia(StationID via) const
5317{
5318 uint ret = 0;
5319 for (const auto &it : *this) {
5320 ret += it.second.GetShare(via);
5321 }
5322 return ret;
5323}
5324
5330uint FlowStatMap::GetFlowFrom(StationID from) const
5331{
5332 FlowStatMap::const_iterator i = this->find(from);
5333 if (i == this->end()) return 0;
5334 return (--(i->second.GetShares()->end()))->first;
5335}
5336
5343uint FlowStatMap::GetFlowFromVia(StationID from, StationID via) const
5344{
5345 FlowStatMap::const_iterator i = this->find(from);
5346 if (i == this->end()) return 0;
5347 return i->second.GetShare(via);
5348}
5349
5351static CommandCost CheckBuildAbove_Station(TileIndex tile, [[maybe_unused]] DoCommandFlags flags, [[maybe_unused]] Axis axis, int height)
5352{
5353 StationType type = GetStationType(tile);
5354 auto bridgeable_info = GetStationBridgeableTileInfo(type);
5355
5356 switch (type) {
5357 case StationType::Rail:
5359 if (const StationSpec *spec = GetStationSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5360 break;
5361
5362 case StationType::Bus:
5363 case StationType::Truck:
5365 if (const RoadStopSpec *spec = GetRoadStopSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5366 break;
5367
5368 default: break;
5369 }
5370
5371 return IsStationBridgeAboveOk(tile, bridgeable_info, type, GetStationGfx(tile), height);
5372}
5373
5375extern const TileTypeProcs _tile_type_station_procs = {
5376 .draw_tile_proc = DrawTile_Station,
5377 .get_slope_pixel_z_proc = [](TileIndex tile, uint, uint, bool) { return GetTileMaxPixelZ(tile); },
5378 .clear_tile_proc = ClearTile_Station,
5379 .get_tile_desc_proc = GetTileDesc_Station,
5380 .get_tile_track_status_proc = GetTileTrackStatus_Station,
5381 .click_tile_proc = ClickTile_Station,
5382 .animate_tile_proc = AnimateTile_Station,
5383 .tile_loop_proc = TileLoop_Station,
5384 .change_tile_owner_proc = ChangeTileOwner_Station,
5385 .vehicle_enter_tile_proc = VehicleEnterTile_Station,
5386 .get_foundation_proc = [](TileIndex, Slope tileh) { return FlatteningFoundation(tileh); },
5387 .terraform_tile_proc = TerraformTile_Station,
5388 .check_build_above_proc = CheckBuildAbove_Station,
5389};
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:131
@ FLYING
Vehicle is flying in the air.
Definition airport.h:78
@ 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
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 uint8_t FindFirstBit(T x)
Search the first set bit in a value.
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
EnumBitSet< BridgePillarFlag, uint8_t > BridgePillarFlags
Bitset of BridgePillarFlag elements.
Definition bridge_type.h:53
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
EnumBitSet< CargoType, uint64_t > CargoTypes
Bitset of CargoType elements.
Definition cargo_type.h:113
static constexpr CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
@ Passengers
Passengers.
Definition cargotype.h:51
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:238
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.
uint Count() const
Count the number of set bits.
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 bool None() const
Test if none of the values are set.
constexpr Timpl & Flip()
Flip all bits.
constexpr Timpl & Reset()
Reset all bits.
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.
Definition bitmap_type.h:94
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?
Iterate a range of enum values.
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.
const RailStationTileLayout & stl
Station tile layout being iterated.
uint position
Position within iterator.
StationGfx operator*() const
Dereference operator.
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:117
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:127
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:222
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:128
StringID name
Name of this rail type.
Definition rail.h:167
uint8_t fallback_railtype
Original railtype number to use when drawing non-newgrf railtypes, or when drawing stations.
Definition rail.h:192
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:289
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:118
struct RoadTypeInfo::@070000167274302256150317022075324310363002361255 strings
Strings associated with the rail type.
StringID name
Name of this rail type.
Definition road.h:79
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.
StrongType::Typedef< int32_t, DateTag< struct Economy >, StrongType::Compare, StrongType::Integer > Date
Iterate over all vehicles on a tile.
void DrawClearLandTile(const TileInfo *ti, uint8_t density)
Draw a ClearGround::Grass tile.
Definition clear_cmd.cpp:61
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.
EnumBitSet< DoCommandFlag, uint16_t > DoCommandFlags
Bitset of DoCommandFlag elements.
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(facility, severity, format_string,...)
Output a line of debugging information.
Definition debug.h:37
@ Misc
Misc message facility.
Definition debug_type.h:32
@ Critical
Critical, user should know about this.
Definition debug_type.h:15
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.
EnumIndexArray< T, DiagDirection, DiagDirection::End > DiagDirectionIndexArray
Array with DiagDirection as index.
Direction
Defines the 8 directions on the map.
Axis
Enumeration for the two axis X and Y.
@ X
The X axis.
DiagDirection
Enumeration for diagonal directions.
@ Invalid
Flag for an invalid DiagDirection.
@ SW
Southwest.
@ End
Used for iterations.
@ SE
Southeast.
EnumBitSet< DiagDirection, uint8_t > DiagDirections
Bitset of DiagDirection elements.
Prices _price
Prices and also the fractional part.
Definition economy.cpp:107
static const uint ROAD_STOP_TRACKBIT_FACTOR
Multiplier for how many regular track bits a bay stop counts.
@ 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:553
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
EnumClassIndexContainer< std::array< T, to_underlying(N)>, Index > EnumIndexArray
A typedef for EnumClassIndexContainer using std::array as the backing container type.
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:1037
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.
static const SpriteID SPR_RAIL_TRACK_Y
Definition sprites.h:449
static const SpriteID SPR_MONO_TRACK_X_SNOW
Definition sprites.h:496
static const SpriteID SPR_MGLV_TRACK_Y_SNOW
Definition sprites.h:508
static const SpriteID SPR_MONO_TRACK_Y
Definition sprites.h:491
static const SpriteID SPR_MONO_TRACK_Y_SNOW
Definition sprites.h:495
static const SpriteID SPR_MONO_TRACK_X
Definition sprites.h:492
static const SpriteID SPR_RAIL_TRACK_X_SNOW
Definition sprites.h:454
static const SpriteID SPR_MGLV_TRACK_X
Definition sprites.h:506
static const SpriteID SPR_RAIL_TRACK_Y_SNOW
Definition sprites.h:453
static const SpriteID SPR_MGLV_TRACK_X_SNOW
Definition sprites.h:509
static const SpriteID SPR_MGLV_TRACK_Y
Definition sprites.h:505
static const SpriteID SPR_RAIL_TRACK_X
Definition sprites.h:450
HouseZone
Concentric rings of zoning around the centre of a town.
Definition house.h:59
@ TownEdge
Edge of the town; roads without pavement.
Definition house.h:60
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.
uint GetFoundationSpriteBlock(TileIndex tile)
Get the sprite block to use for a foundation.
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.
EnumBitSet< EdgeUpdateMode, uint8_t > EdgeUpdateModes
Bitset of EdgeUpdateMode elements.
@ 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.
@ Buoy
Buoy without underlying water.
Definition newgrf.h:37
@ RoadStops
Road stops feature.
Definition newgrf.h:100
@ Stations
Stations feature.
Definition newgrf.h:84
@ Airports
Airports feature.
Definition newgrf.h:93
@ AirportTiles
Airport tiles feature.
Definition newgrf.h:97
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.
bool Convert8bitBooleanCallback(const GRFFile *grffile, uint16_t cbid, uint16_t cb_res)
Converts a callback result into a boolean.
void ErrorUnknownCallbackResult(GrfID grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
GRFConfig * GetGRFConfig(GrfID grfid, uint32_t mask)
Retrieve a NewGRF from the current config by its grfid.
@ Any
Use first found.
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, RailSpriteType 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.
EnumBitSet< RoadStopDrawMode, uint8_t > RoadStopDrawModes
Bitset of RoadStopDrawMode elements.
@ 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, RoadSpriteType rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of road types.
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.
uint32_t GetPlatformInfo(StationGfx gfx, int platforms, int length, int platform, int position, bool centred)
Evaluate a tile's position within a station, and return the result in a bit-stuffed format.
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.
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:917
@ 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:78
@ Editor
In the scenario editor.
Definition openttd.h:21
@ Menu
In the main menu.
Definition openttd.h:19
Functions related to order backups.
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:345
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
@ Ground
Main group of ground images.
Definition rail.h:45
@ Overlay
Images for overlaying track.
Definition rail.h:44
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:431
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:379
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:303
RailTrackOffset
Offsets for sprites within an overlay/underlay set.
Definition rail.h:63
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:65
@ RTO_X
Piece of rail in X direction.
Definition rail.h:64
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:624
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:26
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:33
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:232
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:217
@ Roadstop
Required: Bay stop surface.
Definition road.h:49
@ Ground
Required: Main group of ground images.
Definition road.h:41
@ Overlay
Optional: Images for overlaying track.
Definition road.h:40
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:242
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:93
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 RoadTramType::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 RoadTramType::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:635
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:472
@ Paved
Road with paved sidewalks.
Definition road_map.h:475
@ Barren
Road on barren land.
Definition road_map.h:473
@ Grass
Road on grass.
Definition road_map.h:474
static constexpr RoadBits ROAD_X
Full road along the x-axis (south-west + north-east).
Definition road_type.h:67
EnumBitSet< RoadBit, uint8_t > RoadBits
Bitset of RoadBit elements.
Definition road_type.h:65
static constexpr RoadBits ROAD_Y
Full road along the y-axis (north-west + south-east).
Definition road_type.h:68
static constexpr RoadTramTypes ROADTRAMTYPES_ALL
All possible RoadTramTypes.
Definition road_type.h:49
RoadType
The different roadtypes we support.
Definition road_type.h:24
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:29
RoadTramType
The different types of road type.
Definition road_type.h:38
@ Tram
Tram type.
Definition road_type.h:40
@ Road
Road type.
Definition road_type.h:39
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:569
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:598
@ 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:170
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:377
DiagDirection GetInclinedSlopeDirection(Slope s)
Returns the direction of an inclined slope.
Definition slope_func.h:249
Slope
Enumeration for the slope-type.
Definition slope_type.h:53
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:54
@ Leveled
The tile is leveled up to a flat slope.
Definition slope_type.h:121
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 SpriteID SPR_TRAMWAY_TRAM
Definition sprites.h:304
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1794
static const SpriteID SPR_FLAT_SNOW_DESERT_TILE
Definition sprites.h:689
static const PaletteID PALETTE_TO_GREY
Definition sprites.h:1778
static const SpriteID SPR_IMG_BUOY
Definition sprites.h:1464
static const SpriteID SPR_FLAT_GRASS_TILE
Definition sprites.h:677
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1732
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1722
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
Definition sprites.h:1735
CommandCost CheckStationSpread(TileArea area, const TileArea &new_area)
Check if adding a new area to a station will exceed the maximum station spread.
Definition station.cpp:528
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 void MakeStationSpreadAreaSmaller(BaseStation *st)
Make the spread area of a station as small as possible.
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.
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 constexpr DiagDirectionIndexArray< uint8_t > _dock_w_chk
X dimension of dock for each direction.
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.
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.
@ Dock
Ship dock.
@ Rail
Railway station.
@ Road
Truck or bus stop.
@ Oilrig
Heliport of an oilrig.
@ Heliport
Standalone heliport.
@ 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.
static constexpr DiagDirectionIndexArray< TileIndexDiffC > _dock_tileoffs_chkaround
Offset of northern-most dock tile for each direction.
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.
static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, RoadTramType sub_mode, DiagDirection side)
Tile callback function signature for getting the possible tracks that can be taken on a given tile by...
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.
CargoTypes GetCargoWaitingMask(const Station *st)
Get a mask of the cargo types that have cargo waiting at the station.
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(StringID error_message, StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
Find a nearby station that joins this station.
static constexpr DiagDirectionIndexArray< uint8_t > _dock_h_chk
Y dimension of dock for each direction.
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.
@ Count
by amount of cargo
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.
@ 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.
EnumBitSet< StationFacility, uint8_t > StationFacilities
Bitset of StationFacility elements.
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.
StrongType::Typedef< uint32_t, struct StringIDTag, StrongType::Compare, StrongType::Integer > StringID
Numeric value that represents a string, independent of the selected language.
static constexpr 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:75
@ Airplanes
Can planes land on this airport type?
Definition airport.h:165
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...
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.
TileArea spread
NOSAVE: Station spread tile area.
VehicleType type
Type of vehicle.
Class for storing amounts of cargo.
Definition cargo_type.h:118
Specification of a cargo type.
Definition cargotype.h:77
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:141
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:111
static IterateWrapper Iterate(size_t from=0)
Returns an iterable ensemble of all valid CargoSpec.
Definition cargotype.h:194
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.
GrfID grfid
grfid that introduced this entity.
const struct GRFFile * grffile
grf file 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:64
IndustryType type
type of industry.
Definition industry.h:117
ProducedCargoes produced
produced cargo slots
Definition industry.h:112
Owner owner
owner of the industry. Which SHOULD always be (imho) OWNER_NONE
Definition industry.h:118
static Industry * GetByTile(TileIndex tile)
Get the industry of the given tile.
Definition industry.h:253
TileArea location
Location of the industry.
Definition industry.h:108
Station * neutral_station
Associated neutral station.
Definition industry.h:110
StationList stations_near
NOSAVE: List of nearby stations.
Definition industry.h:125
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...
bool Contains(TileIndex tile) const
Does this tile area contain a tile?
Definition tilearea.cpp:104
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.
bool IsEmpty() const
Test if this tile area is empty.
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 T * Create(Targs &&... args)
static Industry * Get(auto index)
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).base()> used_names
Used default station suffixes.
bool IsAvailable(StringID str) const
Is the given station name available, and not already used?
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::vector< TileFlags > tileflags
List of tile flags.
std::vector< NewGRFSpriteLayout > renderdata
Number of tile layouts.
EnumBitSet< TileFlag, uint8_t > TileFlags
Bitset of TileFlag elements.
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:448
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:40
uint16_t rail_speed
Speed limit of rail (bridges and track).
Definition tile_cmd.h:53
std::optional< std::string > grf
newGRF used for the tile contents
Definition tile_cmd.h:51
StringID station_name
Type of station within the class.
Definition tile_cmd.h:47
StringID str
Description of the tile.
Definition tile_cmd.h:41
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:45
std::array< Owner, 4 > owner
Name of the owner(s).
Definition tile_cmd.h:43
StringID airport_class
Name of the airport class.
Definition tile_cmd.h:48
StringID airport_name
Name of the airport.
Definition tile_cmd.h:49
uint16_t tram_speed
Speed limit of tram (bridges and track).
Definition tile_cmd.h:57
StringID roadtype
Type of road on the tile.
Definition tile_cmd.h:54
StringID tramtype
Type of tram on the tile.
Definition tile_cmd.h:56
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:52
uint16_t road_speed
Speed limit of road (bridges and track).
Definition tile_cmd.h:55
std::array< StringID, 4 > owner_type
Type of each owner.
Definition tile_cmd.h:44
StringID airport_tile_name
Name of the airport tile.
Definition tile_cmd.h:50
StringID station_class
Class of station.
Definition tile_cmd.h:46
Tile information, used while rendering the tile.
Definition tile_cmd.h:34
Slope tileh
Slope of the tile.
Definition tile_cmd.h:35
TileIndex tile
Tile index.
Definition tile_cmd.h:36
Town data structure.
Definition town.h:64
CompanyMask statues
which companies have a statue?
Definition town.h:82
TileIndex xy
town center tile
Definition town.h:65
uint16_t noise_reached
level of noise that all the airports are generating
Definition town.h:80
uint16_t MaxTownNoise() const
Calculate the max town noise.
Definition town.h:224
CompanyID exclusivity
which company has exclusivity
Definition town.h:87
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:88
Track status of a tile.
Definition track_type.h:105
'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 GetMovingDirection() const
Get the moving direction of this vehicle chain.
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
bool IsMovingFront() const
Is this vehicle the moving front of the vehicle chain?
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.
Vehicle * First() const
Get the first vehicle of this vehicle chain.
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:25
@ CannotEnter
The vehicle cannot enter the tile.
Definition tile_cmd.h:27
void AddProducedCargo(TileIndex tile, CargoArray &produced)
Obtain the produced cargo of a tile.
Definition tile_cmd.h:257
EnumBitSet< VehicleEnterTileState, uint8_t > VehicleEnterTileStates
Bitset of VehicleEnterTileState elements.
Definition tile_cmd.h:31
void AddAcceptedCargo(TileIndex tile, CargoArray &acceptance, CargoTypes &always_accepted)
Obtain cargo acceptance of a tile.
Definition tile_cmd.h:245
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
@ 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,...
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:292
bool IsReversingRoadTrackdir(Trackdir dir)
Checks whether the trackdir means that we are reversing.
Definition track_func.h:568
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:220
Track AxisToTrack(Axis a)
Convert an Axis to the corresponding Track Axis::X -> Track::X Axis::Y -> Track::Y Uses the fact that...
Definition track_func.h:62
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:343
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a DiagDirection to the associated diagonal Track.
Definition track_func.h:419
static constexpr TrackBits TRACK_BIT_3WAY_NE
"Arrow" to the north-east
Definition track_type.h:48
EnumBitSet< Track, uint8_t > TrackBits
Bitset of Track elements.
Definition track_type.h:43
static constexpr TrackBits TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:52
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:63
Track
These are used to specify a single track.
Definition track_type.h:19
@ Upper
Track in the upper corner of the tile (north).
Definition track_type.h:23
@ Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ Left
Track in the left corner of the tile (west).
Definition track_type.h:25
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.
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *moving_front, 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.
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
@ Buildings
company buildings - depots, stations, HQ, ...
TransportType
Available types of transport.
@ Water
Transport over water.
@ Rail
Transport by train.
@ Road
Transport by road vehicle.
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:558
@ TrainSlowing
Train is slowing down.
Functions related to vehicles.
@ Ship
Ship vehicle type.
@ Invalid
Non-existing type of vehicle.
@ Aircraft
Aircraft vehicle type.
@ Road
Road vehicle type.
@ 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:605
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:765
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:664
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:775
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:356
bool IsTileOnWater(Tile t)
Tests if the tile was built on water.
Definition water_map.h:140
WaterClass
classes of water (for WaterTileType::Clear water tile type).
Definition water_map.h:41
@ Invalid
Used for industry tiles on land (also for oilrig if newgrf says so).
Definition water_map.h:45
@ Canal
Canal.
Definition water_map.h:43
@ Sea
Sea.
Definition water_map.h:42
bool HasTileWaterClass(Tile t)
Checks whether the tile has an waterclass associated.
Definition water_map.h:105
WaterClass GetWaterClass(Tile t)
Get the water class at a tile.
Definition water_map.h:116
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:378
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:367
bool IsWater(Tile t)
Is it a plain water tile?
Definition water_map.h:151
bool IsWaterTile(Tile t)
Is it a water tile with plain water?
Definition water_map.h:194
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:1204
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3242
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:3334
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:3228
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3212
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:3352
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