OpenTTD Source 20260206-master-g4d4e37dbf1
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
218enum StationNaming : uint8_t {
219 STATIONNAMING_RAIL,
220 STATIONNAMING_ROAD,
221 STATIONNAMING_AIRPORT,
222 STATIONNAMING_OILRIG,
223 STATIONNAMING_DOCK,
224 STATIONNAMING_HELIPORT,
225};
226
229 std::bitset<STR_SV_STNAME_FALLBACK - STR_SV_STNAME> used_names;
230 std::bitset<NUM_INDUSTRYTYPES> indtypes;
231
232 bool IsAvailable(StringID str) const
233 {
234 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
235 return !this->used_names.test(str - STR_SV_STNAME);
236 }
237
238 void SetUsed(StringID str)
239 {
240 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
241 this->used_names.set(str - STR_SV_STNAME);
242 }
243};
244
245static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
246{
247 const Town *t = st->town;
248
250
251 for (const Station *s : Station::Iterate()) {
252 if (s != st && s->town == t) {
253 if (s->indtype != IT_INVALID) {
254 sni.indtypes[s->indtype] = true;
255 StringID name = GetIndustrySpec(s->indtype)->station_name;
256 if (name != STR_UNDEFINED) {
257 /* Filter for other industrytypes with the same name */
258 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
259 const IndustrySpec *indsp = GetIndustrySpec(it);
260 if (indsp->enabled && indsp->station_name == name) sni.indtypes[it] = true;
261 }
262 }
263 continue;
264 }
265 if (IsInsideMM(s->string_id, STR_SV_STNAME, STR_SV_STNAME_FALLBACK)) {
266 auto str = s->string_id;
267 if (str == STR_SV_STNAME_FOREST) str = STR_SV_STNAME_WOODS;
268 sni.SetUsed(str);
269 }
270 }
271 }
272
273 for (auto indtile : SpiralTileSequence(tile, 7)) {
274 if (!IsTileType(indtile, TileType::Industry)) continue;
275
276 /* If the station name is undefined it means that it doesn't name a station */
277 IndustryType indtype = GetIndustryType(indtile);
278 const IndustrySpec *indsp = GetIndustrySpec(indtype);
279 if (indsp->station_name == STR_UNDEFINED) continue;
280
281 /* In all cases if an industry that provides a name is found two of
282 * the standard names will be disabled. */
283 sni.SetUsed(STR_SV_STNAME_OILFIELD);
284 sni.SetUsed(STR_SV_STNAME_MINES);
285 if (sni.indtypes[indtype]) continue;
286
287 /* STR_NULL means it only disables oil rig/mines */
288 if (indsp->station_name != STR_NULL) {
289 st->indtype = indtype;
290 return STR_SV_STNAME_FALLBACK;
291 }
292 break;
293 }
294
295 /* check default names
296 * Oil rigs/mines name could be marked not free by looking for a near by industry. */
297 switch (name_class) {
298 case STATIONNAMING_AIRPORT:
299 if (sni.IsAvailable(STR_SV_STNAME_AIRPORT)) return STR_SV_STNAME_AIRPORT;
300 break;
301 case STATIONNAMING_OILRIG:
302 if (sni.IsAvailable(STR_SV_STNAME_OILFIELD)) return STR_SV_STNAME_OILFIELD;
303 break;
304 case STATIONNAMING_DOCK:
305 if (sni.IsAvailable(STR_SV_STNAME_DOCKS)) return STR_SV_STNAME_DOCKS;
306 break;
307 case STATIONNAMING_HELIPORT:
308 if (sni.IsAvailable(STR_SV_STNAME_HELIPORT)) return STR_SV_STNAME_HELIPORT;
309 break;
310 default:
311 break;
312 };
313
314 /* check mine? */
315 if (sni.IsAvailable(STR_SV_STNAME_MINES)) {
316 if (CountMapSquareAround(tile, CMSAMine) >= 2) {
317 return STR_SV_STNAME_MINES;
318 }
319 }
320
321 /* check close enough to town to get central as name? */
322 if (DistanceMax(tile, t->xy) < 8) {
323 if (sni.IsAvailable(STR_SV_STNAME)) return STR_SV_STNAME;
324 if (sni.IsAvailable(STR_SV_STNAME_CENTRAL)) return STR_SV_STNAME_CENTRAL;
325 }
326
327 /* Check lakeside */
328 if (sni.IsAvailable(STR_SV_STNAME_LAKESIDE) &&
329 DistanceFromEdge(tile) < 20 &&
330 CountMapSquareAround(tile, CMSAWater) >= 5) {
331 return STR_SV_STNAME_LAKESIDE;
332 }
333
334 /* Check woods */
335 if (sni.IsAvailable(STR_SV_STNAME_WOODS) && (
336 CountMapSquareAround(tile, CMSATree) >= 8 ||
338 ) {
339 return _settings_game.game_creation.landscape == LandscapeType::Tropic ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
340 }
341
342 /* check elevation compared to town */
343 int z = GetTileZ(tile);
344 int z2 = GetTileZ(t->xy);
345 if (z < z2) {
346 if (sni.IsAvailable(STR_SV_STNAME_VALLEY)) return STR_SV_STNAME_VALLEY;
347 } else if (z > z2) {
348 if (sni.IsAvailable(STR_SV_STNAME_HEIGHTS)) return STR_SV_STNAME_HEIGHTS;
349 }
350
351 /* check direction compared to town */
352 if (TileX(tile) < TileX(t->xy)) {
353 sni.SetUsed(STR_SV_STNAME_SOUTH);
354 sni.SetUsed(STR_SV_STNAME_WEST);
355 } else {
356 sni.SetUsed(STR_SV_STNAME_NORTH);
357 sni.SetUsed(STR_SV_STNAME_EAST);
358 }
359 if (TileY(tile) < TileY(t->xy)) {
360 sni.SetUsed(STR_SV_STNAME_SOUTH);
361 sni.SetUsed(STR_SV_STNAME_EAST);
362 } else {
363 sni.SetUsed(STR_SV_STNAME_NORTH);
364 sni.SetUsed(STR_SV_STNAME_WEST);
365 }
366
368 static const StringID fallback_names[] = {
369 STR_SV_STNAME_NORTH,
370 STR_SV_STNAME_SOUTH,
371 STR_SV_STNAME_EAST,
372 STR_SV_STNAME_WEST,
373 STR_SV_STNAME_TRANSFER,
374 STR_SV_STNAME_HALT,
375 STR_SV_STNAME_EXCHANGE,
376 STR_SV_STNAME_ANNEXE,
377 STR_SV_STNAME_SIDINGS,
378 STR_SV_STNAME_BRANCH,
379 STR_SV_STNAME_UPPER,
380 STR_SV_STNAME_LOWER,
381 };
382 for (auto str : fallback_names) {
383 if (sni.IsAvailable(str)) return str;
384 }
385 return STR_SV_STNAME_FALLBACK;
386}
387
394{
395 uint threshold = 8;
396
397 Station *best_station = nullptr;
398 ForAllStationsRadius(tile, threshold, [&](Station *st) {
399 if (!st->IsInUse() && st->owner == _current_company) {
400 uint cur_dist = DistanceManhattan(tile, st->xy);
401
402 if (cur_dist < threshold) {
403 threshold = cur_dist;
404 best_station = st;
405 } else if (cur_dist == threshold && best_station != nullptr) {
406 /* In case of a tie, lowest station ID wins */
407 if (st->index < best_station->index) best_station = st;
408 }
409 }
410 });
411
412 return best_station;
413}
414
415
417{
418 switch (type) {
419 case StationType::Rail: return this->train_station;
420 case StationType::Airport: return this->airport;
421 case StationType::Truck: return this->truck_station;
422 case StationType::Bus: return this->bus_station;
424 case StationType::Oilrig: return this->docking_station;
425 default: NOT_REACHED();
426 }
427}
428
433{
434 Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
435
436 pt.y -= 32 * ZOOM_BASE;
437 if (this->facilities.Test(StationFacility::Airport) && this->airport.type == AT_OILRIG) pt.y -= 16 * ZOOM_BASE;
438
439 if (this->sign.kdtree_valid) _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeStation(this->index));
440
441 this->sign.UpdatePosition(pt.x, pt.y, GetString(STR_VIEWPORT_STATION, this->index, this->facilities), GetString(STR_STATION_NAME, this->index, this->facilities));
442
443 _viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeStation(this->index));
444
446}
447
453{
454 if (this->xy == new_xy) return;
455
456 _station_kdtree.Remove(this->index);
457
458 this->BaseStation::MoveSign(new_xy);
459
460 _station_kdtree.Insert(this->index);
461}
462
465{
466 for (BaseStation *st : BaseStation::Iterate()) {
467 st->UpdateVirtCoord();
468 }
469}
470
471void BaseStation::FillCachedName() const
472{
473 auto tmp_params = MakeParameters(this->index);
474 this->cached_name = GetStringWithArgs(Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params);
475}
476
477void ClearAllStationCachedNames()
478{
479 for (BaseStation *st : BaseStation::Iterate()) {
480 st->cached_name.clear();
481 }
482}
483
489CargoTypes GetAcceptanceMask(const Station *st)
490{
491 CargoTypes mask = 0;
492
493 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
494 if (it->status.Test(GoodsEntry::State::Acceptance)) SetBit(mask, std::distance(std::begin(st->goods), it));
495 }
496 return mask;
497}
498
504CargoTypes GetEmptyMask(const Station *st)
505{
506 CargoTypes mask = 0;
507
508 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
509 if (it->TotalCount() == 0) SetBit(mask, std::distance(std::begin(st->goods), it));
510 }
511 return mask;
512}
513
520static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
521{
522 StringID msg = reject ? STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_LIST : STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST;
524}
525
533CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
534{
535 CargoArray produced{};
536 FlatSet<IndustryID> industries;
537 TileArea ta = TileArea(north_tile, w, h).Expand(rad);
538
539 /* Loop over all tiles to get the produced cargo of
540 * everything except industries */
541 for (TileIndex tile : ta) {
542 if (IsTileType(tile, TileType::Industry)) industries.insert(GetIndustryIndex(tile));
543 AddProducedCargo(tile, produced);
544 }
545
546 /* Loop over the seen industries. They produce cargo for
547 * anything that is within 'rad' of any one of their tiles.
548 */
549 for (IndustryID industry : industries) {
550 const Industry *i = Industry::Get(industry);
551 /* Skip industry with neutral station */
552 if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) continue;
553
554 for (const auto &p : i->produced) {
555 if (IsValidCargoType(p.cargo)) produced[p.cargo]++;
556 }
557 }
558
559 return produced;
560}
561
570CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted)
571{
572 CargoArray acceptance{};
573 if (always_accepted != nullptr) *always_accepted = 0;
574
575 TileArea ta = TileArea(center_tile, w, h).Expand(rad);
576
577 for (TileIndex tile : ta) {
578 /* Ignore industry if it has a neutral station. */
579 if (!_settings_game.station.serve_neutral_industries && IsTileType(tile, TileType::Industry) && Industry::GetByTile(tile)->neutral_station != nullptr) continue;
580
581 AddAcceptedCargo(tile, acceptance, always_accepted);
582 }
583
584 return acceptance;
585}
586
592static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
593{
594 CargoArray acceptance{};
595 if (always_accepted != nullptr) *always_accepted = 0;
596
598 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
599 AddAcceptedCargo(tile, acceptance, always_accepted);
600 }
601
602 return acceptance;
603}
604
610void UpdateStationAcceptance(Station *st, bool show_msg)
611{
612 /* old accepted goods types */
613 CargoTypes old_acc = GetAcceptanceMask(st);
614
615 /* And retrieve the acceptance. */
616 CargoArray acceptance{};
617 if (!st->rect.IsEmpty()) {
618 acceptance = GetAcceptanceAroundStation(st, &st->always_accepted);
619 }
620
621 /* Adjust in case our station only accepts fewer kinds of goods */
622 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
623 uint amt = acceptance[cargo];
624
625 /* Make sure the station can accept the goods type. */
626 bool is_passengers = IsCargoInClass(cargo, CargoClass::Passengers);
627 if ((!is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::TruckStop, StationFacility::Airport, StationFacility::Dock})) ||
628 (is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::BusStop, StationFacility::Airport, StationFacility::Dock}))) {
629 amt = 0;
630 }
631
632 GoodsEntry &ge = st->goods[cargo];
635 (*LinkGraph::Get(ge.link_graph))[ge.node].SetDemand(amt / 8);
636 }
637 }
638
639 /* Only show a message in case the acceptance was actually changed. */
640 CargoTypes new_acc = GetAcceptanceMask(st);
641 if (old_acc == new_acc) return;
642
643 /* show a message to report that the acceptance was changed? */
644 if (show_msg && st->owner == _local_company && st->IsInUse()) {
645 /* Combine old and new masks to get changes */
646 CargoTypes accepts = new_acc & ~old_acc;
647 CargoTypes rejects = ~new_acc & old_acc;
648
649 /* Show news message if there are any changes */
650 if (accepts != 0) ShowRejectOrAcceptNews(st, accepts, false);
651 if (rejects != 0) ShowRejectOrAcceptNews(st, rejects, true);
652 }
653
654 /* redraw the station view since acceptance changed */
656}
657
658static void UpdateStationSignCoord(BaseStation *st)
659{
660 const StationRect *r = &st->rect;
661
662 if (r->IsEmpty()) return; // no tiles belong to this station
663
664 /* clamp sign coord to be inside the station rect */
665 TileIndex new_xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
666 st->MoveSign(new_xy);
667
668 if (!Station::IsExpected(st)) return;
669 Station *full_station = Station::From(st);
670 for (const GoodsEntry &ge : full_station->goods) {
671 LinkGraphID lg = ge.link_graph;
672 if (!LinkGraph::IsValidID(lg)) continue;
673 (*LinkGraph::Get(lg))[ge.node].UpdateLocation(st->xy);
674 }
675}
676
686static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
687{
688 /* Find a deleted station close to us */
689 if (*st == nullptr && reuse) *st = GetClosestDeletedStation(area.tile);
690
691 if (*st != nullptr) {
692 if ((*st)->owner != _current_company) {
693 return CommandCost(CMD_ERROR);
694 }
695
696 CommandCost ret = (*st)->rect.BeforeAddRect(area.tile, area.w, area.h, StationRect::ADD_TEST);
697 if (ret.Failed()) return ret;
698 } else {
699 /* allocate and initialize new station */
700 if (!Station::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_STATIONS_LOADING);
701
702 if (flags.Test(DoCommandFlag::Execute)) {
703 *st = Station::Create(area.tile);
704 _station_kdtree.Insert((*st)->index);
705
706 (*st)->town = ClosestTownFromTile(area.tile, UINT_MAX);
707 (*st)->string_id = GenerateStationName(*st, area.tile, name_class);
708
710 (*st)->town->have_ratings.Set(_current_company);
711 }
712 }
713 }
714 return CommandCost();
715}
716
724{
725 if (!st->IsInUse()) {
726 st->delete_ctr = 0;
728 }
729 /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
730 UpdateStationSignCoord(st);
731}
732
739{
740 this->UpdateVirtCoord();
742
743 if (adding) {
744 this->RecomputeCatchment();
745 MarkCatchmentTilesDirty();
747 } else {
748 MarkCatchmentTilesDirty();
749 }
750
751 switch (type) {
754 break;
756 break;
758 case StationType::Bus:
760 break;
763 break;
764 default: NOT_REACHED();
765 }
766
767 if (adding) {
768 UpdateStationAcceptance(this, false);
770 } else {
772 this->RecomputeCatchment();
773 }
774
775}
776
777CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags);
778
788CommandCost CheckBuildableTile(TileIndex tile, DiagDirections invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge = true)
789{
790 if (check_bridge && IsBridgeAbove(tile)) {
791 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
792 }
793
795 if (ret.Failed()) return ret;
796
797 auto [tileh, z] = GetTileSlopeZ(tile);
798
799 /* Prohibit building if
800 * 1) The tile is "steep" (i.e. stretches two height levels).
801 * 2) The tile is non-flat and the build_on_slopes switch is disabled.
802 */
803 if ((!allow_steep && IsSteepSlope(tileh)) ||
804 ((!_settings_game.construction.build_on_slopes) && tileh != SLOPE_FLAT)) {
805 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
806 }
807
809 int flat_z = z + GetSlopeMaxZ(tileh);
810 if (tileh != SLOPE_FLAT) {
811 /* Forbid building if the tile faces a slope in a invalid direction. */
812 for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) {
813 if (invalid_dirs.Test(dir) && !CanBuildDepotByTileh(dir, tileh)) {
814 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
815 }
816 }
817 cost.AddCost(_price[Price::BuildFoundation]);
818 }
819
820 /* The level of this tile must be equal to allowed_z. */
821 if (allowed_z < 0) {
822 /* First tile. */
823 allowed_z = flat_z;
824 } else if (allowed_z != flat_z) {
825 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
826 }
827
828 return cost;
829}
830
837static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCommandFlags flags)
838{
840 int allowed_z = -1;
841
842 for (; tile_iter != INVALID_TILE; ++tile_iter) {
843 CommandCost ret = CheckBuildableTile(tile_iter, {}, allowed_z, true);
844 if (ret.Failed()) return ret;
845 cost.AddCost(ret.GetCost());
846
847 ret = Command<Commands::LandscapeClear>::Do(flags, tile_iter);
848 if (ret.Failed()) return ret;
849 cost.AddCost(ret.GetCost());
850 }
851
852 return cost;
853}
854
861{
862 static constexpr std::array<StringID, to_underlying(StationType::End)> too_low_msgs = {
863 STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION, // Rail
864 INVALID_STRING_ID, // Airport
865 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Truck
866 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Bus
867 INVALID_STRING_ID, // Oilrig
868 STR_ERROR_BRIDGE_TOO_LOW_FOR_DOCK, // Dock
869 STR_ERROR_BRIDGE_TOO_LOW_FOR_BUOY, // Buoy
870 STR_ERROR_BRIDGE_TOO_LOW_FOR_RAIL_WAYPOINT, // RailWaypoint
871 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROAD_WAYPOINT, // RoadWaypoint
872 };
873 return too_low_msgs[to_underlying(type)];
874};
875
886static CommandCost IsStationBridgeAboveOk(TileIndex tile, std::span<const BridgeableTileInfo> bridgeable_info, StationType type, StationGfx layout, int bridge_height, StringID disallowed_msg = INVALID_STRING_ID)
887{
888 int height = layout < std::size(bridgeable_info) ? bridgeable_info[layout].height : 0;
889
890 if (height == 0) {
891 if (disallowed_msg != INVALID_STRING_ID) return CommandCost{disallowed_msg};
892 /* Get normal error message associated with clearing the tile. */
893 return Command<Commands::LandscapeClear>::Do(DoCommandFlag::Auto, tile);
894 }
895 if (GetTileMaxZ(tile) + height > bridge_height) {
896 int height_diff = (GetTileMaxZ(tile) + height - bridge_height) * TILE_HEIGHT_STEP;
898 }
899
900 return CommandCost{};
901}
902
908static std::span<const BridgeableTileInfo> GetStationBridgeableTileInfo(StationType type)
909{
910 return _station_bridgeable_info[to_underlying(type)];
911}
912
922{
923 if (!IsBridgeAbove(tile)) return CommandCost();
924
925 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
926 auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info;
927 return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
928}
929
939{
940 if (!IsBridgeAbove(tile)) return CommandCost();
941
942 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
943 auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info;
944 return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
945}
946
954{
955 if (!IsBridgeAbove(tile)) return CommandCost();
956
957 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
958 auto bridgeable_info = GetStationBridgeableTileInfo(StationType::Dock);
959 return IsStationBridgeAboveOk(tile, bridgeable_info, StationType::Dock, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
960}
961
968{
969 if (!IsBridgeAbove(tile)) return CommandCost();
970
971 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
972 auto bridgeable_info = GetStationBridgeableTileInfo(StationType::Buoy);
973 return IsStationBridgeAboveOk(tile, bridgeable_info, StationType::Buoy, 0, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
974}
975
992static 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)
993{
995 DiagDirections invalid_dirs = AxisToDiagDirs(axis);
996
997 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
998 bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
999
1000 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, false);
1001 if (ret.Failed()) return ret;
1002 cost.AddCost(ret.GetCost());
1003
1004 if (slope_cb) {
1005 /* Do slope check if requested. */
1006 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
1007 if (ret.Failed()) return ret;
1008 }
1009
1010 /* if station is set, then we have special handling to allow building on top of already existing stations.
1011 * so station points to StationID::Invalid() if we can build on any station.
1012 * Or it points to a station if we're only allowed to build on exactly that station. */
1013 if (station != nullptr && IsTileType(tile_cur, TileType::Station)) {
1014 if (!IsRailStation(tile_cur)) {
1015 return ClearTile_Station(tile_cur, DoCommandFlag::Auto); // get error message
1016 } else {
1017 StationID st = GetStationIndex(tile_cur);
1018 if (*station == StationID::Invalid()) {
1019 *station = st;
1020 } else if (*station != st) {
1021 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1022 }
1023 }
1024 } else {
1025 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
1026 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
1027 /* Don't overbuild signals. */
1028 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
1029
1030 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
1031 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
1032 TrackBits tracks = GetTrackBits(tile_cur);
1033 Track track = RemoveFirstTrack(&tracks);
1034 Track expected_track = invalid_dirs.Test(DIAGDIR_NE) ? TRACK_X : TRACK_Y;
1035
1036 /* The existing track must align with the desired station axis. */
1037 if (tracks == TRACK_BIT_NONE && track == expected_track) {
1038 /* Check for trains having a reservation for this tile. */
1039 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
1040 Train *v = GetTrainForReservation(tile_cur, track);
1041 if (v != nullptr) {
1042 affected_vehicles.push_back(v);
1043 }
1044 }
1045 ret = Command<Commands::RemoveRail>::Do(flags, tile_cur, track);
1046 if (ret.Failed()) return ret;
1047 cost.AddCost(ret.GetCost());
1048 /* With DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) CmdLandscapeClear would fail since the rail still exists */
1049 return cost;
1050 }
1051 }
1052 }
1053 ret = Command<Commands::LandscapeClear>::Do(flags, tile_cur);
1054 if (ret.Failed()) return ret;
1055 cost.AddCost(ret.GetCost());
1056 }
1057
1058 return cost;
1059}
1060
1075static 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)
1076{
1078
1079 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, false);
1080 if (ret.Failed()) return ret;
1081 cost.AddCost(ret.GetCost());
1082
1083 ret = IsRoadStationBridgeAboveOk(cur_tile, spec, station_type, is_drive_through ? GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + axis : FindFirstBit(invalid_dirs.base()));
1084 if (ret.Failed()) return ret;
1085
1086 /* If station is set, then we have special handling to allow building on top of already existing stations.
1087 * Station points to StationID::Invalid() if we can build on any station.
1088 * Or it points to a station if we're only allowed to build on exactly that station. */
1089 if (station != nullptr && IsTileType(cur_tile, TileType::Station)) {
1090 if (!IsAnyRoadStop(cur_tile)) {
1091 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1092 } else {
1093 if (station_type != GetStationType(cur_tile) ||
1094 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
1095 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1096 }
1097 /* Drive-through station in the wrong direction. */
1098 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
1099 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1100 }
1101 StationID st = GetStationIndex(cur_tile);
1102 if (*station == StationID::Invalid()) {
1103 *station = st;
1104 } else if (*station != st) {
1105 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1106 }
1107 }
1108 } else {
1109 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
1110 /* Road bits in the wrong direction. */
1111 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
1112 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
1113 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1114 switch (CountBits(rb)) {
1115 case 1:
1116 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1117
1118 case 2:
1119 if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1120 return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
1121
1122 default: // 3 or 4
1123 return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
1124 }
1125 }
1126
1127 if (build_over_road) {
1128 /* There is a road, check if we can build road+tram stop over it. */
1129 RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
1130 if (road_rt != INVALID_ROADTYPE) {
1131 Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
1132 if (road_owner == OWNER_TOWN) {
1133 if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
1134 } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
1135 ret = CheckOwnership(road_owner);
1136 if (ret.Failed()) return ret;
1137 }
1138 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
1139
1140 if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1141
1142 if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
1143 ret = CheckOwnership(road_owner);
1144 if (ret.Failed()) return ret;
1145 }
1146
1147 cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
1148 } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
1149 cost.AddCost(RoadBuildCost(rt) * 2);
1150 }
1151
1152 /* There is a tram, check if we can build road+tram stop over it. */
1153 RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
1154 if (tram_rt != INVALID_ROADTYPE) {
1155 Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
1156 if (Company::IsValidID(tram_owner) &&
1157 (!_settings_game.construction.road_stop_on_competitor_road ||
1158 /* Disallow breaking end-of-line of someone else
1159 * so trams can still reverse on this tile. */
1160 HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
1161 ret = CheckOwnership(tram_owner);
1162 if (ret.Failed()) return ret;
1163 }
1164 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
1165
1166 if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1167
1168 cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
1169 } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
1170 cost.AddCost(RoadBuildCost(rt) * 2);
1171 }
1172 } else if (rt == INVALID_ROADTYPE) {
1173 return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
1174 } else {
1175 ret = Command<Commands::LandscapeClear>::Do(flags, cur_tile);
1176 if (ret.Failed()) return ret;
1177 cost.AddCost(ret.GetCost());
1178 cost.AddCost(RoadBuildCost(rt) * 2);
1179 }
1180 }
1181
1182 return cost;
1183}
1184
1192{
1193 TileArea cur_ta = st->train_station;
1194
1195 /* determine new size of train station region.. */
1196 int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
1197 int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
1198 new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
1199 new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
1200 new_ta.tile = TileXY(x, y);
1201
1202 /* make sure the final size is not too big. */
1203 if (new_ta.w > _settings_game.station.station_spread || new_ta.h > _settings_game.station.station_spread) {
1204 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1205 }
1206
1207 return CommandCost();
1208}
1209
1210RailStationTileLayout::RailStationTileLayout(const StationSpec *spec, uint8_t platforms, uint8_t length) : platforms(platforms), length(length)
1211{
1212 if (spec == nullptr) return;
1213
1214 /* Look for a predefined layout for the required size. */
1215 auto found = spec->layouts.find(GetStationLayoutKey(platforms, length));
1216 if (found != std::end(spec->layouts)) this->layout = found->second;
1217}
1218
1219StationGfx RailStationTileLayout::Iterator::operator*() const
1220{
1221 /* Use predefined layout if it exists. Mask bit zero which will indicate axis. */
1222 if (!stl.layout.empty()) return this->stl.layout[this->position] & ~1;
1223
1224 if (this->stl.length == 1) {
1225 /* Special case for 1-long platforms, all bare platforms except one small building. */
1226 return this->position == ((this->stl.platforms - 1) / 2) ? 2 : 0;
1227 }
1228
1229 if ((this->position < this->stl.length && (this->stl.platforms % 2 == 1))) {
1230 /* Number of tracks is odd, make the first platform bare with a small building. */
1231 return this->position == ((this->stl.length - 1) / 2) ? 2 : 0;
1232 }
1233
1234 if (this->stl.length > 4 && ((this->position % this->stl.length) == 0 || (this->position % this->stl.length) == this->stl.length - 1)) {
1235 /* Station is longer than 4 tiles, place bare platforms at either end. */
1236 return 0;
1237 }
1238
1239 /* None of the above so must be north or south part of larger station. */
1240 return (((this->position / this->stl.length) % 2) == (this->stl.platforms % 2)) ? 4 : 6;
1241}
1242
1256template <class T, StringID error_message, class F>
1257CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1258{
1259 assert(*st == nullptr);
1260 bool check_surrounding = true;
1261
1262 if (existing_station != StationID::Invalid()) {
1263 if (adjacent && existing_station != station_to_join) {
1264 /* You can't build an adjacent station over the top of one that
1265 * already exists. */
1266 return CommandCost(error_message);
1267 } else {
1268 /* Extend the current station, and don't check whether it will
1269 * be near any other stations. */
1270 T *candidate = T::GetIfValid(existing_station);
1271 if (candidate != nullptr && filter(candidate)) *st = candidate;
1272 check_surrounding = (*st == nullptr);
1273 }
1274 } else {
1275 /* There's no station here. Don't check the tiles surrounding this
1276 * one if the company wanted to build an adjacent station. */
1277 if (adjacent) check_surrounding = false;
1278 }
1279
1280 if (check_surrounding) {
1281 /* Make sure there is no more than one other station around us that is owned by us. */
1282 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1283 if (ret.Failed()) return ret;
1284 }
1285
1286 /* Distant join */
1287 if (*st == nullptr && station_to_join != StationID::Invalid()) *st = T::GetIfValid(station_to_join);
1288
1289 return CommandCost();
1290}
1291
1301static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1302{
1303 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1304}
1305
1316CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1317{
1318 if (is_road) {
1319 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); });
1320 } else {
1321 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); });
1322 }
1323}
1324
1336
1348
1363static 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)
1364{
1366 bool length_price_ready = true;
1367 uint8_t tracknum = 0;
1368 int allowed_z = -1;
1369 for (TileIndex cur_tile : tile_area) {
1370 /* Clear the land below the station. */
1371 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1372 if (ret.Failed()) return ret;
1373
1374 /* Only add _price[Price::BuildStationRailLength] once for each valid plat_len. */
1375 if (tracknum == numtracks) {
1376 length_price_ready = true;
1377 tracknum = 0;
1378 } else {
1379 tracknum++;
1380 }
1381
1382 /* AddCost for new or rotated rail stations. */
1383 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1384 cost.AddCost(ret.GetCost());
1385 cost.AddCost(_price[Price::BuildStationRail]);
1386 cost.AddCost(RailBuildCost(rt));
1387
1388 if (length_price_ready) {
1390 length_price_ready = false;
1391 }
1392 }
1393 }
1394
1395 return cost;
1396}
1397
1404static StationSpec::TileFlags GetStationTileFlags(StationGfx gfx, const StationSpec *statspec)
1405{
1406 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1407 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlag::Pylons : StationSpec::TileFlags{};
1408 return statspec->tileflags[gfx];
1409}
1410
1417{
1418 const auto flags = GetStationTileFlags(GetStationGfx(tile), statspec);
1422}
1423
1438CommandCost 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)
1439{
1440 /* Does the authority allow this? */
1441 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1442 if (ret.Failed()) return ret;
1443
1444 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1445
1446 /* Check if the given station class is valid */
1447 if (static_cast<uint>(spec_class) >= StationClass::GetClassCount()) return CMD_ERROR;
1448 const StationClass *cls = StationClass::Get(spec_class);
1449 if (IsWaypointClass(*cls)) return CMD_ERROR;
1450 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1451 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1452
1453 int w_org, h_org;
1454 if (axis == AXIS_X) {
1455 w_org = plat_len;
1456 h_org = numtracks;
1457 } else {
1458 h_org = plat_len;
1459 w_org = numtracks;
1460 }
1461
1462 /* Check if the first tile and the last tile are valid */
1463 if (!IsValidTile(tile_org) || TileAddWrap(tile_org, w_org - 1, h_org - 1) == INVALID_TILE) return CMD_ERROR;
1464
1465 bool reuse = (station_to_join != NEW_STATION);
1466 if (!reuse) station_to_join = StationID::Invalid();
1467 bool distant_join = (station_to_join != StationID::Invalid());
1468
1469 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1470
1471 if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR;
1472
1473 /* these values are those that will be stored in train_tile and station_platforms */
1474 TileArea new_location(tile_org, w_org, h_org);
1475
1476 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1477 StationID est = StationID::Invalid();
1478 std::vector<Train *> affected_vehicles;
1479 /* Add construction and clearing expenses. */
1480 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1481 if (cost.Failed()) return cost;
1482
1483 Station *st = nullptr;
1484 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1485 if (ret.Failed()) return ret;
1486
1487 ret = BuildStationPart(&st, flags, reuse, new_location, STATIONNAMING_RAIL);
1488 if (ret.Failed()) return ret;
1489
1490 if (st != nullptr && st->train_station.tile != INVALID_TILE) {
1491 ret = CanExpandRailStation(st, new_location);
1492 if (ret.Failed()) return ret;
1493 }
1494
1495 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1496 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1497 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1498
1499 RailStationTileLayout stl{statspec, numtracks, plat_len};
1500 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1501 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1502 /* Don't check the layout if there's no bridge above anyway. */
1503 if (!IsBridgeAbove(tile)) continue;
1504
1505 StationGfx gfx = *it + axis;
1506 if (statspec != nullptr) {
1507 uint32_t platinfo = GetPlatformInfo(AXIS_X, gfx, plat_len, numtracks, j, i, false);
1508 /* As the station is not yet completely finished, the station does not yet exist. */
1509 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, INVALID_TILE);
1510 if (callback != CALLBACK_FAILED && callback <= UINT8_MAX) gfx = (callback & ~1) + axis;
1511 }
1512
1513 ret = IsRailStationBridgeAboveOk(tile, statspec, StationType::Rail, gfx);
1514 if (ret.Failed()) return ret;
1515 }
1516 }
1517
1518 /* Check if we can allocate a custom stationspec to this station */
1519 auto specindex = AllocateSpecToStation(statspec, st);
1520 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1521
1522 if (statspec != nullptr) {
1523 /* Perform NewStation checks */
1524
1525 /* Check if the station size is permitted */
1526 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1527 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1528
1529 /* Check if the station is buildable */
1531 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1533 }
1534 }
1535
1536 if (flags.Test(DoCommandFlag::Execute)) {
1537 st->train_station = new_location;
1538 st->AddFacility(StationFacility::Train, new_location.tile);
1539
1540 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
1541
1542 if (specindex.has_value()) AssignSpecToStation(statspec, st, *specindex);
1543 if (statspec != nullptr) {
1544 /* Include this station spec's animation trigger bitmask
1545 * in the station's cached copy. */
1546 st->cached_anim_triggers.Set(statspec->animation.triggers);
1547 }
1548
1549 Track track = AxisToTrack(axis);
1550 Company *c = Company::Get(st->owner);
1551 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1552 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1553 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1554 /* Check for trains having a reservation for this tile. */
1556 if (v != nullptr) {
1557 affected_vehicles.push_back(v);
1559 }
1560 }
1561
1562 /* Railtype can change when overbuilding. */
1563 if (IsRailStationTile(tile)) {
1564 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1566 }
1567
1568 /* Remove animation if overbuilding */
1569 DeleteAnimatedTile(tile);
1570 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1571
1572 MakeRailStation(tile, st->owner, st->index, axis, *it, rt);
1573 /* Free the spec if we overbuild something */
1574 DeallocateSpecFromStation(st, old_specindex);
1575 if (statspec == nullptr) DeleteNewGRFInspectWindow(GSF_STATIONS, tile);
1576
1577 SetCustomStationSpecIndex(tile, *specindex);
1578 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1579 SetAnimationFrame(tile, 0);
1580
1581 if (statspec != nullptr) {
1582 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1583 uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks, j, i, false);
1584
1585 /* As the station is not yet completely finished, the station does not yet exist. */
1586 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1587 if (callback != CALLBACK_FAILED) {
1588 if (callback <= UINT8_MAX) {
1589 SetStationGfx(tile, (callback & ~1) + axis);
1590 } else {
1592 }
1593 }
1594
1595 /* Trigger station animation -- after building? */
1596 TriggerStationAnimation(st, tile, StationAnimationTrigger::Built);
1597 }
1598
1599 SetRailStationTileFlags(tile, statspec);
1600
1601 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1603 }
1604 AddTrackToSignalBuffer(tile_track, track, _current_company);
1605 YapfNotifyTrackLayoutChange(tile_track, track);
1606 }
1607
1608 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1609 /* Restore reservations of trains. */
1610 RestoreTrainReservation(affected_vehicles[i]);
1611 }
1612
1613 /* Check whether we need to expand the reservation of trains already on the station. */
1614 TileArea update_reservation_area;
1615 if (axis == AXIS_X) {
1616 update_reservation_area = TileArea(tile_org, 1, numtracks);
1617 } else {
1618 update_reservation_area = TileArea(tile_org, numtracks, 1);
1619 }
1620
1621 for (TileIndex tile : update_reservation_area) {
1622 /* Don't even try to make eye candy parts reserved. */
1623 if (IsStationTileBlocked(tile)) continue;
1624
1625 DiagDirection dir = AxisToDiagDir(axis);
1626 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1627 TileIndex platform_begin = tile;
1628 TileIndex platform_end = tile;
1629
1630 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1631 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1632 platform_begin = next_tile;
1633 }
1634 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1635 platform_end = next_tile;
1636 }
1637
1638 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1639 bool reservation = false;
1640 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1641 reservation = HasStationReservation(t);
1642 }
1643
1644 if (reservation) {
1645 SetRailStationPlatformReservation(platform_begin, dir, true);
1646 }
1647 }
1648
1649 st->MarkTilesDirty(false);
1651 }
1652
1653 return cost;
1654}
1655
1656static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1657{
1658restart:
1659
1660 /* too small? */
1661 if (ta.w != 0 && ta.h != 0) {
1662 /* check the left side, x = constant, y changes */
1663 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1664 /* the left side is unused? */
1665 if (++i == ta.h) {
1666 ta.tile += TileDiffXY(1, 0);
1667 ta.w--;
1668 goto restart;
1669 }
1670 }
1671
1672 /* check the right side, x = constant, y changes */
1673 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1674 /* the right side is unused? */
1675 if (++i == ta.h) {
1676 ta.w--;
1677 goto restart;
1678 }
1679 }
1680
1681 /* check the upper side, y = constant, x changes */
1682 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1683 /* the left side is unused? */
1684 if (++i == ta.w) {
1685 ta.tile += TileDiffXY(0, 1);
1686 ta.h--;
1687 goto restart;
1688 }
1689 }
1690
1691 /* check the lower side, y = constant, x changes */
1692 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1693 /* the left side is unused? */
1694 if (++i == ta.w) {
1695 ta.h--;
1696 goto restart;
1697 }
1698 }
1699 } else {
1700 ta.Clear();
1701 }
1702
1703 return ta;
1704}
1705
1706static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1707{
1708 return st->TileBelongsToRailStation(tile);
1709}
1710
1711static void MakeRailStationAreaSmaller(BaseStation *st)
1712{
1713 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1714}
1715
1716static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1717{
1718 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1719}
1720
1721static void MakeShipStationAreaSmaller(Station *st)
1722{
1723 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1724 UpdateStationDockingTiles(st);
1725}
1726
1727static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1728{
1729 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1730}
1731
1732void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1733{
1734 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1735}
1736
1747template <class T>
1748CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
1749{
1750 /* Count of the number of tiles removed */
1751 int quantity = 0;
1753 /* Accumulator for the errors seen during clearing. If no errors happen,
1754 * and the quantity is 0 there is no station. Otherwise it will be one
1755 * of the other error that got accumulated. */
1756 CommandCost error;
1757
1758 /* Do the action for every tile into the area */
1759 for (TileIndex tile : ta) {
1760 /* Make sure the specified tile is a rail station */
1761 if (!HasStationTileRail(tile)) continue;
1762
1763 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1765 error.AddCost(std::move(ret));
1766 if (error.Failed()) continue;
1767
1768 /* Check ownership of station */
1769 T *st = T::GetByTile(tile);
1770 if (st == nullptr) continue;
1771
1773 ret = CheckOwnership(st->owner);
1774 error.AddCost(std::move(ret));
1775 if (error.Failed()) continue;
1776 }
1777
1778 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1779 quantity++;
1780
1781 if (keep_rail || IsStationTileBlocked(tile)) {
1782 /* Don't refund the 'steel' of the track when we keep the
1783 * rail, or when the tile didn't have any rail at all. */
1784 total_cost.AddCost(-_price[Price::ClearRail]);
1785 }
1786
1787 if (flags.Test(DoCommandFlag::Execute)) {
1788 /* read variables before the station tile is removed */
1789 uint specindex = GetCustomStationSpecIndex(tile);
1790 Track track = GetRailStationTrack(tile);
1791 Owner owner = GetTileOwner(tile);
1792 RailType rt = GetRailType(tile);
1793 Train *v = nullptr;
1794
1795 if (HasStationReservation(tile)) {
1796 v = GetTrainForReservation(tile, track);
1797 if (v != nullptr) FreeTrainReservation(v);
1798 }
1799
1800 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1801 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1802
1803 DoClearSquare(tile);
1804 DeleteNewGRFInspectWindow(GSF_STATIONS, tile.base());
1805 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
1806 Company::Get(owner)->infrastructure.station--;
1808
1809 st->tile_waiting_random_triggers.erase(tile);
1810 st->rect.AfterRemoveTile(st, tile);
1811 AddTrackToSignalBuffer(tile, track, owner);
1812 YapfNotifyTrackLayoutChange(tile, track);
1813
1814 DeallocateSpecFromStation(st, specindex);
1815
1816 include(affected_stations, st);
1817
1818 if (v != nullptr) RestoreTrainReservation(v);
1819 }
1820 }
1821
1822 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1823
1824 for (T *st : affected_stations) {
1825
1826 /* now we need to make the "spanned" area of the railway station smaller
1827 * if we deleted something at the edges.
1828 * we also need to adjust train_tile. */
1829 MakeRailStationAreaSmaller(st);
1830 UpdateStationSignCoord(st);
1831
1832 /* if we deleted the whole station, delete the train facility. */
1833 if (st->train_station.tile == INVALID_TILE) {
1837 MarkCatchmentTilesDirty();
1838 st->UpdateVirtCoord();
1840 }
1841 }
1842
1843 total_cost.AddCost(quantity * removal_cost);
1844 return total_cost;
1845}
1846
1856CommandCost CmdRemoveFromRailStation(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
1857{
1858 if (end == 0) end = start;
1859 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1860
1861 TileArea ta(start, end);
1862 std::vector<Station *> affected_stations;
1863
1864 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[Price::ClearStationRail], keep_rail);
1865 if (ret.Failed()) return ret;
1866
1867 /* Do all station specific functions here. */
1868 for (Station *st : affected_stations) {
1869
1871 st->MarkTilesDirty(false);
1872 MarkCatchmentTilesDirty();
1873 st->RecomputeCatchment();
1874 }
1875
1876 /* Now apply the rail cost to the number that we deleted */
1877 return ret;
1878}
1879
1889CommandCost CmdRemoveFromRailWaypoint(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
1890{
1891 if (end == 0) end = start;
1892 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1893
1894 TileArea ta(start, end);
1895 std::vector<Waypoint *> affected_stations;
1896
1897 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[Price::ClearWaypointRail], keep_rail);
1898}
1899
1900
1909template <class T>
1910CommandCost RemoveRailStation(T *st, DoCommandFlags flags, Money removal_cost)
1911{
1912 /* Current company owns the station? */
1914 CommandCost ret = CheckOwnership(st->owner);
1915 if (ret.Failed()) return ret;
1916 }
1917
1918 /* determine width and height of platforms */
1919 TileArea ta = st->train_station;
1920
1921 assert(ta.w != 0 && ta.h != 0);
1922
1924 /* clear all areas of the station */
1925 for (TileIndex tile : ta) {
1926 /* only remove tiles that are actually train station tiles */
1927 if (st->TileBelongsToRailStation(tile)) {
1928 std::vector<T*> affected_stations; // dummy
1929 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1930 if (ret.Failed()) return ret;
1931 cost.AddCost(ret.GetCost());
1932 }
1933 }
1934
1935 return cost;
1936}
1937
1944static CommandCost RemoveRailStation(TileIndex tile, DoCommandFlags flags)
1945{
1946 /* if there is flooding, remove platforms tile by tile */
1948 return Command<Commands::RemoveFromRailStation>::Do(DoCommandFlag::Execute, tile, TileIndex{}, false);
1949 }
1950
1951 Station *st = Station::GetByTile(tile);
1952 CommandCost cost = RemoveRailStation(st, flags, _price[Price::ClearStationRail]);
1953
1955
1956 return cost;
1957}
1958
1965static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlags flags)
1966{
1967 /* if there is flooding, remove waypoints tile by tile */
1969 return Command<Commands::RemoveFromRailWaypoint>::Do(DoCommandFlag::Execute, tile, TileIndex{}, false);
1970 }
1971
1973}
1974
1975
1981static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
1982{
1983 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1984
1985 if (*primary_stop == nullptr) {
1986 /* we have no roadstop of the type yet, so write a "primary stop" */
1987 return primary_stop;
1988 } else {
1989 /* there are stops already, so append to the end of the list */
1990 RoadStop *stop = *primary_stop;
1991 while (stop->next != nullptr) stop = stop->next;
1992 return &stop->next;
1993 }
1994}
1995
1996static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1997CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1998
2008static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
2009{
2010 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
2011}
2012
2027CommandCost 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)
2028{
2029 DiagDirections invalid_dirs{};
2030 if (is_drive_through) {
2031 invalid_dirs.Set(AxisToDiagDir(axis));
2032 invalid_dirs.Set(ReverseDiagDir(AxisToDiagDir(axis)));
2033 } else {
2034 invalid_dirs.Set(ddir);
2035 }
2036
2037 /* Check every tile in the area. */
2038 int allowed_z = -1;
2040 for (TileIndex cur_tile : tile_area) {
2041 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, roadstopspec, flags, invalid_dirs, is_drive_through, station_type, axis, station, rt);
2042 if (ret.Failed()) return ret;
2043
2044 bool is_preexisting_roadstop = IsTileType(cur_tile, TileType::Station) && IsAnyRoadStop(cur_tile);
2045
2046 /* Only add costs if a stop doesn't already exist in the location */
2047 if (!is_preexisting_roadstop) {
2048 cost.AddCost(ret.GetCost());
2049 cost.AddCost(unit_cost);
2050 }
2051 }
2052
2053 return cost;
2054}
2055
2072CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
2073 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
2074{
2075 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= RoadStopType::End) return CMD_ERROR;
2076 bool reuse = (station_to_join != NEW_STATION);
2077 if (!reuse) station_to_join = StationID::Invalid();
2078 bool distant_join = (station_to_join != StationID::Invalid());
2079
2080 /* Check if the given station class is valid */
2081 if (static_cast<uint>(spec_class) >= RoadStopClass::GetClassCount()) return CMD_ERROR;
2082 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
2083 if (IsWaypointClass(*cls)) return CMD_ERROR;
2084 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
2085
2086 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
2087 if (roadstopspec != nullptr) {
2088 if (stop_type == RoadStopType::Truck && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2089 if (stop_type == RoadStopType::Bus && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2090 if (!is_drive_through && roadstopspec->flags.Test(RoadStopSpecFlag::DriveThroughOnly)) return CMD_ERROR;
2091 }
2092
2093 /* Check if the requested road stop is too big */
2094 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2095 /* Check for incorrect width / length. */
2096 if (width == 0 || length == 0) return CMD_ERROR;
2097 /* Check if the first tile and the last tile are valid */
2098 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
2099
2100 TileArea roadstop_area(tile, width, length);
2101
2102 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2103
2104 /* Trams only have drive through stops */
2105 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
2106
2107 Axis axis = DiagDirToAxis(ddir);
2108
2110 if (ret.Failed()) return ret;
2111
2112 bool is_truck_stop = stop_type != RoadStopType::Bus;
2113
2114 /* Total road stop cost. */
2115 Money unit_cost;
2116 if (roadstopspec != nullptr) {
2117 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? Price::BuildStationTruck : Price::BuildStationBus);
2118 } else {
2119 unit_cost = _price[is_truck_stop ? Price::BuildStationTruck : Price::BuildStationBus];
2120 }
2121 StationID est = StationID::Invalid();
2122 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, roadstopspec, axis, ddir, &est, rt, unit_cost);
2123 if (cost.Failed()) return cost;
2124
2125 Station *st = nullptr;
2126 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2127 if (ret.Failed()) return ret;
2128
2129 /* Check if this number of road stops can be allocated. */
2130 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);
2131
2132 ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD);
2133 if (ret.Failed()) return ret;
2134
2135 /* Check if we can allocate a custom stationspec to this station */
2136 auto specindex = AllocateSpecToRoadStop(roadstopspec, st);
2137 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2138
2139 if (roadstopspec != nullptr) {
2140 /* Perform NewGRF checks */
2141
2142 /* Check if the road stop is buildable */
2143 if (roadstopspec->callback_mask.Test(RoadStopCallbackMask::Avail)) {
2144 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? StationType::Truck : StationType::Bus, 0);
2145 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2146 }
2147 }
2148
2149 if (flags.Test(DoCommandFlag::Execute)) {
2150 if (specindex.has_value()) AssignSpecToRoadStop(roadstopspec, st, *specindex);
2151 /* Check every tile in the area. */
2152 for (TileIndex cur_tile : roadstop_area) {
2153 /* Get existing road types and owners before any tile clearing */
2154 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
2155 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
2156 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
2157 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
2158
2159 if (IsTileType(cur_tile, TileType::Station) && IsStationRoadStop(cur_tile)) {
2160 RemoveRoadStop(cur_tile, flags, *specindex);
2161 }
2162
2163 if (roadstopspec != nullptr) {
2164 /* Include this road stop spec's animation trigger bitmask
2165 * in the station's cached copy. */
2166 st->cached_roadstop_anim_triggers.Set(roadstopspec->animation.triggers);
2167 }
2168
2169 RoadStop *road_stop = RoadStop::Create(cur_tile);
2170 /* Insert into linked list of RoadStops. */
2171 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2172 *currstop = road_stop;
2173
2174 if (is_truck_stop) {
2175 st->truck_station.Add(cur_tile);
2176 } else {
2177 st->bus_station.Add(cur_tile);
2178 }
2179
2180 /* Initialize an empty station. */
2181 st->AddFacility(is_truck_stop ? StationFacility::TruckStop : StationFacility::BusStop, cur_tile);
2182
2183 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
2184
2185 RoadStopType rs_type = is_truck_stop ? RoadStopType::Truck : RoadStopType::Bus;
2186 if (is_drive_through) {
2187 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2188 * bits first. */
2189 if (IsNormalRoadTile(cur_tile)) {
2190 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
2191 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
2192 }
2193
2194 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2195 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2196
2197 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == RoadStopType::Bus ? StationType::Bus : StationType::Truck), road_rt, tram_rt, axis);
2198 road_stop->MakeDriveThrough();
2199 } else {
2200 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2201 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2202 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2203 }
2206 Company::Get(st->owner)->infrastructure.station++;
2207
2208 SetCustomRoadStopSpecIndex(cur_tile, *specindex);
2209 if (roadstopspec != nullptr) {
2210 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2211 TriggerRoadStopAnimation(st, cur_tile, StationAnimationTrigger::Built);
2212 }
2213
2214 MarkTileDirtyByTile(cur_tile);
2215 }
2216
2217 if (st != nullptr) {
2219 }
2220 }
2221 return cost;
2222}
2223
2231static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2232{
2233 Station *st = Station::GetByTile(tile);
2234
2236 CommandCost ret = CheckOwnership(st->owner);
2237 if (ret.Failed()) return ret;
2238 }
2239
2240 bool is_truck = IsTruckStop(tile);
2241
2242 RoadStop **primary_stop;
2243 RoadStop *cur_stop;
2244 if (is_truck) { // truck stop
2245 primary_stop = &st->truck_stops;
2246 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Truck);
2247 } else {
2248 primary_stop = &st->bus_stops;
2249 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Bus);
2250 }
2251
2252 assert(cur_stop != nullptr);
2253
2254 /* don't do the check for drive-through road stops when company bankrupts */
2256 /* remove the 'going through road stop' status from all vehicles on that tile */
2257 if (flags.Test(DoCommandFlag::Execute)) {
2258 for (Vehicle *v : VehiclesOnTile(tile)) {
2259 if (v->type != VEH_ROAD) continue;
2260 /* Okay... we are a road vehicle on a drive through road stop.
2261 * But that road stop has just been removed, so we need to make
2262 * sure we are in a valid state... however, vehicles can also
2263 * turn on road stop tiles, so only clear the 'road stop' state
2264 * bits and only when the state was 'in road stop', otherwise
2265 * we'll end up clearing the turn around bits. */
2268 }
2269 }
2270 } else {
2272 if (ret.Failed()) return ret;
2273 }
2274
2275 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2276
2277 if (flags.Test(DoCommandFlag::Execute)) {
2278 if (*primary_stop == cur_stop) {
2279 /* removed the first stop in the list */
2280 *primary_stop = cur_stop->next;
2281 /* removed the only stop? */
2282 if (*primary_stop == nullptr) {
2285 }
2286 } else {
2287 /* tell the predecessor in the list to skip this stop */
2288 RoadStop *pred = *primary_stop;
2289 while (pred->next != cur_stop) pred = pred->next;
2290 pred->next = cur_stop->next;
2291 }
2292
2293 /* Update company infrastructure counts. */
2294 for (RoadTramType rtt : _roadtramtypes) {
2295 RoadType rt = GetRoadType(tile, rtt);
2297 }
2298
2299 Company::Get(st->owner)->infrastructure.station--;
2301
2302 uint specindex = GetCustomRoadStopSpecIndex(tile);
2303
2304 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2305
2306 if (IsDriveThroughStopTile(tile)) {
2307 /* Clears the tile for us */
2308 cur_stop->ClearDriveThrough();
2309 DeleteAnimatedTile(tile);
2310 } else {
2311 DoClearSquare(tile);
2312 }
2313
2314 delete cur_stop;
2315
2316 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2317 * this station, then look for any currently heading to the tile. */
2318 StationID station_id = st->index;
2320 [](const Vehicle *v) { return v->type == VEH_ROAD; },
2321 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2322 [station_id, tile](Vehicle *v) {
2323 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2324 v->SetDestTile(v->GetOrderStationLocation(station_id));
2325 }
2326 }
2327 );
2328
2329 st->rect.AfterRemoveTile(st, tile);
2330
2331 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
2332
2333 st->tile_waiting_random_triggers.erase(tile);
2334 st->RemoveRoadStopTileData(tile);
2335 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2336
2337 /* Update the tile area of the truck/bus stop */
2338 if (is_truck) {
2339 st->truck_station.Clear();
2340 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2341 } else {
2342 st->bus_station.Clear();
2343 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2344 }
2345 }
2346
2348 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2349}
2350
2358CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2359{
2360 Waypoint *wp = Waypoint::GetByTile(tile);
2361
2363 CommandCost ret = CheckOwnership(wp->owner);
2364 if (ret.Failed()) return ret;
2365 }
2366
2367 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2368 if (!flags.Test(DoCommandFlag::Bankrupt)) {
2370 if (ret.Failed()) return ret;
2371 }
2372
2373 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2374
2375 if (flags.Test(DoCommandFlag::Execute)) {
2376 /* Update company infrastructure counts. */
2377 for (RoadTramType rtt : _roadtramtypes) {
2378 RoadType rt = GetRoadType(tile, rtt);
2380 }
2381
2382 Company::Get(wp->owner)->infrastructure.station--;
2384
2385 uint specindex = GetCustomRoadStopSpecIndex(tile);
2386
2387 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2388
2389 DoClearSquare(tile);
2390
2391 wp->rect.AfterRemoveTile(wp, tile);
2392
2393 wp->tile_waiting_random_triggers.erase(tile);
2394 wp->RemoveRoadStopTileData(tile);
2395 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2396
2397 if (replacement_spec_index < 0) {
2398 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2399
2400 UpdateStationSignCoord(wp);
2401
2402 /* if we deleted the whole waypoint, delete the road facility. */
2406 wp->UpdateVirtCoord();
2408 }
2409 }
2410 }
2411
2413}
2414
2423static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
2424{
2426 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2427 bool had_success = false;
2428
2429 for (TileIndex cur_tile : roadstop_area) {
2430 /* Make sure the specified tile is a road stop of the correct type */
2431 if (!IsTileType(cur_tile, TileType::Station) || !IsAnyRoadStop(cur_tile) || IsRoadWaypoint(cur_tile) != road_waypoint) continue;
2432
2433 /* Save information on to-be-restored roads before the stop is removed. */
2434 RoadBits road_bits = ROAD_NONE;
2435 RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
2436 Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
2437 if (IsDriveThroughStopTile(cur_tile)) {
2438 for (RoadTramType rtt : _roadtramtypes) {
2439 road_type[rtt] = GetRoadType(cur_tile, rtt);
2440 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2441 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2442 /* If we don't want to preserve our roads then restore only roads of others. */
2443 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2444 }
2445 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2446 }
2447
2448 CommandCost ret;
2449 if (road_waypoint) {
2450 ret = RemoveRoadWaypointStop(cur_tile, flags);
2451 } else {
2452 ret = RemoveRoadStop(cur_tile, flags);
2453 }
2454 if (ret.Failed()) {
2455 last_error = std::move(ret);
2456 continue;
2457 }
2458 cost.AddCost(ret.GetCost());
2459 had_success = true;
2460
2461 /* Restore roads. */
2462 if (flags.Test(DoCommandFlag::Execute) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
2463 MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2464 road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
2465
2466 /* Update company infrastructure counts. */
2467 int count = CountBits(road_bits);
2468 UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
2469 UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
2470 }
2471 }
2472
2473 return had_success ? cost : last_error;
2474}
2475
2486CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2487{
2488 if (stop_type >= RoadStopType::End) return CMD_ERROR;
2489 /* Check for incorrect width / height. */
2490 if (width == 0 || height == 0) return CMD_ERROR;
2491 /* Check if the first tile and the last tile are valid */
2492 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2493 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2494 if (remove_road && flags.Test(DoCommandFlag::Bankrupt)) return CMD_ERROR;
2495
2496 TileArea roadstop_area(tile, width, height);
2497
2498 return RemoveGenericRoadStop(flags, roadstop_area, false, remove_road);
2499}
2500
2509{
2510 if (end == 0) end = start;
2511 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2512
2513 TileArea roadstop_area(start, end);
2514
2515 return RemoveGenericRoadStop(flags, roadstop_area, true, false);
2516}
2517
2526uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2527{
2528 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2529 * So no need to go any further*/
2530 if (as->noise_level < 2) return as->noise_level;
2531
2532 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2533 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2534 * Basically, it says that the less tolerant a town is, the bigger the distance before
2535 * an actual decrease can be granted */
2536 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2537
2538 /* now, we want to have the distance segmented using the distance judged bareable by town
2539 * This will give us the coefficient of reduction the distance provides. */
2540 uint noise_reduction = distance / town_tolerance_distance;
2541
2542 /* If the noise reduction equals the airport noise itself, don't give it for free.
2543 * Otherwise, simply reduce the airport's level. */
2544 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2545}
2546
2557Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2558{
2559 assert(Town::GetNumItems() > 0);
2560
2561 Town *nearest = nullptr;
2562
2563 auto width = as->size_x;
2564 auto height = as->size_y;
2565 if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
2566
2567 uint perimeter_min_x = TileX(tile);
2568 uint perimeter_min_y = TileY(tile);
2569 uint perimeter_max_x = perimeter_min_x + width - 1;
2570 uint perimeter_max_y = perimeter_min_y + height - 1;
2571
2572 mindist = UINT_MAX - 1; // prevent overflow
2573
2574 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2575 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2576 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2577 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) {
2578 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2579 if (t == nullptr) continue;
2580
2581 uint dist = DistanceManhattan(t->xy, cur_tile);
2582 if (dist == mindist && t->index < nearest->index) nearest = t;
2583 if (dist < mindist) {
2584 nearest = t;
2585 mindist = dist;
2586 }
2587 }
2588 }
2589
2590 return nearest;
2591}
2592
2600static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2601{
2603}
2604
2605
2608{
2609 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2610
2611 for (const Station *st : Station::Iterate()) {
2612 if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
2613 uint dist;
2614 Town *nearest = AirportGetNearestTown(st, dist);
2615 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2616 }
2617 }
2618}
2619
2630CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2631{
2632 bool reuse = (station_to_join != NEW_STATION);
2633 if (!reuse) station_to_join = StationID::Invalid();
2634 bool distant_join = (station_to_join != StationID::Invalid());
2635
2636 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2637
2638 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2639
2641 if (ret.Failed()) return ret;
2642
2643 /* Check if a valid, buildable airport was chosen for construction */
2644 const AirportSpec *as = AirportSpec::Get(airport_type);
2645 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2646 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2647
2648 Direction rotation = as->layouts[layout].rotation;
2649 int w = as->size_x;
2650 int h = as->size_y;
2651 if (rotation == DIR_E || rotation == DIR_W) std::swap(w, h);
2652 TileArea airport_area = TileArea(tile, w, h);
2653
2654 if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
2655 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2656 }
2657
2658 AirportTileTableIterator tile_iter(as->layouts[layout].tiles, tile);
2659 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2660 if (cost.Failed()) return cost;
2661
2662 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2663 uint dist;
2664 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2665 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2666
2667 /* Check if local auth would allow a new airport */
2668 StringID authority_refuse_message = STR_NULL;
2669 Town *authority_refuse_town = nullptr;
2670
2671 if (_settings_game.economy.station_noise_level) {
2672 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2673 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2674 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2675 authority_refuse_town = nearest;
2676 }
2677 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2678 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2679 uint num = 0;
2680 for (const Station *st : Station::Iterate()) {
2681 if (st->town == t && st->facilities.Test(StationFacility::Airport) && st->airport.type != AT_OILRIG) num++;
2682 }
2683 if (num >= 2) {
2684 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2685 authority_refuse_town = t;
2686 }
2687 }
2688
2689 if (authority_refuse_message != STR_NULL) {
2690 return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index);
2691 }
2692
2693 Station *st = nullptr;
2694 ret = FindJoiningStation(StationID::Invalid(), station_to_join, allow_adjacent, airport_area, &st);
2695 if (ret.Failed()) return ret;
2696
2697 /* Distant join */
2698 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2699
2700 ret = BuildStationPart(&st, flags, reuse, airport_area, GetAirport(airport_type)->flags.Test(AirportFTAClass::Flag::Airplanes) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT);
2701 if (ret.Failed()) return ret;
2702
2703 if (st != nullptr && st->airport.tile != INVALID_TILE) {
2704 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2705 }
2706
2707 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2708 cost.AddCost(_price[Price::BuildStationAirport]);
2709 }
2710
2711 if (flags.Test(DoCommandFlag::Execute)) {
2712 /* Always add the noise, so there will be no need to recalculate when option toggles */
2713 nearest->noise_reached += newnoise_level;
2714
2716 st->airport.type = airport_type;
2717 st->airport.layout = layout;
2718 st->airport.blocks = {};
2719 st->airport.rotation = rotation;
2720
2721 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
2722
2723 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2724 Tile t(iter);
2725 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WaterClass::Invalid);
2726 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2727 st->airport.Add(iter);
2728
2729 if (AirportTileSpec::Get(GetTranslatedAirportTileID(iter.GetStationGfx()))->animation.status != AnimationStatus::NoAnimation) AddAnimatedTile(t);
2730 }
2731
2732 /* Only call the animation trigger after all tiles have been built */
2733 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2734 TriggerAirportTileAnimation(st, iter, AirportAnimationTrigger::Built);
2735 }
2736
2738
2739 Company::Get(st->owner)->infrastructure.airport++;
2740
2742 InvalidateWindowData(WC_STATION_VIEW, st->index, -1);
2743
2744 if (_settings_game.economy.station_noise_level) {
2745 SetWindowDirty(WC_TOWN_VIEW, nearest->index);
2746 }
2747 }
2748
2749 return cost;
2750}
2751
2758static CommandCost RemoveAirport(TileIndex tile, DoCommandFlags flags)
2759{
2760 Station *st = Station::GetByTile(tile);
2761
2763 CommandCost ret = CheckOwnership(st->owner);
2764 if (ret.Failed()) return ret;
2765 }
2766
2767 tile = st->airport.tile;
2768
2770
2771 for (const Aircraft *a : Aircraft::Iterate()) {
2772 if (!a->IsNormalAircraft()) continue;
2773 if (a->targetairport == st->index && a->state != FLYING) {
2774 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2775 }
2776 }
2777
2778 if (flags.Test(DoCommandFlag::Execute)) {
2779 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2780 TileIndex tile_cur = st->airport.GetHangarTile(i);
2781 OrderBackup::Reset(tile_cur, false);
2783 }
2784
2785 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2786 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2787 * need of recalculation */
2788 uint dist;
2789 Town *nearest = AirportGetNearestTown(st, dist);
2791
2792 if (_settings_game.economy.station_noise_level) {
2793 SetWindowDirty(WC_TOWN_VIEW, nearest->index);
2794 }
2795 }
2796
2797 for (TileIndex tile_cur : st->airport) {
2798 if (!st->TileBelongsToAirport(tile_cur)) continue;
2799
2800 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2801 if (ret.Failed()) return ret;
2802
2803 cost.AddCost(_price[Price::ClearStationAirport]);
2804
2805 if (flags.Test(DoCommandFlag::Execute)) {
2806 DoClearSquare(tile_cur);
2807 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur.base());
2808 }
2809 }
2810
2811 if (flags.Test(DoCommandFlag::Execute)) {
2812 /* Clear the persistent storage. */
2813 delete st->airport.psa;
2814
2815 st->rect.AfterRemoveRect(st, st->airport);
2816
2817 st->airport.Clear();
2820
2821 InvalidateWindowData(WC_STATION_VIEW, st->index, -1);
2822
2823 Company::Get(st->owner)->infrastructure.airport--;
2824
2826
2827 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
2828 }
2829
2830 return cost;
2831}
2832
2839CommandCost CmdOpenCloseAirport(DoCommandFlags flags, StationID station_id)
2840{
2841 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2842 Station *st = Station::Get(station_id);
2843
2845
2846 CommandCost ret = CheckOwnership(st->owner);
2847 if (ret.Failed()) return ret;
2848
2849 if (flags.Test(DoCommandFlag::Execute)) {
2852 }
2853 return CommandCost();
2854}
2855
2862bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2863{
2864 for (const OrderList *orderlist : OrderList::Iterate()) {
2865 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2866 assert(v != nullptr);
2867 if ((v->owner == company) != include_company) continue;
2868
2869 for (const Order &order : orderlist->GetOrders()) {
2870 if (order.GetDestination() == station && (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT))) {
2871 return true;
2872 }
2873 }
2874 }
2875 return false;
2876}
2877
2878static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
2879 {-1, 0},
2880 { 0, 0},
2881 { 0, 0},
2882 { 0, -1}
2883};
2884static const uint8_t _dock_w_chk[4] = { 2, 1, 2, 1 };
2885static const uint8_t _dock_h_chk[4] = { 1, 2, 1, 2 };
2886
2895CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
2896{
2897 bool reuse = (station_to_join != NEW_STATION);
2898 if (!reuse) station_to_join = StationID::Invalid();
2899 bool distant_join = (station_to_join != StationID::Invalid());
2900
2901 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2902
2904 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2905 direction = ReverseDiagDir(direction);
2906
2907 /* Docks cannot be placed on rapids */
2908 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2909
2911 if (ret.Failed()) return ret;
2912
2913 ret = IsDockBridgeAboveOk(tile, to_underlying(direction));
2914 if (ret.Failed()) return ret;
2915
2917 ret = Command<Commands::LandscapeClear>::Do(flags, tile);
2918 if (ret.Failed()) return ret;
2919 cost.AddCost(ret.GetCost());
2920
2921 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2922
2923 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2924 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2925 }
2926
2928 if (ret.Failed()) return ret;
2929
2930 /* Get the water class of the water tile before it is cleared.*/
2931 WaterClass wc = GetWaterClass(tile_cur);
2932
2933 bool add_cost = !IsWaterTile(tile_cur);
2934 ret = Command<Commands::LandscapeClear>::Do(flags, tile_cur);
2935 if (ret.Failed()) return ret;
2936 if (add_cost) cost.AddCost(ret.GetCost());
2937
2938 tile_cur += TileOffsByDiagDir(direction);
2939 if (!IsTileType(tile_cur, TileType::Water) || !IsTileFlat(tile_cur)) {
2940 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2941 }
2942
2943 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2944 _dock_w_chk[direction], _dock_h_chk[direction]);
2945
2946 /* middle */
2947 Station *st = nullptr;
2948 ret = FindJoiningStation(StationID::Invalid(), station_to_join, adjacent, dock_area, &st);
2949 if (ret.Failed()) return ret;
2950
2951 /* Distant join */
2952 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2953
2954 ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
2955 if (ret.Failed()) return ret;
2956
2957 if (flags.Test(DoCommandFlag::Execute)) {
2958 st->ship_station.Add(tile);
2959 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2960 st->ship_station.Add(flat_tile);
2962
2963 st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
2964
2965 /* If the water part of the dock is on a canal, update infrastructure counts.
2966 * This is needed as we've cleared that tile before.
2967 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2968 * See: MakeWaterKeepingClass() */
2969 if (wc == WaterClass::Canal && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WaterClass::Canal && IsTileOwner(flat_tile, _current_company))) {
2970 Company::Get(st->owner)->infrastructure.water++;
2971 }
2972 Company::Get(st->owner)->infrastructure.station += 2;
2973
2974 MakeDock(tile, st->owner, st->index, direction, wc);
2975 UpdateStationDockingTiles(st);
2976
2978 }
2979
2980 return cost;
2981}
2982
2983void RemoveDockingTile(TileIndex t)
2984{
2985 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2986 TileIndex tile = t + TileOffsByDiagDir(d);
2987 if (!IsValidTile(tile)) continue;
2988
2989 if (IsTileType(tile, TileType::Station)) {
2990 Station *st = Station::GetByTile(tile);
2991 if (st != nullptr) UpdateStationDockingTiles(st);
2992 } else if (IsTileType(tile, TileType::Industry)) {
2994 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
2995 }
2996 }
2997}
2998
3005{
3006 assert(IsValidTile(tile));
3007
3008 /* Clear and maybe re-set docking tile */
3009 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
3010 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
3011 if (!IsValidTile(docking_tile)) continue;
3012
3013 if (IsPossibleDockingTile(docking_tile)) {
3014 SetDockingTile(docking_tile, false);
3015 CheckForDockingTile(docking_tile);
3016 }
3017 }
3018}
3019
3026{
3027 assert(IsDockTile(t));
3028
3029 StationGfx gfx = GetStationGfx(t);
3030 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
3031
3032 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
3033 TileIndex tile = t + TileOffsByDiagDir(d);
3034 if (!IsValidTile(tile)) continue;
3035 if (!IsDockTile(tile)) continue;
3036 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
3037 }
3038
3039 return INVALID_TILE;
3040}
3041
3048static CommandCost RemoveDock(TileIndex tile, DoCommandFlags flags)
3049{
3050 Station *st = Station::GetByTile(tile);
3051 CommandCost ret = CheckOwnership(st->owner);
3052 if (ret.Failed()) return ret;
3053
3054 if (!IsDockTile(tile)) return CMD_ERROR;
3055
3056 TileIndex tile1 = FindDockLandPart(tile);
3057 if (tile1 == INVALID_TILE) return CMD_ERROR;
3058 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
3059
3060 ret = EnsureNoVehicleOnGround(tile1);
3061 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
3062 if (ret.Failed()) return ret;
3063
3064 if (flags.Test(DoCommandFlag::Execute)) {
3065 DoClearSquare(tile1);
3066 MarkTileDirtyByTile(tile1);
3067 MakeWaterKeepingClass(tile2, st->owner);
3068
3069 st->rect.AfterRemoveTile(st, tile1);
3070 st->rect.AfterRemoveTile(st, tile2);
3071
3072 MakeShipStationAreaSmaller(st);
3073 if (st->ship_station.tile == INVALID_TILE) {
3074 st->ship_station.Clear();
3075 st->docking_station.Clear();
3078 }
3079
3080 Company::Get(st->owner)->infrastructure.station -= 2;
3081
3083
3086
3087 for (Ship *s : Ship::Iterate()) {
3088 /* Find all ships going to our dock. */
3089 if (s->current_order.GetDestination() != st->index) {
3090 continue;
3091 }
3092
3093 /* Find ships that are marked as "loading" but are no longer on a
3094 * docking tile. Force them to leave the station (as they were loading
3095 * on the removed dock). */
3096 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
3097 s->LeaveStation();
3098 }
3099
3100 /* If we no longer have a dock, mark the order as invalid and send
3101 * the ship to the next order (or, if there is none, make it
3102 * wander the world). */
3103 if (s->current_order.IsType(OT_GOTO_STATION) && !st->facilities.Test(StationFacility::Dock)) {
3104 s->SetDestTile(s->GetOrderStationLocation(st->index));
3105 }
3106 }
3107 }
3108
3110}
3111
3119{
3120 const auto &layouts = _station_display_datas[to_underlying(st)];
3121 if (gfx >= layouts.size()) gfx &= 1;
3122 return layouts.data() + gfx;
3123}
3124
3134bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3135{
3136 bool snow_desert;
3137 switch (*ground) {
3138 case SPR_RAIL_TRACK_X:
3139 case SPR_MONO_TRACK_X:
3140 case SPR_MGLV_TRACK_X:
3141 snow_desert = false;
3142 *overlay_offset = RTO_X;
3143 break;
3144
3145 case SPR_RAIL_TRACK_Y:
3146 case SPR_MONO_TRACK_Y:
3147 case SPR_MGLV_TRACK_Y:
3148 snow_desert = false;
3149 *overlay_offset = RTO_Y;
3150 break;
3151
3152 case SPR_RAIL_TRACK_X_SNOW:
3153 case SPR_MONO_TRACK_X_SNOW:
3154 case SPR_MGLV_TRACK_X_SNOW:
3155 snow_desert = true;
3156 *overlay_offset = RTO_X;
3157 break;
3158
3159 case SPR_RAIL_TRACK_Y_SNOW:
3160 case SPR_MONO_TRACK_Y_SNOW:
3161 case SPR_MGLV_TRACK_Y_SNOW:
3162 snow_desert = true;
3163 *overlay_offset = RTO_Y;
3164 break;
3165
3166 default:
3167 return false;
3168 }
3169
3170 if (ti != nullptr) {
3171 /* Decide snow/desert from tile */
3172 switch (_settings_game.game_creation.landscape) {
3174 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3175 break;
3176
3178 snow_desert = GetTropicZone(ti->tile) == TROPICZONE_DESERT;
3179 break;
3180
3181 default:
3182 break;
3183 }
3184 }
3185
3186 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3187 return true;
3188}
3189
3196static BridgePillarFlags GetStationBlockedPillars(std::span<const BridgeableTileInfo> bridgeable_info, uint8_t layout)
3197{
3198 if (layout < std::size(bridgeable_info)) return bridgeable_info[layout].disallowed_pillars;
3199 return BRIDGEPILLARFLAGS_ALL;
3200}
3201
3211{
3212 if (statspec == nullptr || !statspec->flags.Test(StationSpecFlag::CustomFoundations)) return false;
3213
3214 /* Station has custom foundations.
3215 * Check whether the foundation continues beyond the tile's upper sides. */
3216 uint edge_info = 0;
3217 auto [slope, z] = GetFoundationPixelSlope(ti->tile);
3218 if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
3219 if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
3220
3221 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, gfx, edge_info);
3222 if (image == 0) return false;
3223
3225 /* Station provides extended foundations. */
3226 static constexpr uint8_t foundation_parts[] = {
3227 UINT8_MAX, UINT8_MAX, UINT8_MAX, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3228 UINT8_MAX, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3229 UINT8_MAX, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3230 7, 8, 9, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3231 };
3232 assert(ti->tileh < std::size(foundation_parts));
3233 if (foundation_parts[ti->tileh] == UINT8_MAX) return false;
3234
3235 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3236 } else {
3237 /* Draw simple foundations, built up from 8 possible foundation sprites.
3238 * Each set bit represents one of the eight composite sprites to be drawn.
3239 * 'Invalid' entries will not drawn but are included for completeness. */
3240 static constexpr uint8_t composite_foundation_parts[] = {
3241 0b0000'0000, 0b1101'0001, 0b1110'0100, 0b1110'0000, // Invalid, Invalid, Invalid, SLOPE_SW
3242 0b1100'1010, 0b1100'1001, 0b1100'0100, 0b1100'0000, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3243 0b1101'0010, 0b1001'0001, 0b1110'0100, 0b1010'0000, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3244 0b0100'1010, 0b0000'1001, 0b0100'0100, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3245 };
3246 assert(ti->tileh < std::size(composite_foundation_parts));
3247
3248 uint8_t parts = composite_foundation_parts[ti->tileh];
3249
3250 /* If foundations continue beyond the tile's upper sides then mask out the last two pieces. */
3251 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3252 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3253
3254 /* We always have to draw at least one sprite, so if we have no parts to draw fall back to default foundation. */
3255 if (parts == 0) return false;
3256
3258 for (uint i : SetBitIterator(parts)) {
3259 AddSortableSpriteToDraw(image + i, PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3260 }
3262 }
3263
3264 OffsetGroundSprite(0, -static_cast<int>(TILE_HEIGHT));
3266
3267 return true;
3268}
3269
3270static void DrawTile_Station(TileInfo *ti)
3271{
3272 const NewGRFSpriteLayout *layout = nullptr;
3273 SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_layout and t
3274 DrawTileSpriteSpan tmp_layout;
3275 const DrawTileSprites *t = nullptr;
3276 int32_t total_offset;
3277 const RailTypeInfo *rti = nullptr;
3278 uint32_t relocation = 0;
3279 uint32_t ground_relocation = 0;
3280 BaseStation *st = nullptr;
3281 const StationSpec *statspec = nullptr;
3282 uint tile_layout = 0;
3283 auto bridgeable_info = GetStationBridgeableTileInfo(GetStationType(ti->tile));
3284
3285 if (HasStationRail(ti->tile)) {
3286 rti = GetRailTypeInfo(GetRailType(ti->tile));
3287 total_offset = rti->GetRailtypeSpriteOffset();
3288
3289 if (IsCustomStationSpecIndex(ti->tile)) {
3290 /* look for customization */
3291 st = BaseStation::GetByTile(ti->tile);
3292 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3293
3294 if (statspec != nullptr) {
3295 tile_layout = GetStationGfx(ti->tile);
3296
3298 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3299 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
3300 }
3301
3302 /* Ensure the chosen tile layout is valid for this custom station */
3303 if (!statspec->renderdata.empty()) {
3304 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3305 if (!layout->NeedsPreprocessing()) {
3306 t = layout;
3307 layout = nullptr;
3308 }
3309 }
3310 }
3311 }
3312 if (statspec != nullptr) bridgeable_info = statspec->bridgeable_info;
3313 } else {
3314 total_offset = 0;
3315 }
3316
3317 StationGfx gfx = GetStationGfx(ti->tile);
3318 if (IsAirport(ti->tile)) {
3319 gfx = GetAirportGfx(ti->tile);
3320 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3321 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3322 if (ats->grf_prop.HasSpriteGroups() && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3323 return;
3324 }
3325 /* No sprite group (or no valid one) found, meaning no graphics associated.
3326 * Use the substitute one instead */
3327 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3328 gfx = ats->grf_prop.subst_id;
3329 }
3330 switch (gfx) {
3331 case APT_RADAR_GRASS_FENCE_SW:
3332 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3333 break;
3334 case APT_GRASS_FENCE_NE_FLAG:
3335 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3336 break;
3337 case APT_RADAR_FENCE_SW:
3338 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3339 break;
3340 case APT_RADAR_FENCE_NE:
3341 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3342 break;
3343 case APT_GRASS_FENCE_NE_FLAG_2:
3344 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3345 break;
3346 }
3347 }
3348
3349 Owner owner = GetTileOwner(ti->tile);
3350
3351 PaletteID palette;
3352 if (Company::IsValidID(owner)) {
3353 palette = GetCompanyPalette(owner);
3354 } else {
3355 /* Some stations are not owner by a company, namely oil rigs */
3356 palette = PALETTE_TO_GREY;
3357 }
3358
3359 if (layout == nullptr && t == nullptr) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3360
3361 /* don't show foundation for docks */
3362 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3363 if (!DrawCustomStationFoundations(statspec, st, ti, tile_layout)) {
3365 }
3366 }
3367
3368 bool draw_ground = false;
3369
3370 if (IsBuoy(ti->tile)) {
3371 DrawWaterClassGround(ti);
3372 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
3373 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3374 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3375 if (ti->tileh == SLOPE_FLAT) {
3376 DrawWaterClassGround(ti);
3377 } else {
3378 assert(IsDock(ti->tile));
3379 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3380 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WaterClass::Invalid;
3381 if (wc == WaterClass::Sea) {
3382 DrawShoreTile(ti->tileh);
3383 } else {
3384 DrawClearLandTile(ti, 3);
3385 }
3386 }
3387 } else if (IsRoadWaypointTile(ti->tile)) {
3389 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3390 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3391 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3392 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3393 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3394 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3395
3396 if (ti->tileh != SLOPE_FLAT) {
3398 }
3399
3400 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3401 } else {
3402 if (layout != nullptr) {
3403 /* Sprite layout which needs preprocessing */
3404 bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround);
3405 processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3406 GetCustomStationRelocation(processor, statspec, st, ti->tile);
3407 tmp_layout = processor.GetLayout();
3408 t = &tmp_layout;
3409 total_offset = 0;
3410 } else if (statspec != nullptr) {
3411 /* Simple sprite layout */
3412 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3414 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3415 }
3416 ground_relocation += rti->fallback_railtype;
3417 }
3418
3419 draw_ground = true;
3420 }
3421
3422 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3423 SpriteID image = t->ground.sprite;
3424 PaletteID pal = t->ground.pal;
3425 RailTrackOffset overlay_offset;
3426 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3427 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
3428 DrawGroundSprite(image, PAL_NONE);
3429 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3430
3431 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3432 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
3433 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3434 }
3435 } else {
3436 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3437 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3438 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3439
3440 /* PBS debugging, draw reserved tracks darker */
3441 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3443 }
3444 }
3445 }
3446
3448
3449 if (IsAnyRoadStop(ti->tile)) {
3450 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3451 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3452 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3453 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3454
3455 StationGfx view = GetStationGfx(ti->tile);
3456 StationType type = GetStationType(ti->tile);
3457
3458 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3459 RoadStopDrawModes stop_draw_mode{};
3460 if (stopspec != nullptr) {
3461 stop_draw_mode = stopspec->draw_mode;
3462 st = BaseStation::GetByTile(ti->tile);
3463 std::array<int32_t, 1> regs100;
3464 auto result = GetRoadStopLayout(ti, stopspec, st, type, view, regs100);
3465 if (result.has_value()) {
3466 if (stopspec->flags.Test(RoadStopSpecFlag::DrawModeRegister)) {
3467 stop_draw_mode = static_cast<RoadStopDrawModes>(regs100[0]);
3468 }
3469 if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) {
3470 draw_ground = true;
3471 }
3472 processor = std::move(*result);
3473 tmp_layout = processor.GetLayout();
3474 t = &tmp_layout;
3475 }
3476 }
3477
3478 /* Draw ground sprite */
3479 if (draw_ground) {
3480 SpriteID image = t->ground.sprite;
3481 PaletteID pal = t->ground.pal;
3482 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3483 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3484 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3485 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3486 }
3487 }
3488
3489 if (IsDriveThroughStopTile(ti->tile)) {
3490 if (type != StationType::RoadWaypoint && (stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Overlay))) {
3491 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
3492 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3493 }
3494 } else {
3495 /* Non-drivethrough road stops are only valid for roads. */
3496 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3497
3498 if ((stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Road)) && road_rti->UsesOverlay()) {
3499 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
3500 DrawGroundSprite(ground + view, PAL_NONE);
3501 }
3502 }
3503 if (stopspec != nullptr) bridgeable_info = stopspec->bridgeable_info;
3504
3505 if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
3506 /* Draw road, tram catenary */
3507 DrawRoadCatenary(ti);
3508 }
3509 }
3510
3511 if (IsRailWaypoint(ti->tile)) {
3512 /* Don't offset the waypoint graphics; they're always the same. */
3513 total_offset = 0;
3514 }
3515
3516 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
3517 DrawBridgeMiddle(ti, GetStationBlockedPillars(bridgeable_info, GetStationGfx(ti->tile)));
3518}
3519
3520void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3521{
3522 int32_t total_offset = 0;
3524 const DrawTileSprites *t = GetStationTileLayout(st, image);
3525 const RailTypeInfo *railtype_info = nullptr;
3526
3527 if (railtype != INVALID_RAILTYPE) {
3528 railtype_info = GetRailTypeInfo(railtype);
3529 total_offset = railtype_info->GetRailtypeSpriteOffset();
3530 }
3531
3532 SpriteID img = t->ground.sprite;
3533 RailTrackOffset overlay_offset;
3534 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3535 SpriteID ground = GetCustomRailSprite(railtype_info, INVALID_TILE, RTSG_GROUND);
3536 DrawSprite(img, PAL_NONE, x, y);
3537 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3538 } else {
3539 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3540 }
3541
3542 if (roadtype != INVALID_ROADTYPE) {
3543 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3544 if (image >= 4) {
3545 /* Drive-through stop */
3546 uint sprite_offset = 5 - image;
3547
3548 /* Road underlay takes precedence over tram */
3549 if (roadtype_info->UsesOverlay()) {
3550 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_GROUND);
3551 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3552
3553 SpriteID overlay = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_OVERLAY);
3554 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3555 } else if (RoadTypeIsTram(roadtype)) {
3556 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3557 }
3558 } else {
3559 /* Bay stop */
3560 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3561 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_ROADSTOP);
3562 DrawSprite(ground + image, PAL_NONE, x, y);
3563 }
3564 }
3565 }
3566
3567 /* Default waypoint has no railtype specific sprites */
3568 DrawRailTileSeqInGUI(x, y, t, (st == StationType::RailWaypoint || st == StationType::RoadWaypoint) ? 0 : total_offset, 0, pal);
3569}
3570
3571static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)
3572{
3573 return GetTileMaxPixelZ(tile);
3574}
3575
3576static Foundation GetFoundation_Station(TileIndex, Slope tileh)
3577{
3578 return FlatteningFoundation(tileh);
3579}
3580
3581static void FillTileDescRoadStop(TileIndex tile, TileDesc &td)
3582{
3583 RoadType road_rt = GetRoadTypeRoad(tile);
3584 RoadType tram_rt = GetRoadTypeTram(tile);
3585 Owner road_owner = INVALID_OWNER;
3586 Owner tram_owner = INVALID_OWNER;
3587 if (road_rt != INVALID_ROADTYPE) {
3588 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3589 td.roadtype = rti->strings.name;
3590 td.road_speed = rti->max_speed / 2;
3591 road_owner = GetRoadOwner(tile, RTT_ROAD);
3592 }
3593
3594 if (tram_rt != INVALID_ROADTYPE) {
3595 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3596 td.tramtype = rti->strings.name;
3597 td.tram_speed = rti->max_speed / 2;
3598 tram_owner = GetRoadOwner(tile, RTT_TRAM);
3599 }
3600
3601 if (IsDriveThroughStopTile(tile)) {
3602 /* Is there a mix of owners? */
3603 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
3604 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
3605 uint i = 1;
3606 if (road_owner != INVALID_OWNER) {
3607 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3608 td.owner[i] = road_owner;
3609 i++;
3610 }
3611 if (tram_owner != INVALID_OWNER) {
3612 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3613 td.owner[i] = tram_owner;
3614 }
3615 }
3616 }
3617}
3618
3619void FillTileDescRailStation(TileIndex tile, TileDesc &td)
3620{
3621 const StationSpec *spec = GetStationSpec(tile);
3622
3623 if (spec != nullptr) {
3625 td.station_name = spec->name;
3626
3627 if (spec->grf_prop.HasGrfFile()) {
3628 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3629 td.grf = gc->GetName();
3630 }
3631 }
3632
3633 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3634 td.rail_speed = rti->max_speed;
3635 td.railtype = rti->strings.name;
3636}
3637
3638void FillTileDescAirport(TileIndex tile, TileDesc &td)
3639{
3640 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3642 td.airport_name = as->name;
3643
3644 const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
3645 td.airport_tile_name = ats->name;
3646
3647 if (as->grf_prop.HasGrfFile()) {
3648 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3649 td.grf = gc->GetName();
3650 } else if (ats->grf_prop.HasGrfFile()) {
3651 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3652 td.grf = gc->GetName();
3653 }
3654}
3655
3656static void GetTileDesc_Station(TileIndex tile, TileDesc &td)
3657{
3658 td.owner[0] = GetTileOwner(tile);
3660
3661 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3662 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3663 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3664
3665 StringID str;
3666 switch (GetStationType(tile)) {
3667 default: NOT_REACHED();
3668 case StationType::Rail: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3670 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3671 break;
3672 case StationType::Truck: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3673 case StationType::Bus: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3674 case StationType::Oilrig: {
3675 const Industry *i = Station::GetByTile(tile)->industry;
3676 const IndustrySpec *is = GetIndustrySpec(i->type);
3677 td.owner[0] = i->owner;
3678 str = is->name;
3679 if (is->grf_prop.HasGrfFile()) td.grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3680 break;
3681 }
3682 case StationType::Dock: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3683 case StationType::Buoy: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3684 case StationType::RailWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3685 case StationType::RoadWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3686 }
3687 td.str = str;
3688}
3689
3690
3691static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
3692{
3693 TrackBits trackbits = TRACK_BIT_NONE;
3694
3695 switch (mode) {
3696 case TRANSPORT_RAIL:
3697 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3698 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
3699 }
3700 break;
3701
3702 case TRANSPORT_WATER:
3703 /* buoy is coded as a station, it is always on open water */
3704 if (IsBuoy(tile)) {
3705 trackbits = TRACK_BIT_ALL;
3706 /* remove tracks that connect NE map edge */
3707 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
3708 /* remove tracks that connect NW map edge */
3709 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
3710 }
3711 break;
3712
3713 case TRANSPORT_ROAD:
3714 if (IsAnyRoadStop(tile)) {
3715 RoadTramType rtt = (RoadTramType)sub_mode;
3716 if (!HasTileRoadType(tile, rtt)) break;
3717
3718 if (IsBayRoadStopTile(tile)) {
3719 DiagDirection dir = GetBayRoadStopDir(tile);
3720 if (side != INVALID_DIAGDIR && dir != side) break;
3721 trackbits = DiagDirToDiagTrackBits(dir);
3722 } else {
3723 Axis axis = GetDriveThroughStopAxis(tile);
3724 if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
3725 trackbits = AxisToTrackBits(axis);
3726 }
3727 }
3728 break;
3729
3730 default:
3731 break;
3732 }
3733
3735}
3736
3737
3738static void TileLoop_Station(TileIndex tile)
3739{
3740 auto *st = BaseStation::GetByTile(tile);
3741 switch (GetStationType(tile)) {
3743 TriggerAirportTileAnimation(Station::From(st), tile, AirportAnimationTrigger::TileLoop);
3744 break;
3745
3746 case StationType::Rail:
3748 TriggerStationAnimation(st, tile, StationAnimationTrigger::TileLoop);
3749 break;
3750
3751 case StationType::Dock:
3752 if (!IsTileFlat(tile)) break; // only handle water part
3753 [[fallthrough]];
3754
3755 case StationType::Oilrig: //(station part)
3756 case StationType::Buoy:
3757 TileLoop_Water(tile);
3758 break;
3759
3760 case StationType::Truck:
3761 case StationType::Bus:
3762 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3763 break;
3764
3768 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3770 MarkTileDirtyByTile(tile);
3771 }
3772 break;
3773
3777 MarkTileDirtyByTile(tile);
3778 }
3779 break;
3780
3781 default: break;
3782 }
3783
3784 HouseZone new_zone = HouseZone::TownEdge;
3785 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3786 if (t != nullptr) {
3787 new_zone = GetTownRadiusGroup(t, tile);
3788 }
3789
3790 /* Adjust road ground type depending on 'new_zone' */
3792 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3793
3794 if (new_rs != cur_rs) {
3795 SetRoadWaypointRoadside(tile, cur_rs == Roadside::Barren ? new_rs : Roadside::Barren);
3796 MarkTileDirtyByTile(tile);
3797 }
3798
3799 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3800 break;
3801 }
3802
3803 default: break;
3804 }
3805}
3806
3807
3808static void AnimateTile_Station(TileIndex tile)
3809{
3810 if (HasStationRail(tile)) {
3811 AnimateStationTile(tile);
3812 return;
3813 }
3814
3815 if (IsAirport(tile)) {
3816 AnimateAirportTile(tile);
3817 return;
3818 }
3819
3820 if (IsAnyRoadStopTile(tile)) {
3821 AnimateRoadStopTile(tile);
3822 return;
3823 }
3824}
3825
3826
3827static bool ClickTile_Station(TileIndex tile)
3828{
3829 const BaseStation *bst = BaseStation::GetByTile(tile);
3830
3833 } else if (IsHangar(tile)) {
3834 const Station *st = Station::From(bst);
3836 } else {
3837 ShowStationViewWindow(bst->index);
3838 }
3839 return true;
3840}
3841
3842static VehicleEnterTileStates VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
3843{
3844 if (v->type == VEH_TRAIN) {
3845 StationID station_id = GetStationIndex(tile);
3846 if (!v->current_order.ShouldStopAtStation(v, station_id)) return {};
3847 if (!IsRailStation(tile) || !v->IsFrontEngine()) return {};
3848
3849 int station_ahead;
3850 int station_length;
3851 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3852
3853 /* Stop whenever that amount of station ahead + the distance from the
3854 * begin of the platform to the stop location is longer than the length
3855 * of the platform. Station ahead 'includes' the current tile where the
3856 * vehicle is on, so we need to subtract that. */
3857 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return {};
3858
3860
3861 x &= 0xF;
3862 y &= 0xF;
3863
3864 if (DiagDirToAxis(dir) != AXIS_X) std::swap(x, y);
3865 if (y == TILE_SIZE / 2) {
3866 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
3867 stop &= TILE_SIZE - 1;
3868
3869 if (x == stop) {
3870 return VehicleEnterTileState::EnteredStation; // enter station
3871 } else if (x < stop) {
3873 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3874 if (spd < v->cur_speed) v->cur_speed = spd;
3875 }
3876 }
3877 } else if (v->type == VEH_ROAD) {
3879 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
3880 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3881 /* Attempt to allocate a parking bay in a road stop */
3882 if (RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv)) return {};
3884 }
3885 }
3886 }
3887
3888 return {};
3889}
3890
3896{
3897 /* Collect cargoes accepted since the last big tick. */
3898 CargoTypes cargoes = 0;
3899 for (CargoType cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
3900 if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) SetBit(cargoes, cargo_type);
3901 }
3902
3903 /* Anything to do? */
3904 if (cargoes == 0) return;
3905
3906 /* Loop over all houses in the catchment. */
3908 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3909 if (IsTileType(tile, TileType::House)) {
3911 }
3912 }
3913}
3914
3922{
3923 if (!st->IsInUse()) {
3924 if (++st->delete_ctr >= 8) delete st;
3925 return false;
3926 }
3927
3928 if (Station::IsExpected(st)) {
3930
3931 for (GoodsEntry &ge : Station::From(st)->goods) {
3933 }
3934 }
3935
3936
3938
3939 return true;
3940}
3941
3942static inline void byte_inc_sat(uint8_t *p)
3943{
3944 uint8_t b = *p + 1;
3945 if (b != 0) *p = b;
3946}
3947
3954static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3955{
3956 /* If truncating also punish the source stations' ratings to
3957 * decrease the flow of incoming cargo. */
3958
3959 if (!ge->HasData()) return;
3960
3961 StationCargoAmountMap waiting_per_source;
3962 ge->GetData().cargo.Truncate(amount, &waiting_per_source);
3963 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3964 Station *source_station = Station::GetIfValid(i->first);
3965 if (source_station == nullptr) continue;
3966
3967 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3968 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3969 }
3970}
3971
3977{
3978 bool waiting_changed = false;
3979
3980 byte_inc_sat(&st->time_since_load);
3981 byte_inc_sat(&st->time_since_unload);
3982
3983 for (const CargoSpec *cs : CargoSpec::Iterate()) {
3984 GoodsEntry *ge = &st->goods[cs->Index()];
3985
3986 /* The station might not currently be moving this cargo. */
3987 if (!ge->HasRating()) {
3988 /* Slowly increase the rating back to its original level in the case we
3989 * didn't deliver cargo yet to this station. This happens when a bribe
3990 * failed while you didn't moved that cargo yet to a station. */
3991 if (ge->rating < INITIAL_STATION_RATING) ge->rating++;
3992
3993 /* Nothing else to do with this cargo. */
3994 continue;
3995 }
3996
3997 byte_inc_sat(&ge->time_since_pickup);
3998
3999 /* If this cargo hasn't been picked up in a long time, get rid of it. */
4000 if (ge->time_since_pickup == 255 && _settings_game.order.selectgoods) {
4002 ge->last_speed = 0;
4003 TruncateCargo(cs, ge);
4004 waiting_changed = true;
4005 continue;
4006 }
4007
4008 bool skip = false;
4009 int rating = 0;
4010 uint waiting = ge->AvailableCount();
4011
4012 /* num_dests is at least 1 if there is any cargo as
4013 * StationID::Invalid() is also a destination.
4014 */
4015 uint num_dests = ge->HasData() ? static_cast<uint>(ge->GetData().cargo.Packets()->MapSize()) : 0;
4016
4017 /* Average amount of cargo per next hop, but prefer solitary stations
4018 * with only one or two next hops. They are allowed to have more
4019 * cargo waiting per next hop.
4020 * With manual cargo distribution waiting_avg = waiting / 2 as then
4021 * StationID::Invalid() is the only destination.
4022 */
4023 uint waiting_avg = waiting / (num_dests + 1);
4024
4025 if (_cheats.station_rating.value) {
4026 ge->rating = rating = MAX_STATION_RATING;
4027 skip = true;
4028 } else if (cs->callback_mask.Test(CargoCallbackMask::StationRatingCalc)) {
4029 /* Perform custom station rating. If it succeeds the speed, days in transit and
4030 * waiting cargo ratings must not be executed. */
4031
4032 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
4033 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
4034
4035 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
4037 | (ClampTo<uint8_t>(last_speed) << 24);
4038 /* Convert to the 'old' vehicle types */
4039 uint32_t var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
4040 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
4041 if (callback != CALLBACK_FAILED) {
4042 skip = true;
4043 rating = GB(callback, 0, 14);
4044
4045 /* Simulate a 15 bit signed value */
4046 if (HasBit(callback, 14)) rating -= 0x4000;
4047 }
4048 }
4049
4050 if (!skip) {
4051 int b = ge->last_speed - 85;
4052 if (b >= 0) rating += b >> 2;
4053
4054 uint8_t waittime = ge->time_since_pickup;
4055 if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
4056 if (waittime <= 21) rating += 25;
4057 if (waittime <= 12) rating += 25;
4058 if (waittime <= 6) rating += 45;
4059 if (waittime <= 3) rating += 35;
4060
4061 rating -= 90;
4062 if (ge->max_waiting_cargo <= 1500) rating += 55;
4063 if (ge->max_waiting_cargo <= 1000) rating += 35;
4064 if (ge->max_waiting_cargo <= 600) rating += 10;
4065 if (ge->max_waiting_cargo <= 300) rating += 20;
4066 if (ge->max_waiting_cargo <= 100) rating += 10;
4067 }
4068
4069 if (Company::IsValidID(st->owner) && st->town->statues.Test(st->owner)) rating += 26;
4070
4071 uint8_t age = ge->last_age;
4072 if (age < 3) rating += 10;
4073 if (age < 2) rating += 10;
4074 if (age < 1) rating += 13;
4075
4076 {
4077 int or_ = ge->rating; // old rating
4078
4079 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
4080 ge->rating = rating = ClampTo<uint8_t>(or_ + Clamp(rating - or_, -2, 2));
4081
4082 /* if rating is <= 64 and more than 100 items waiting on average per destination,
4083 * remove some random amount of goods from the station */
4084 if (rating <= 64 && waiting_avg >= 100) {
4085 int dec = Random() & 0x1F;
4086 if (waiting_avg < 200) dec &= 7;
4087 waiting -= (dec + 1) * num_dests;
4088 waiting_changed = true;
4089 }
4090
4091 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
4092 if (rating <= 127 && waiting != 0) {
4093 uint32_t r = Random();
4094 if (rating <= (int)GB(r, 0, 7)) {
4095 /* Need to have int, otherwise it will just overflow etc. */
4096 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) + 1) * num_dests), 0);
4097 waiting_changed = true;
4098 }
4099 }
4100
4101 /* At some point we really must cap the cargo. Previously this
4102 * was a strict 4095, but now we'll have a less strict, but
4103 * increasingly aggressive truncation of the amount of cargo. */
4104 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
4105 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
4106 static const uint MAX_WAITING_CARGO = 1 << 15;
4107
4108 if (waiting > WAITING_CARGO_THRESHOLD) {
4109 uint difference = waiting - WAITING_CARGO_THRESHOLD;
4110 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
4111
4112 waiting = std::min(waiting, MAX_WAITING_CARGO);
4113 waiting_changed = true;
4114 }
4115
4116 /* We can't truncate cargo that's already reserved for loading.
4117 * Thus StoredCount() here. */
4118 if (waiting_changed && waiting < ge->AvailableCount()) {
4119 /* Feed back the exact own waiting cargo at this station for the
4120 * next rating calculation. */
4121 ge->max_waiting_cargo = 0;
4122
4123 TruncateCargo(cs, ge, ge->AvailableCount() - waiting);
4124 } else {
4125 /* If the average number per next hop is low, be more forgiving. */
4126 ge->max_waiting_cargo = waiting_avg;
4127 }
4128 }
4129 }
4130
4131 StationID index = st->index;
4132 if (waiting_changed) {
4133 SetWindowDirty(WC_STATION_VIEW, index); // update whole window
4134 } else {
4135 SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
4136 }
4137}
4138
4147void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
4148{
4149 GoodsEntry &ge = st->goods[cargo];
4150
4151 /* Reroute cargo in station. */
4152 if (ge.HasData()) ge.GetData().cargo.Reroute(UINT_MAX, &ge.GetData().cargo, avoid, avoid2, &ge);
4153
4154 /* Reroute cargo staged to be transferred. */
4155 for (Vehicle *v : st->loading_vehicles) {
4156 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4157 if (u->cargo_type != cargo) continue;
4158 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4159 }
4160 }
4161}
4162
4172{
4173 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
4174 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL);
4175 GoodsEntry &ge = from->goods[cargo];
4177 if (lg == nullptr) continue;
4178 std::vector<NodeID> to_remove{};
4179 for (Edge &edge : (*lg)[ge.node].edges) {
4180 Station *to = Station::Get((*lg)[edge.dest_node].station);
4181 assert(to->goods[cargo].node == edge.dest_node);
4182 assert(TimerGameEconomy::date >= edge.LastUpdate());
4183 auto timeout = TimerGameEconomy::Date(LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3));
4184 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4185 bool updated = false;
4186
4187 if (auto_distributed) {
4188 /* Have all vehicles refresh their next hops before deciding to
4189 * remove the node. */
4190 std::vector<Vehicle *> vehicles;
4191 for (const OrderList *l : OrderList::Iterate()) {
4192 bool found_from = false;
4193 bool found_to = false;
4194 for (const Order &order : l->GetOrders()) {
4195 if (!order.IsType(OT_GOTO_STATION) && !order.IsType(OT_IMPLICIT)) continue;
4196 if (order.GetDestination() == from->index) {
4197 found_from = true;
4198 if (found_to) break;
4199 } else if (order.GetDestination() == to->index) {
4200 found_to = true;
4201 if (found_from) break;
4202 }
4203 }
4204 if (!found_to || !found_from) continue;
4205 vehicles.push_back(l->GetFirstSharedVehicle());
4206 }
4207
4208 auto iter = vehicles.begin();
4209 while (iter != vehicles.end()) {
4210 Vehicle *v = *iter;
4211 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4213 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4214 }
4215 if (edge.LastUpdate() == TimerGameEconomy::date) {
4216 updated = true;
4217 break;
4218 }
4219
4220 Vehicle *next_shared = v->NextShared();
4221 if (next_shared) {
4222 *iter = next_shared;
4223 ++iter;
4224 } else {
4225 iter = vehicles.erase(iter);
4226 }
4227
4228 if (iter == vehicles.end()) iter = vehicles.begin();
4229 }
4230 }
4231
4232 if (!updated) {
4233 /* If it's still considered dead remove it. */
4234 to_remove.emplace_back(to->goods[cargo].node);
4235 if (ge.HasData()) ge.GetData().flows.DeleteFlows(to->index);
4236 RerouteCargo(from, cargo, to->index, from->index);
4237 }
4238 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4239 edge.Restrict();
4240 if (ge.HasData()) ge.GetData().flows.RestrictFlows(to->index);
4241 RerouteCargo(from, cargo, to->index, from->index);
4242 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4243 edge.Release();
4244 }
4245 }
4246 /* Remove dead edges. */
4247 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4248
4249 assert(TimerGameEconomy::date >= lg->LastCompression());
4251 lg->Compress();
4252 }
4253 }
4254}
4255
4266void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
4267{
4268 GoodsEntry &ge1 = st->goods[cargo];
4269 Station *st2 = Station::Get(next_station_id);
4270 GoodsEntry &ge2 = st2->goods[cargo];
4271 LinkGraph *lg = nullptr;
4272 if (ge1.link_graph == LinkGraphID::Invalid()) {
4273 if (ge2.link_graph == LinkGraphID::Invalid()) {
4275 lg = LinkGraph::Create(cargo);
4277 ge2.link_graph = lg->index;
4278 ge2.node = lg->AddNode(st2);
4279 } else {
4280 Debug(misc, 0, "Can't allocate link graph");
4281 }
4282 } else {
4283 lg = LinkGraph::Get(ge2.link_graph);
4284 }
4285 if (lg) {
4286 ge1.link_graph = lg->index;
4287 ge1.node = lg->AddNode(st);
4288 }
4289 } else if (ge2.link_graph == LinkGraphID::Invalid()) {
4290 lg = LinkGraph::Get(ge1.link_graph);
4291 ge2.link_graph = lg->index;
4292 ge2.node = lg->AddNode(st2);
4293 } else {
4294 lg = LinkGraph::Get(ge1.link_graph);
4295 if (ge1.link_graph != ge2.link_graph) {
4297 if (lg->Size() < lg2->Size()) {
4298 LinkGraphSchedule::instance.Dequeue(lg);
4299 lg2->Merge(lg); // Updates GoodsEntries of lg
4300 lg = lg2;
4301 } else {
4302 LinkGraphSchedule::instance.Dequeue(lg2);
4303 lg->Merge(lg2); // Updates GoodsEntries of lg2
4304 }
4305 }
4306 }
4307 if (lg != nullptr) {
4308 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, modes);
4309 }
4310}
4311
4319void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4320{
4321 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4322 if (v->refit_cap > 0) {
4323 /* The cargo count can indeed be higher than the refit_cap if
4324 * wagons have been auto-replaced and subsequently auto-
4325 * refitted to a higher capacity. The cargo gets redistributed
4326 * among the wagons in that case.
4327 * As usage is not such an important figure anyway we just
4328 * ignore the additional cargo then.*/
4329 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4330 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EdgeUpdateMode::Increase);
4331 }
4332 }
4333}
4334
4335/* called for every station each tick */
4336static void StationHandleSmallTick(BaseStation *st)
4337{
4338 if (st->facilities.Test(StationFacility::Waypoint) || !st->IsInUse()) return;
4339
4340 uint8_t b = st->delete_ctr + 1;
4341 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4342 st->delete_ctr = b;
4343
4344 if (b == 0) UpdateStationRating(Station::From(st));
4345}
4346
4347void OnTick_Station()
4348{
4349 if (_game_mode == GM_EDITOR) return;
4350
4351 for (BaseStation *st : BaseStation::Iterate()) {
4352 StationHandleSmallTick(st);
4353
4354 /* Clean up the link graph about once a week. */
4357 };
4358
4359 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4361 /* Stop processing this station if it was deleted */
4362 if (!StationHandleBigTick(st)) continue;
4363 }
4364
4365 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4367 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4368 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4369 if (Station::IsExpected(st)) TriggerAirportAnimation(Station::From(st), AirportAnimationTrigger::AcceptanceTick);
4370 }
4371 }
4372}
4373
4375static const IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::Trigger::Month, TimerGameEconomy::Priority::Station}, [](auto)
4376{
4377 for (Station *st : Station::Iterate()) {
4378 for (GoodsEntry &ge : st->goods) {
4381 }
4382 }
4383});
4384
4393void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4394{
4395 ForAllStationsRadius(tile, radius, [&](Station *st) {
4396 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4397 for (GoodsEntry &ge : st->goods) {
4398 if (ge.status.Any()) {
4399 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4400 }
4401 }
4402 }
4403 });
4404}
4405
4406static uint UpdateStationWaiting(Station *st, CargoType cargo, uint amount, Source source)
4407{
4408 /* We can't allocate a CargoPacket? Then don't do anything
4409 * at all; i.e. just discard the incoming cargo. */
4410 if (!CargoPacket::CanAllocateItem()) return 0;
4411
4412 GoodsEntry &ge = st->goods[cargo];
4413 amount += ge.amount_fract;
4414 ge.amount_fract = GB(amount, 0, 8);
4415
4416 amount >>= 8;
4417 /* No new "real" cargo item yet. */
4418 if (amount == 0) return 0;
4419
4420 StationID next = ge.GetVia(st->index);
4421 ge.GetOrCreateData().cargo.Append(CargoPacket::Create(st->index, amount, source), next);
4422 LinkGraph *lg = nullptr;
4423 if (ge.link_graph == LinkGraphID::Invalid()) {
4424 if (LinkGraph::CanAllocateItem()) {
4425 lg = LinkGraph::Create(cargo);
4427 ge.link_graph = lg->index;
4428 ge.node = lg->AddNode(st);
4429 } else {
4430 Debug(misc, 0, "Can't allocate link graph");
4431 }
4432 } else {
4433 lg = LinkGraph::Get(ge.link_graph);
4434 }
4435 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4436
4437 if (!ge.HasRating()) {
4440 }
4441
4443 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4444 TriggerAirportAnimation(st, AirportAnimationTrigger::NewCargo, cargo);
4446 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4447
4448
4449 SetWindowDirty(WC_STATION_VIEW, st->index);
4450 st->MarkTilesDirty(true);
4451 return amount;
4452}
4453
4454static bool IsUniqueStationName(const std::string &name)
4455{
4456 for (const Station *st : Station::Iterate()) {
4457 if (!st->name.empty() && st->name == name) return false;
4458 }
4459
4460 return true;
4461}
4462
4470CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
4471{
4472 Station *st = Station::GetIfValid(station_id);
4473 if (st == nullptr) return CMD_ERROR;
4474
4475 CommandCost ret = CheckOwnership(st->owner);
4476 if (ret.Failed()) return ret;
4477
4478 bool reset = text.empty();
4479
4480 if (!reset) {
4482 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4483 }
4484
4485 if (flags.Test(DoCommandFlag::Execute)) {
4486 st->cached_name.clear();
4487 if (reset) {
4488 st->name.clear();
4489 } else {
4490 st->name = text;
4491 }
4492
4493 st->UpdateVirtCoord();
4495 }
4496
4497 return CommandCost();
4498}
4499
4507std::tuple<CommandCost, StationID> CmdMoveStationName(DoCommandFlags flags, StationID station_id, TileIndex tile)
4508{
4509 Station *st = Station::GetIfValid(station_id);
4510 if (st == nullptr) return { CMD_ERROR, StationID::Invalid() };
4511
4512 if (st->owner != OWNER_NONE) {
4513 CommandCost ret = CheckOwnership(st->owner);
4514 if (ret.Failed()) return { ret, StationID::Invalid() };
4515 }
4516
4517 const StationRect *r = &st->rect;
4518 if (!r->PtInExtendedRect(TileX(tile), TileY(tile))) {
4519 return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() };
4520 }
4521
4522 bool other_station = false;
4523 /* Check if the tile is the base tile of another station */
4524 ForAllStationsRadius(tile, 0, [&](BaseStation *s) {
4525 if (s != nullptr) {
4526 if (s != st && s->xy == tile) other_station = true;
4527 }
4528 });
4529 if (other_station) return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() };
4530
4531 if (flags.Test(DoCommandFlag::Execute)) {
4532 st->MoveSign(tile);
4533
4534 st->UpdateVirtCoord();
4535 }
4536 return { CommandCost(), station_id };
4537}
4538
4544void CcMoveStationName(Commands, const CommandCost &result, StationID station_id)
4545 {
4546 if (result.Failed()) return;
4547
4549 Station *st = Station::Get(station_id);
4550 SetViewportStationRect(st, false);
4551 }
4552
4553static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4554{
4555 for (Station *st : nearby) {
4556 if (st->TileIsInCatchment(tile)) stations.insert(st);
4557 }
4558}
4559
4565{
4566 if (this->tile != INVALID_TILE) {
4567 if (IsTileType(this->tile, TileType::House)) {
4568 /* Town nearby stations need to be filtered per tile. */
4569 assert(this->w == 1 && this->h == 1);
4570 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4571 } else {
4572 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4573 this->stations.insert(st);
4574 return true;
4575 });
4576 }
4577 this->tile = INVALID_TILE;
4578 }
4579 return this->stations;
4580}
4581
4582
4583static bool CanMoveGoodsToStation(const Station *st, CargoType cargo)
4584{
4585 /* Is the station reserved exclusively for somebody else? */
4586 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4587
4588 /* Lowest possible rating, better not to give cargo anymore. */
4589 if (st->goods[cargo].rating == 0) return false;
4590
4591 /* Selectively servicing stations, and not this one. */
4592 if (_settings_game.order.selectgoods && !st->goods[cargo].HasVehicleEverTriedLoading()) return false;
4593
4595 /* Passengers are never served by just a truck stop. */
4596 if (st->facilities == StationFacility::TruckStop) return false;
4597 } else {
4598 /* Non-passengers are never served by just a bus stop. */
4599 if (st->facilities == StationFacility::BusStop) return false;
4600 }
4601 return true;
4602}
4603
4604uint MoveGoodsToStation(CargoType cargo, uint amount, Source source, const StationList &all_stations, Owner exclusivity)
4605{
4606 /* Return if nothing to do. Also the rounding below fails for 0. */
4607 if (all_stations.empty()) return 0;
4608 if (amount == 0) return 0;
4609
4610 Station *first_station = nullptr;
4611 typedef std::pair<Station *, uint> StationInfo;
4612 std::vector<StationInfo> used_stations;
4613
4614 for (Station *st : all_stations) {
4615 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4616 if (!CanMoveGoodsToStation(st, cargo)) continue;
4617
4618 /* Avoid allocating a vector if there is only one station to significantly
4619 * improve performance in this common case. */
4620 if (first_station == nullptr) {
4621 first_station = st;
4622 continue;
4623 }
4624 if (used_stations.empty()) {
4625 used_stations.reserve(2);
4626 used_stations.emplace_back(first_station, 0);
4627 }
4628 used_stations.emplace_back(st, 0);
4629 }
4630
4631 /* no stations around at all? */
4632 if (first_station == nullptr) return 0;
4633
4634 if (used_stations.empty()) {
4635 /* only one station around */
4636 amount *= first_station->goods[cargo].rating + 1;
4637 return UpdateStationWaiting(first_station, cargo, amount, source);
4638 }
4639
4640 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_best = {}; // best rating for each company, including OWNER_NONE
4641 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_sum = {}; // sum of ratings for each company
4642 uint best_rating = 0;
4643 uint best_sum = 0; // sum of best ratings for each company
4644
4645 for (auto &p : used_stations) {
4646 auto owner = p.first->owner;
4647 auto rating = p.first->goods[cargo].rating;
4648 if (rating > company_best[owner]) {
4649 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4650 company_best[owner] = rating;
4651 if (rating > best_rating) best_rating = rating;
4652 }
4653 company_sum[owner] += rating;
4654 }
4655
4656 /* From now we'll calculate with fractional cargo amounts.
4657 * First determine how much cargo we really have. */
4658 amount *= best_rating + 1;
4659
4660 uint moving = 0;
4661 for (auto &p : used_stations) {
4662 Owner owner = p.first->owner;
4663 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4664 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4665 p.second = amount * company_best[owner] * p.first->goods[cargo].rating / best_sum / company_sum[owner];
4666 moving += p.second;
4667 }
4668
4669 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4670 if (amount > moving) {
4671 std::stable_sort(used_stations.begin(), used_stations.end(), [cargo](const StationInfo &a, const StationInfo &b) {
4672 return b.first->goods[cargo].rating < a.first->goods[cargo].rating;
4673 });
4674
4675 assert(amount - moving <= used_stations.size());
4676 for (uint i = 0; i < amount - moving; i++) {
4677 used_stations[i].second++;
4678 }
4679 }
4680
4681 uint moved = 0;
4682 for (auto &p : used_stations) {
4683 moved += UpdateStationWaiting(p.first, cargo, p.second, source);
4684 }
4685
4686 return moved;
4687}
4688
4689void UpdateStationDockingTiles(Station *st)
4690{
4691 st->docking_station.Clear();
4692
4693 /* For neutral stations, start with the industry area instead of dock area */
4694 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4695
4696 if (area->tile == INVALID_TILE) return;
4697
4698 int x = TileX(area->tile);
4699 int y = TileY(area->tile);
4700
4701 /* Expand the area by a tile on each side while
4702 * making sure that we remain inside the map. */
4703 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4704 int x1 = std::max<int>(x - 1, 0);
4705
4706 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4707 int y1 = std::max<int>(y - 1, 0);
4708
4709 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4710 for (TileIndex tile : ta) {
4711 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4712 }
4713}
4714
4715void BuildOilRig(TileIndex tile)
4716{
4717 if (!Station::CanAllocateItem()) {
4718 Debug(misc, 0, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4719 return;
4720 }
4721
4722 Station *st = Station::Create(tile);
4723 _station_kdtree.Insert(st->index);
4724 st->town = ClosestTownFromTile(tile, UINT_MAX);
4725
4726 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
4727
4728 assert(IsTileType(tile, TileType::Industry));
4729 /* Mark industry as associated both ways */
4730 st->industry = Industry::GetByTile(tile);
4731 st->industry->neutral_station = st;
4732 DeleteAnimatedTile(tile);
4733 MakeOilrig(tile, st->index, GetWaterClass(tile));
4734
4735 st->owner = OWNER_NONE;
4736 st->airport.type = AT_OILRIG;
4737 st->airport.rotation = DIR_N;
4738 st->airport.Add(tile);
4739 st->ship_station.Add(tile);
4742 UpdateStationDockingTiles(st);
4743
4744 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
4745
4746 st->UpdateVirtCoord();
4747
4748 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4749 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4750 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4752 StationList nearby = std::move(st->industry->stations_near);
4753 st->industry->stations_near.clear();
4754 for (Station *near : nearby) {
4755 near->RecomputeCatchment(true);
4756 UpdateStationAcceptance(near, true);
4757 }
4758 }
4759
4760 st->RecomputeCatchment();
4761 UpdateStationAcceptance(st, false);
4762}
4763
4764void DeleteOilRig(TileIndex tile)
4765{
4766 Station *st = Station::GetByTile(tile);
4767
4768 MakeWaterKeepingClass(tile, OWNER_NONE);
4769
4770 /* The oil rig station is not supposed to be shared with anything else */
4771 [[maybe_unused]] static constexpr StationFacilities expected_facility{StationFacility::Airport, StationFacility::Dock};
4772 assert(st->facilities == expected_facility && st->airport.type == AT_OILRIG);
4773 if (st->industry != nullptr && st->industry->neutral_station == st) {
4774 /* Don't leave dangling neutral station pointer */
4775 st->industry->neutral_station = nullptr;
4776 }
4777 delete st;
4778}
4779
4780static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4781{
4782
4783 if (IsAnyRoadStopTile(tile)) {
4784 for (RoadTramType rtt : _roadtramtypes) {
4785 /* Update all roadtypes, no matter if they are present */
4786 if (GetRoadOwner(tile, rtt) == old_owner) {
4787 RoadType rt = GetRoadType(tile, rtt);
4788 if (rt != INVALID_ROADTYPE) {
4789 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4790 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4791 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4792 }
4793 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4794 }
4795 }
4796 }
4797
4798 if (!IsTileOwner(tile, old_owner)) return;
4799
4800 if (new_owner != INVALID_OWNER) {
4801 /* Update company infrastructure counts. Only do it here
4802 * if the new owner is valid as otherwise the clear
4803 * command will do it for us. No need to dirty windows
4804 * here, we'll redraw the whole screen anyway.*/
4805 Company *old_company = Company::Get(old_owner);
4806 Company *new_company = Company::Get(new_owner);
4807
4808 /* Update counts for underlying infrastructure. */
4809 switch (GetStationType(tile)) {
4810 case StationType::Rail:
4812 if (!IsStationTileBlocked(tile)) {
4813 old_company->infrastructure.rail[GetRailType(tile)]--;
4814 new_company->infrastructure.rail[GetRailType(tile)]++;
4815 }
4816 break;
4817
4818 case StationType::Bus:
4819 case StationType::Truck:
4821 /* Road stops were already handled above. */
4822 break;
4823
4824 case StationType::Buoy:
4825 case StationType::Dock:
4826 if (GetWaterClass(tile) == WaterClass::Canal) {
4827 old_company->infrastructure.water--;
4828 new_company->infrastructure.water++;
4829 }
4830 break;
4831
4832 default:
4833 break;
4834 }
4835
4836 /* Update station tile count. */
4837 if (!IsBuoy(tile) && !IsAirport(tile)) {
4838 old_company->infrastructure.station--;
4839 new_company->infrastructure.station++;
4840 }
4841
4842 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4843 SetTileOwner(tile, new_owner);
4845 } else {
4846 if (IsDriveThroughStopTile(tile)) {
4847 /* Remove the drive-through road stop */
4848 if (IsRoadWaypoint(tile)) {
4849 Command<Commands::RemoveFromRoadWaypoint>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile, tile);
4850 } else {
4851 Command<Commands::RemoveRoadStop>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile, 1, 1, (GetStationType(tile) == StationType::Truck) ? RoadStopType::Truck : RoadStopType::Bus, false);
4852 }
4853 assert(IsTileType(tile, TileType::Road));
4854 /* Change owner of tile and all roadtypes */
4855 ChangeTileOwner(tile, old_owner, new_owner);
4856 } else {
4857 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
4858 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4859 * Update owner of buoy if it was not removed (was in orders).
4860 * Do not update when owned by OWNER_WATER (sea and rivers). */
4861 if ((IsTileType(tile, TileType::Water) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4862 }
4863 }
4864}
4865
4874static CommandCost CanRemoveRoadWithStop(TileIndex tile, DoCommandFlags flags)
4875{
4876 /* Water flooding can always clear road stops. */
4877 if (_current_company == OWNER_WATER) return CommandCost();
4878
4879 CommandCost ret;
4880
4881 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4882 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
4883 if (tram_owner != OWNER_NONE) {
4884 ret = CheckOwnership(tram_owner);
4885 if (ret.Failed()) return ret;
4886 }
4887 }
4888
4889 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4890 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
4891 if (road_owner == OWNER_TOWN) {
4892 ret = CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags);
4893 if (ret.Failed()) return ret;
4894 } else if (road_owner != OWNER_NONE) {
4895 ret = CheckOwnership(road_owner);
4896 if (ret.Failed()) return ret;
4897 }
4898 }
4899
4900 return CommandCost();
4901}
4902
4909CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags)
4910{
4911 if (flags.Test(DoCommandFlag::Auto)) {
4912 switch (GetStationType(tile)) {
4913 default: break;
4914 case StationType::Rail: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4915 case StationType::RailWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4916 case StationType::Airport: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4917 case StationType::Truck: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4918 case StationType::Bus: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4919 case StationType::RoadWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4920 case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4921 case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4923 return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG);
4924 }
4925 }
4926
4927 switch (GetStationType(tile)) {
4928 case StationType::Rail: return RemoveRailStation(tile, flags);
4929 case StationType::RailWaypoint: return RemoveRailWaypoint(tile, flags);
4930 case StationType::Airport: return RemoveAirport(tile, flags);
4931 case StationType::Truck: [[fallthrough]];
4932 case StationType::Bus:
4933 if (IsDriveThroughStopTile(tile)) {
4934 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4935 if (remove_road.Failed()) return remove_road;
4936 }
4937 return RemoveRoadStop(tile, flags);
4939 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4940 if (remove_road.Failed()) return remove_road;
4941 return RemoveRoadWaypointStop(tile, flags);
4942 }
4943 case StationType::Buoy: return RemoveBuoy(tile, flags);
4944 case StationType::Dock: return RemoveDock(tile, flags);
4945 default: break;
4946 }
4947
4948 return CMD_ERROR;
4949}
4950
4951static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
4952{
4954 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4955 * TTDP does not call it.
4956 */
4957 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4958 switch (GetStationType(tile)) {
4960 case StationType::Rail: {
4961 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4962 return CommandCost(EXPENSES_CONSTRUCTION, _price[Price::BuildFoundation]);
4963 }
4964
4966 return CommandCost(EXPENSES_CONSTRUCTION, _price[Price::BuildFoundation]);
4967
4968 case StationType::Truck:
4969 case StationType::Bus:
4971 if (IsDriveThroughStopTile(tile)) {
4972 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4973 } else {
4974 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4975 }
4976 return CommandCost(EXPENSES_CONSTRUCTION, _price[Price::BuildFoundation]);
4977 }
4978
4979 default: break;
4980 }
4981 }
4982 }
4983 return Command<Commands::LandscapeClear>::Do(flags, tile);
4984}
4985
4991uint FlowStat::GetShare(StationID st) const
4992{
4993 uint32_t prev = 0;
4994 for (const auto &it : this->shares) {
4995 if (it.second == st) {
4996 return it.first - prev;
4997 } else {
4998 prev = it.first;
4999 }
5000 }
5001 return 0;
5002}
5003
5010StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const
5011{
5012 if (this->unrestricted == 0) return StationID::Invalid();
5013 assert(!this->shares.empty());
5014 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
5015 assert(it != this->shares.end() && it->first <= this->unrestricted);
5016 if (it->second != excluded && it->second != excluded2) return it->second;
5017
5018 /* We've hit one of the excluded stations.
5019 * Draw another share, from outside its range. */
5020
5021 uint end = it->first;
5022 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
5023 uint interval = end - begin;
5024 if (interval >= this->unrestricted) return StationID::Invalid(); // Only one station in the map.
5025 uint new_max = this->unrestricted - interval;
5026 uint rand = RandomRange(new_max);
5027 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
5028 this->shares.upper_bound(rand + interval);
5029 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
5030 if (it2->second != excluded && it2->second != excluded2) return it2->second;
5031
5032 /* We've hit the second excluded station.
5033 * Same as before, only a bit more complicated. */
5034
5035 uint end2 = it2->first;
5036 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
5037 uint interval2 = end2 - begin2;
5038 if (interval2 >= new_max) return StationID::Invalid(); // Only the two excluded stations in the map.
5039 new_max -= interval2;
5040 if (begin > begin2) {
5041 std::swap(begin, begin2);
5042 std::swap(end, end2);
5043 std::swap(interval, interval2);
5044 }
5045 rand = RandomRange(new_max);
5046 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
5047 if (rand < begin) {
5048 it3 = this->shares.upper_bound(rand);
5049 } else if (rand < begin2 - interval) {
5050 it3 = this->shares.upper_bound(rand + interval);
5051 } else {
5052 it3 = this->shares.upper_bound(rand + interval + interval2);
5053 }
5054 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
5055 return it3->second;
5056}
5057
5064{
5065 assert(!this->shares.empty());
5066 SharesMap new_shares;
5067 uint i = 0;
5068 for (const auto &it : this->shares) {
5069 new_shares[++i] = it.second;
5070 if (it.first == this->unrestricted) this->unrestricted = i;
5071 }
5072 this->shares.swap(new_shares);
5073 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
5074}
5075
5082void FlowStat::ChangeShare(StationID st, int flow)
5083{
5084 /* We assert only before changing as afterwards the shares can actually
5085 * be empty. In that case the whole flow stat must be deleted then. */
5086 assert(!this->shares.empty());
5087
5088 uint removed_shares = 0;
5089 uint added_shares = 0;
5090 uint last_share = 0;
5091 SharesMap new_shares;
5092 for (const auto &it : this->shares) {
5093 if (it.second == st) {
5094 if (flow < 0) {
5095 uint share = it.first - last_share;
5096 if (flow == INT_MIN || (uint)(-flow) >= share) {
5097 removed_shares += share;
5098 if (it.first <= this->unrestricted) this->unrestricted -= share;
5099 if (flow != INT_MIN) flow += share;
5100 last_share = it.first;
5101 continue; // remove the whole share
5102 }
5103 removed_shares += (uint)(-flow);
5104 } else {
5105 added_shares += (uint)(flow);
5106 }
5107 if (it.first <= this->unrestricted) this->unrestricted += flow;
5108
5109 /* If we don't continue above the whole flow has been added or
5110 * removed. */
5111 flow = 0;
5112 }
5113 new_shares[it.first + added_shares - removed_shares] = it.second;
5114 last_share = it.first;
5115 }
5116 if (flow > 0) {
5117 new_shares[last_share + (uint)flow] = st;
5118 if (this->unrestricted < last_share) {
5119 this->ReleaseShare(st);
5120 } else {
5121 this->unrestricted += flow;
5122 }
5123 }
5124 this->shares.swap(new_shares);
5125}
5126
5132void FlowStat::RestrictShare(StationID st)
5133{
5134 assert(!this->shares.empty());
5135 uint flow = 0;
5136 uint last_share = 0;
5137 SharesMap new_shares;
5138 for (auto &it : this->shares) {
5139 if (flow == 0) {
5140 if (it.first > this->unrestricted) return; // Not present or already restricted.
5141 if (it.second == st) {
5142 flow = it.first - last_share;
5143 this->unrestricted -= flow;
5144 } else {
5145 new_shares[it.first] = it.second;
5146 }
5147 } else {
5148 new_shares[it.first - flow] = it.second;
5149 }
5150 last_share = it.first;
5151 }
5152 if (flow == 0) return;
5153 new_shares[last_share + flow] = st;
5154 this->shares.swap(new_shares);
5155 assert(!this->shares.empty());
5156}
5157
5163void FlowStat::ReleaseShare(StationID st)
5164{
5165 assert(!this->shares.empty());
5166 uint flow = 0;
5167 uint next_share = 0;
5168 bool found = false;
5169 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
5170 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
5171 if (found) {
5172 flow = next_share - it->first;
5173 this->unrestricted += flow;
5174 break;
5175 } else {
5176 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
5177 if (it->second == st) found = true;
5178 }
5179 next_share = it->first;
5180 }
5181 if (flow == 0) return;
5182 SharesMap new_shares;
5183 new_shares[flow] = st;
5184 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
5185 if (it->second != st) {
5186 new_shares[flow + it->first] = it->second;
5187 } else {
5188 flow = 0;
5189 }
5190 }
5191 this->shares.swap(new_shares);
5192 assert(!this->shares.empty());
5193}
5194
5201{
5202 assert(runtime > 0);
5203 SharesMap new_shares;
5204 uint share = 0;
5205 for (auto i : this->shares) {
5206 share = std::max(share + 1, i.first * 30 / runtime);
5207 new_shares[share] = i.second;
5208 if (this->unrestricted == i.first) this->unrestricted = share;
5209 }
5210 this->shares.swap(new_shares);
5211}
5212
5219void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5220{
5221 FlowStatMap::iterator origin_it = this->find(origin);
5222 if (origin_it == this->end()) {
5223 this->emplace(origin, FlowStat(via, flow));
5224 } else {
5225 origin_it->second.ChangeShare(via, flow);
5226 assert(!origin_it->second.GetShares()->empty());
5227 }
5228}
5229
5238void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5239{
5240 FlowStatMap::iterator prev_it = this->find(origin);
5241 if (prev_it == this->end()) {
5242 FlowStat fs(via, flow);
5243 fs.AppendShare(StationID::Invalid(), flow);
5244 this->emplace(origin, fs);
5245 } else {
5246 prev_it->second.ChangeShare(via, flow);
5247 prev_it->second.ChangeShare(StationID::Invalid(), flow);
5248 assert(!prev_it->second.GetShares()->empty());
5249 }
5250}
5251
5257{
5258 for (auto &i : *this) {
5259 FlowStat &fs = i.second;
5260 uint local = fs.GetShare(StationID::Invalid());
5261 if (local > INT_MAX) { // make sure it fits in an int
5262 fs.ChangeShare(self, -INT_MAX);
5263 fs.ChangeShare(StationID::Invalid(), -INT_MAX);
5264 local -= INT_MAX;
5265 }
5266 fs.ChangeShare(self, -(int)local);
5267 fs.ChangeShare(StationID::Invalid(), -(int)local);
5268
5269 /* If the local share is used up there must be a share for some
5270 * remote station. */
5271 assert(!fs.GetShares()->empty());
5272 }
5273}
5274
5281std::vector<StationID> FlowStatMap::DeleteFlows(StationID via)
5282{
5283 std::vector<StationID> ret;
5284 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5285 FlowStat &s_flows = f_it->second;
5286 s_flows.ChangeShare(via, INT_MIN);
5287 if (s_flows.GetShares()->empty()) {
5288 ret.push_back(f_it->first);
5289 this->erase(f_it++);
5290 } else {
5291 ++f_it;
5292 }
5293 }
5294 return ret;
5295}
5296
5302{
5303 for (auto &it : *this) {
5304 it.second.RestrictShare(via);
5305 }
5306}
5307
5312void FlowStatMap::ReleaseFlows(StationID via)
5313{
5314 for (auto &it : *this) {
5315 it.second.ReleaseShare(via);
5316 }
5317}
5318
5324{
5325 uint ret = 0;
5326 for (const auto &it : *this) {
5327 ret += (--(it.second.GetShares()->end()))->first;
5328 }
5329 return ret;
5330}
5331
5337uint FlowStatMap::GetFlowVia(StationID via) const
5338{
5339 uint ret = 0;
5340 for (const auto &it : *this) {
5341 ret += it.second.GetShare(via);
5342 }
5343 return ret;
5344}
5345
5351uint FlowStatMap::GetFlowFrom(StationID from) const
5352{
5353 FlowStatMap::const_iterator i = this->find(from);
5354 if (i == this->end()) return 0;
5355 return (--(i->second.GetShares()->end()))->first;
5356}
5357
5364uint FlowStatMap::GetFlowFromVia(StationID from, StationID via) const
5365{
5366 FlowStatMap::const_iterator i = this->find(from);
5367 if (i == this->end()) return 0;
5368 return i->second.GetShare(via);
5369}
5370
5371static CommandCost CheckBuildAbove_Station(TileIndex tile, DoCommandFlags, Axis, int height)
5372{
5373 StationType type = GetStationType(tile);
5374 auto bridgeable_info = GetStationBridgeableTileInfo(type);
5375
5376 switch (type) {
5377 case StationType::Rail:
5379 if (const StationSpec *spec = GetStationSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5380 break;
5381
5382 case StationType::Bus:
5383 case StationType::Truck:
5385 if (const RoadStopSpec *spec = GetRoadStopSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5386 break;
5387
5388 default: break;
5389 }
5390
5391 return IsStationBridgeAboveOk(tile, bridgeable_info, type, GetStationGfx(tile), height);
5392}
5393
5394extern const TileTypeProcs _tile_type_station_procs = {
5395 DrawTile_Station, // draw_tile_proc
5396 GetSlopePixelZ_Station, // get_slope_z_proc
5397 ClearTile_Station, // clear_tile_proc
5398 nullptr, // add_accepted_cargo_proc
5399 GetTileDesc_Station, // get_tile_desc_proc
5400 GetTileTrackStatus_Station, // get_tile_track_status_proc
5401 ClickTile_Station, // click_tile_proc
5402 AnimateTile_Station, // animate_tile_proc
5403 TileLoop_Station, // tile_loop_proc
5404 ChangeTileOwner_Station, // change_tile_owner_proc
5405 nullptr, // add_produced_cargo_proc
5406 VehicleEnter_Station, // vehicle_enter_tile_proc
5407 GetFoundation_Station, // get_foundation_proc
5408 TerraformTile_Station, // terraform_tile_proc
5409 CheckBuildAbove_Station, // check_build_above_proc
5410};
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:186
static const uint INVALID_AIRPORTTILE
id for an invalid airport tile
Definition airport.h:25
static const uint NEW_AIRPORTTILE_OFFSET
offset of first newgrf airport tile
Definition airport.h:24
@ AirportClosed
Dummy block for indicating a closed airport.
Definition airport.h:130
@ FLYING
Vehicle is flying in the air.
Definition airport.h:77
@ NUM_AIRPORTS
Maximal number of airports in total.
Definition airport.h:41
@ AT_OILRIG
Oilrig airport.
Definition airport.h:38
Enum of the default airport tiles.
void AddAnimatedTile(TileIndex tile, bool mark_dirty)
Add the given tile to the animated tile table (if it does not exist yet).
void DeleteAnimatedTile(TileIndex tile, bool immediate)
Stops animation on the given tile.
Tile animation!
Functions related to autoslope.
bool AutoslopeCheckForAxis(TileIndex tile, int z_new, Slope tileh_new, Axis axis)
Autoslope check for tiles with something built along an axis.
Definition autoslope.h:51
bool AutoslopeCheckForEntranceEdge(TileIndex tile, int z_new, Slope tileh_new, DiagDirection entrance)
Autoslope check for tiles with an entrance on an edge.
Definition autoslope.h:31
bool AutoslopeEnabled()
Tests if autoslope is enabled for _current_company.
Definition autoslope.h:65
constexpr bool HasExactlyOneBit(T value)
Test whether value has exactly 1 bit set.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
Draw the middle bits of a bridge.
TileIndex GetSouthernBridgeEnd(TileIndex t)
Finds the southern end of a bridge starting at a middle tile.
int GetBridgeHeight(TileIndex t)
Get the height ('z') of a bridge.
Map accessor functions for bridges.
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:104
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:73
@ Passengers
Passengers.
Definition cargotype.h:50
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
Cheats _cheats
All the cheats.
Definition cheat.cpp:16
Types related to cheating.
Iterator to iterate over all tiles belonging to an airport.
Iterator to iterate over all tiles belonging to an airport spec.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Iterator to iterate over all tiles belonging to a bitmaptilearea.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
Flat set implementation that uses a sorted vector for storage.
std::pair< const_iterator, bool > insert(const Tkey &key)
Insert a key into the set, if it does not already exist.
uint GetFlow() const
Get the sum of all flows from this FlowStatMap.
void PassOnFlow(StationID origin, StationID via, uint amount)
Pass on some flow, remembering it as invalid, for later subtraction from locally consumed flow.
std::vector< StationID > DeleteFlows(StationID via)
Delete all flows at a station for specific cargo and destination.
void AddFlow(StationID origin, StationID via, uint amount)
Add some flow from "origin", going via "via".
uint GetFlowFrom(StationID from) const
Get the sum of flows from a specific station from this FlowStatMap.
void FinalizeLocalConsumption(StationID self)
Subtract invalid flows from locally consumed flow.
void ReleaseFlows(StationID via)
Release all flows at a station for specific cargo and destination.
uint GetFlowFromVia(StationID from, StationID via) const
Get the flow from a specific station via a specific other station.
uint GetFlowVia(StationID via) const
Get the sum of flows via a specific station from this FlowStatMap.
void RestrictFlows(StationID via)
Restrict all flows at a station for specific cargo and destination.
Flow statistics telling how much flow should be sent along a link.
static const SharesMap empty_sharesmap
Static instance of FlowStat::SharesMap.
void ScaleToMonthly(uint runtime)
Scale all shares from link graph's runtime to monthly values.
void RestrictShare(StationID st)
Restrict a flow by moving it to the end of the map and decreasing the amount of unrestricted flow.
uint GetShare(StationID st) const
Get flow for a station.
StationID GetVia() const
Get a station a package can be routed to.
const SharesMap * GetShares() const
Get the actual shares as a const pointer so that they can be iterated over.
SharesMap shares
Shares of flow to be sent via specified station (or consumed locally).
void ReleaseShare(StationID st)
Release ("unrestrict") a flow by moving it to the begin of the map and increasing the amount of unres...
void ChangeShare(StationID st, int flow)
Change share for specified station.
void AppendShare(StationID st, uint flow, bool restricted=false)
Add some flow to the end of the shares map.
void Invalidate()
Reduce all flows to minimum capacity so that they don't get in the way of link usage statistics too m...
uint unrestricted
Limit for unrestricted shares.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
void Insert(const T &element)
Insert a single element in the tree.
Definition kdtree.hpp:396
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:90
TimerGameEconomy::Date LastCompression() const
Get date of last compression.
Definition linkgraph.h:236
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:230
static const uint MIN_TIMEOUT_DISTANCE
Minimum effective distance for timeout calculation.
Definition linkgraph.h:170
static constexpr TimerGameEconomy::Date STALE_LINK_DEPOT_TIMEOUT
Number of days before deleting links served only by vehicles stopped in depot.
Definition linkgraph.h:173
static constexpr TimerGameEconomy::Date COMPRESSION_INTERVAL
Minimum number of days between subsequent compressions of a LG.
Definition linkgraph.h:176
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
StringID name
Name of this class.
static NewGRFClass * Get(StationClassID class_index)
uint GetSpecCount() const
Get the number of allocated specs within the class.
const Tspec * GetSpec(uint index) const
Get a spec from the class at a given index.
uint position
Position within iterator.
const RailStationTileLayout & stl
Station tile layout being iterated.
uint length
Length of platforms.
std::span< const StationGfx > layout
Predefined tile layout.
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:125
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:220
struct RailTypeInfo::@157247141350136173143103254227157213063052244122 strings
Strings associated with the rail type.
SpriteID single_y
single piece of rail in Y direction, without ground
Definition rail.h:126
StringID name
Name of this rail type.
Definition rail.h:165
uint8_t fallback_railtype
Original railtype number to use when drawing non-newgrf railtypes, or when drawing stations.
Definition rail.h:190
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:286
struct RailTypeInfo::@332027037331076264023214171276243307073252216167 base_sprites
Struct containing the main sprites.
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:116
struct RoadTypeInfo::@070000167274302256150317022075324310363002361255 strings
Strings associated with the rail type.
StringID name
Name of this rail type.
Definition road.h:77
Generate TileIndices around a center tile or tile area, with increasing distance.
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.
uint StoredCount() const
Returns sum of cargo on board the vehicle (ie not only reserved).
Iterate over all vehicles on a tile.
Functions related to clear (TileType::Clear) land.
CommandCost CommandCostWithParam(StringID str, uint64_t value)
Return an error status, with string and parameter.
Definition command.cpp:416
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Auto
don't allow building on structures
@ Execute
execute the given command
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
Commands
List of commands.
Definition of stuff that is very close to a company, like the company struct itself.
PaletteID GetCompanyPalette(CompanyID company)
Get the palette for recolouring with a company colour.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
void DirtyCompanyInfrastructureWindows(CompanyID company)
Redraw all windows with company infrastructure counts.
GUI Functions related to companies.
static constexpr Owner OWNER_END
Last + 1 owner.
static constexpr Owner OWNER_TOWN
A town owns the tile, or a town is expanding.
static constexpr Owner OWNER_NONE
The tile has no ownership.
static constexpr Owner INVALID_OWNER
An invalid owner.
static constexpr Owner OWNER_WATER
The tile/execution is done by "water".
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh)
Find out if the slope of the tile is suitable to build a depot of given direction.
Definition depot_func.h:27
void ShowDepotWindow(TileIndex tile, VehicleType type)
Opens a depot window.
bool IsValidAxis(Axis d)
Checks if an integer value is a valid Axis.
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
DiagDirections AxisToDiagDirs(Axis a)
Converts an Axis to DiagDirections.
bool IsValidDiagDirection(DiagDirection d)
Checks if an integer value is a valid DiagDirection.
DiagDirection AxisToDiagDir(Axis a)
Converts an Axis to a DiagDirection.
Axis OtherAxis(Axis a)
Select the other axis as provided.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
Direction
Defines the 8 directions on the map.
@ DIR_N
North.
@ DIR_W
West.
@ DIR_E
East.
Axis
Allow incrementing of DiagDirDiff variables.
@ AXIS_X
The X axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_END
Used for iterations.
@ DIAGDIR_BEGIN
Used for iterations.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
@ DIAGDIR_SW
Southwest.
static const uint ROAD_STOP_TRACKBIT_FACTOR
Multiplier for how many regular track bits a bay stop counts.
@ EXPENSES_CONSTRUCTION
Construction costs.
Price
Enumeration of all base prices for use with Prices.
@ BuildStationTruck
Price for building lorry stations.
@ ClearStationBus
Price for destroying bus stops.
@ BuildStationBus
Price for building bus stops.
@ ClearStationAirport
Price for destroying airports.
@ ClearStationTruck
Price for destroying lorry stations.
@ BuildStationAirport
Price for building airports.
@ ClearStationDock
Price for destroying docks.
@ ClearStationRail
Price for destroying rail stations.
@ Invalid
Invalid base price.
@ 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:550
Header file for electrified rail specific functions.
bool HasRailCatenaryDrawn(RailType rt)
Test if we should draw rail catenary.
Definition elrail_func.h:30
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:17
Flat set container implementation.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1038
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
uint8_t GetSnowLine()
Get the current snow line, either variable or static.
void MarkTilesDirty(bool cargo_change) const
Marks the tiles of the station as dirty.
Definition station.cpp:247
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
HouseZone
Concentric rings of zoning around the centre of a town.
Definition house.h:57
@ TownEdge
Edge of the town; roads without pavement.
Definition house.h:58
Base of all industries.
bool IsTileForestIndustry(TileIndex tile)
Check whether the tile is a forest.
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
IndustryType GetIndustryType(Tile tile)
Retrieve the type for this industry.
IndustryID GetIndustryIndex(Tile t)
Get the industry ID of the given tile.
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like IT_...
@ Extractive
Like mines.
void ChangeTileOwner(TileIndex tile, Owner old_owner, Owner new_owner)
Change the owner of a tile.
void DrawFoundation(TileInfo *ti, Foundation f)
Draw foundation f at tile ti.
std::tuple< Slope, int > GetFoundationPixelSlope(TileIndex tile)
Get slope of a tile on top of a (possible) foundation If a tile does not have a foundation,...
Definition landscape.h:67
uint ApplyPixelFoundationToSlope(Foundation f, Slope &s)
Applies a foundation to a slope.
Definition landscape.h:128
Point RemapCoords2(int x, int y)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:97
Command definitions related to landscape (slopes etc.).
@ Arctic
Landscape with snow levels.
@ Tropic
Landscape with distinct rainforests and deserts,.
Some typedefs for the main game.
@ Increase
Increase capacity.
@ DT_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:218
uint DistanceMax(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles.
Definition map.cpp:190
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:109
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:158
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:442
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:391
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:557
static TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:375
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:427
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:417
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:572
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
constexpr To ClampTo(From value)
Clamp the given value down to lie within the requested type.
Types related to the misc widgets.
uint8_t StationGfx
Copy from station_map.h.
StationGfx GetTranslatedAirportTileID(StationGfx gfx)
Do airporttile gfx ID translation for NewGRFs.
NewGRF handling of airport tiles.
@ NoAnimation
There is no animation.
@ DrawTileLayout
Use callback to select a tile layout to use when drawing.
@ Avail
Availability of station in construction window.
@ SlopeCheck
Check slope of new station tiles.
@ StationRatingCalc
custom station rating for this cargo type
@ CBID_STATION_BUILD_TILE_LAYOUT
Called when building a station to customize the tile layout.
@ CBID_STATION_DRAW_TILE_LAYOUT
Choose a tile layout to draw, instead of the standard range.
@ CBID_CARGO_STATION_RATING_CALC
Called to calculate part of a station rating.
@ CBID_STATION_AVAILABILITY
Determine whether a newstation should be made available to build.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
@ Avail
Availability of road stop in construction window.
SpriteID GetCanalSprite(CanalFeature feature, TileIndex tile)
Lookup the base sprite to use for a canal.
Handling of NewGRF canals.
Cargo support for NewGRFs.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
bool Convert8bitBooleanCallback(const GRFFile *grffile, uint16_t cbid, uint16_t cb_res)
Converts a callback result into a boolean.
GRFConfig * GetGRFConfig(uint32_t grfid, uint32_t mask)
Retrieve a NewGRF from the current config by its grfid.
Functions/types related to NewGRF debugging.
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Delete inspect window for a given feature and index.
void TriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, CargoTypes trigger_cargoes)
Run watched cargo accepted callback for a house.
Functions related to NewGRF houses.
SpriteID GetCustomRailSprite(const RailTypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of rail types.
void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger road stop randomisation.
void DeallocateSpecFromRoadStop(BaseStation *st, uint8_t specindex)
Deallocate a RoadStopSpec from a Station.
std::optional< uint8_t > AllocateSpecToRoadStop(const RoadStopSpec *spec, BaseStation *st)
Allocate a RoadStopSpec to a Station.
void AssignSpecToRoadStop(const RoadStopSpec *spec, BaseStation *st, uint8_t specindex)
Assign a previously allocated RoadStopSpec specindex to a Station.
NewGRF definitions and structures for road stops.
@ Overlay
Drive-through stops: Draw the road overlay, e.g. pavement.
@ WaypGround
Waypoints: Draw the sprite layout ground tile (on top of the road).
@ Road
Bay stops: Draw the road itself.
bool IsWaypointClass(const RoadStopClass &cls)
Test if a RoadStopClass is the waypoint class.
RoadStopClassID
@ ROADSTOPTYPE_FREIGHT
This RoadStop is for freight (truck) stops.
@ ROADSTOPTYPE_ALL
This RoadStop is for both types of station road stops.
@ ROADSTOPTYPE_PASSENGER
This RoadStop is for passenger (bus) stops.
@ NoCatenary
Do not show catenary.
@ DriveThroughOnly
Stop is drive-through only.
@ DrawModeRegister
Read draw mode from register 0x100.
SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of road types.
uint32_t GetPlatformInfo(Axis axis, uint8_t tile, int platforms, int length, int x, int y, bool centred)
Evaluate a tile's position within a station, and return the result in a bit-stuffed format.
SpriteID GetCustomStationRelocation(const StationSpec *statspec, BaseStation *st, TileIndex tile, uint32_t var10)
Resolve sprites for drawing a station tile.
SpriteID GetCustomStationFoundationRelocation(const StationSpec *statspec, BaseStation *st, TileIndex tile, uint layout, uint edge_info)
Resolve the sprites for custom station foundations.
void AssignSpecToStation(const StationSpec *spec, BaseStation *st, uint8_t specindex)
Assign a previously allocated StationSpec specindex to a Station.
void DeallocateSpecFromStation(BaseStation *st, uint8_t specindex)
Deallocate a StationSpec from a Station.
void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger station randomisation.
CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, uint8_t plat_len, uint8_t numtracks)
Check the slope of a tile of a new station.
std::optional< uint8_t > AllocateSpecToStation(const StationSpec *spec, BaseStation *st)
Allocate a StationSpec to a Station.
Header file for NewGRF stations.
@ CustomFoundations
Draw custom foundations.
@ SeparateGround
Use different sprite set for ground sprites.
@ ExtendedFoundations
Extended foundation block instead of simple.
uint16_t GetStationLayoutKey(uint8_t platforms, uint8_t length)
Get the station layout key for a given station layout size.
NewGRFClass< StationSpec, StationClassID, STAT_CLASS_MAX > StationClass
Class containing information relating to station classes.
StationClassID
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:902
@ Acceptance
A type of cargo is (no longer) accepted.
Definition news_type.h:43
@ Company
Company news item. (Newspaper with face).
Definition news_type.h:82
@ Small
Small news item. (Information window with text and viewport).
Definition news_type.h:79
@ Vehicle
Vehicle news item. (new engine available).
Definition news_type.h:81
@ InColour
News item is shown in colour (otherwise it is shown in black & white).
Definition news_type.h:90
@ LinkGraph
A game paused due to the link graph schedule lagging.
Definition openttd.h:75
Functions related to order backups.
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:331
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:90
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:428
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:376
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:300
@ RTSG_GROUND
Main group of ground images.
Definition rail.h:43
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:42
RailTrackOffset
Offsets for sprites within an overlay/underlay set.
Definition rail.h:61
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:63
@ RTO_X
Piece of rail in X direction.
Definition rail.h:62
Command definitions for rail.
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
static bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
TrackBits GetRailReservationTrackBits(Tile t)
Returns the reserved track bits of the tile.
Definition rail_map.h:194
bool HasSignals(Tile t)
Checks if a rail tile has signals.
Definition rail_map.h:72
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
Pseudo random number generator.
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
Declaration of link refreshing utility.
bool ValParamRoadType(RoadType roadtype)
Validate functions for rail building.
Definition road.cpp:165
bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
Checks if an engine of the given RoadType got power on a tile with a given RoadType.
Definition road.h:230
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:215
@ ROTSG_OVERLAY
Optional: Images for overlaying track.
Definition road.h:38
@ ROTSG_ROADSTOP
Required: Bay stop surface.
Definition road.h:47
@ ROTSG_GROUND
Required: Main group of ground images.
Definition road.h:39
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:240
void DrawRoadGroundSprites(const TileInfo *ti, RoadBits road, RoadBits tram, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, Roadside roadside, bool snow_or_desert)
Draw road ground sprites.
void DrawRoadCatenary(const TileInfo *ti)
Draws the catenary for the given tile.
void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count)
Update road infrastructure counts for a company.
Definition road_cmd.cpp:180
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:252
void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, uint road_offset, uint tram_offset, bool draw_underlay)
Draw road underlay and overlay sprites.
Functions related to roads.
RoadBits AxisToRoadBits(Axis a)
Create the road-part which belongs to the given Axis.
Definition road_func.h:111
Functions used internally by the roads.
bool MayHaveRoad(Tile t)
Test whether a tile can have road/tram types.
Definition road_map.cpp:21
RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance)
Returns the RoadBits on an arbitrary tile Special behaviour:
Definition road_map.cpp:54
void SetRoadOwner(Tile t, RoadTramType rtt, Owner o)
Set the owner of a specific road type.
Definition road_map.h:235
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:285
bool HasTileRoadType(Tile t, RoadTramType rtt)
Check if a tile has a road or a tram road type.
Definition road_map.h:195
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:620
RoadBits GetAllRoadBits(Tile tile)
Get all set RoadBits on the given tile.
Definition road_map.h:125
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:218
Roadside
The possible road side decorations.
Definition road_map.h:457
@ Paved
Road with paved sidewalks.
Definition road_map.h:460
@ Barren
Road on barren land.
Definition road_map.h:458
@ Grass
Road on grass.
Definition road_map.h:459
RoadBits
Enumeration for the road parts on a tile.
Definition road_type.h:56
@ ROAD_NONE
No road-part is build.
Definition road_type.h:57
@ ROAD_Y
Full road along the y-axis (north-west + south-east).
Definition road_type.h:63
@ ROAD_X
Full road along the x-axis (south-west + north-east).
Definition road_type.h:62
RoadTramType
The different types of road type.
Definition road_type.h:37
@ RTT_ROAD
Road road type.
Definition road_type.h:38
@ RTT_TRAM
Tram road type.
Definition road_type.h:39
RoadType
The different roadtypes we support.
Definition road_type.h:23
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:28
@ DRD_NONE
None of the directions are disallowed.
Definition road_type.h:78
Base class for roadstops.
Road vehicle states.
@ RVSB_IN_ROAD_STOP
The vehicle is in a road stop.
Definition roadveh.h:49
@ RVSB_ROAD_STOP_TRACKDIR_MASK
Only bits 0 and 3 are used to encode the trackdir for road stops.
Definition roadveh.h:57
@ RVS_IN_DT_ROAD_STOP
The vehicle is in a drive-through road stop.
Definition roadveh.h:46
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Base for ships.
bool IsShipDestinationTile(TileIndex tile, StationID station)
Test if a tile is a docking tile for the given station.
Definition ship_cmd.cpp:617
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:589
@ Enter
signal entering the block found
Definition signal.cpp:255
static constexpr int GetSlopeMaxZ(Slope s)
Returns the height of the highest corner of a slope relative to TileZ (= minimal height).
Definition slope_func.h:160
static constexpr bool IsSteepSlope(Slope s)
Checks if a slope is steep.
Definition slope_func.h:36
Foundation FlatteningFoundation(Slope s)
Returns the foundation needed to flatten a slope.
Definition slope_func.h:369
DiagDirection GetInclinedSlopeDirection(Slope s)
Returns the direction of an inclined slope.
Definition slope_func.h:239
Slope
Enumeration for the slope-type.
Definition slope_type.h:47
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:48
Foundation
Enumeration for Foundations.
Definition slope_type.h:92
@ FOUNDATION_LEVELED
The tile is leveled up to a flat slope.
Definition slope_type.h:94
@ Industry
Source/destination is an industry.
Definition source_type.h:21
@ Town
Source/destination is a town.
Definition source_type.h:22
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:113
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:99
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:198
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1559
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1549
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
Definition sprites.h:1562
Base classes/functions for stations.
void ForAllStationsAroundTiles(const TileArea &ta, Func func)
Call a function on all stations that have any part of the requested area within their catchment.
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index=-1)
Remove a bus station/truck stop.
std::tuple< CommandCost, StationID > CmdMoveStationName(DoCommandFlags flags, StationID station_id, TileIndex tile)
Move a station name.
static StringID GetBridgeTooLowMessageForStationType(StationType type)
Get station-type-specific string for a bridge that is too low.
static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
Common part of building various station parts and possibly attaching them to an existing one.
static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlags flags)
Remove a rail waypoint.
void CcMoveStationName(Commands, const CommandCost &result, StationID station_id)
Callback function that is called after a name is moved.
CargoTypes GetEmptyMask(const Station *st)
Get a mask of the cargo types that are empty at the station.
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.
CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted)
Get the acceptance of cargoes around the tile in 1/8.
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)
CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta)
Check whether we can expand the rail part of the given station.
void ClearDockingTilesCheckingNeighbours(TileIndex tile)
Clear docking tile status from tiles around a removed dock, if the tile has no neighbours which would...
bool(* CMSAMatcher)(TileIndex tile)
Function to check whether the given tile matches some criterion.
CommandCost IsRoadStationBridgeAboveOk(TileIndex tile, const RoadStopSpec *spec, StationType type, StationGfx layout)
Test if a road station can be built below a bridge.
bool IsHangar(Tile t)
Check whether the given tile is a hangar.
static Station * GetClosestDeletedStation(TileIndex tile)
Find the closest deleted station of the current company.
CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
Rename a station.
static CommandCost RemoveAirport(TileIndex tile, DoCommandFlags flags)
Remove an airport.
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.
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.
bool HasStationInUse(StationID station, bool include_company, CompanyID company)
Tests whether the company's vehicles have this station in orders.
static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
Counts the numbers of tiles matching a specific type in the area around.
CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
Build a bus or truck stop.
static StationSpec::TileFlags GetStationTileFlags(StationGfx gfx, const StationSpec *statspec)
Get station tile flags for the given StationGfx.
static CommandCost IsDockBridgeAboveOk(TileIndex tile, StationGfx layout)
Test if a dock can be built below a bridge.
static bool DrawCustomStationFoundations(const StationSpec *statspec, BaseStation *st, TileInfo *ti, StationGfx gfx)
Draw custom station foundations for a NewGRF station if provided.
const DrawTileSprites * GetStationTileLayout(StationType st, uint8_t gfx)
Get station tile layout for a station type and its station gfx.
CommandCost CmdRemoveFromRoadWaypoint(DoCommandFlags flags, TileIndex start, TileIndex end)
Remove road waypoints.
void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
Reroute cargo of type c at station st or in any vehicles unloading there.
CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags)
Clear a single tile of a station.
static void UpdateStationRating(Station *st)
Periodic update of a station's rating.
void UpdateAllStationVirtCoords()
Update the virtual coords needed to draw the station sign for all stations.
static 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 void FreeTrainReservation(Train *v)
Clear platform reservation during station building/removing.
void SetRailStationTileFlags(TileIndex tile, const StationSpec *statspec)
Set rail station tile flags for the given tile.
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *station, RoadType rt, Money unit_cost)
Calculates cost of new road stops within the area.
static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
Get the acceptance of cargoes around the station in.
static CommandCost RemoveDock(TileIndex tile, DoCommandFlags flags)
Remove a dock.
bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
Check whether a sprite is a track sprite, which can be replaced by a non-track ground sprite and a ra...
CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
Get the cargo types being produced around the tile (in a rectangle).
CommandCost CmdRemoveFromRailStation(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
Remove a single tile from a rail station.
static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
Find a nearby station that joins this station.
void DeleteStaleLinks(Station *from)
Check all next hops of cargo packets in this station for existence of a a valid link they may use to ...
static BridgePillarFlags GetStationBlockedPillars(std::span< const BridgeableTileInfo > bridgeable_info, uint8_t layout)
Get blocked pillar information for a station tile.
CommandCost CheckBuildableTile(TileIndex tile, DiagDirections invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge=true)
Checks if the given tile is buildable, flat and has a certain height.
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
Find a nearby station that joins this station.
Town * AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
Finds the town nearest to given airport.
static bool StationHandleBigTick(BaseStation *st)
This function is called for each station once every 250 ticks.
static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
Add news item for when a station changes which cargoes it accepts.
CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailType rt, Axis axis, uint8_t numtracks, uint8_t plat_len, StationClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
Build rail station.
static void DeleteStationIfEmpty(BaseStation *st)
This is called right after a station was deleted.
static CommandCost IsStationBridgeAboveOk(TileIndex tile, std::span< const BridgeableTileInfo > bridgeable_info, StationType type, StationGfx layout, int bridge_height, StringID disallowed_msg=INVALID_STRING_ID)
Test if a bridge can be built above a station.
Command definitions related to stations.
Functions related to stations.
void ShowStationViewWindow(StationID station)
Opens StationViewWindow for given station.
Declarations for accessing the k-d tree of stations.
void ForAllStationsRadius(TileIndex center, uint radius, Func func)
Call a function on all stations whose sign is within a radius of a center tile.
Sprites to use and how to display them for station tiles.
Functions related to station layouts.
void MakeAirport(Tile t, Owner o, StationID sid, uint8_t section, WaterClass wc)
Make the given tile an airport tile.
StationType GetStationType(Tile t)
Get the station type of this tile.
Definition station_map.h:44
StationGfx GetStationGfx(Tile t)
Get the station graphics of this tile.
Definition station_map.h:68
void SetStationGfx(Tile t, StationGfx gfx)
Set the station graphics of this tile.
Definition station_map.h:80
void MakeRoadStop(Tile t, Owner o, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, DiagDirection d)
Make the given tile a roadstop tile.
void SetCustomStationSpecIndex(Tile t, uint8_t specindex)
Set the custom station spec for this tile.
void SetStationTileHaveWires(Tile t, bool b)
Set the catenary wires state of the rail station.
bool IsAirport(Tile t)
Is this station tile an airport?
bool IsBayRoadStopTile(Tile t)
Is tile t a bay (non-drive through) road stop station?
uint GetCustomRoadStopSpecIndex(Tile t)
Get the custom road stop spec for this tile.
static const int GFX_DOCK_BASE_WATER_PART
The offset for the water parts.
Definition station_map.h:35
bool IsBuoy(Tile t)
Is tile t a buoy tile?
bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile)
Check if a tile is a valid continuation to a railstation tile.
bool IsRoadWaypoint(Tile t)
Is the station at t a road waypoint?
void MakeDriveThroughRoadStop(Tile t, Owner station, Owner road, Owner tram, StationID sid, StationType rst, RoadType road_rt, RoadType tram_rt, Axis a)
Make the given tile a drivethrough roadstop tile.
bool IsDriveThroughStopTile(Tile t)
Is tile t a drive through road stop station or waypoint?
static Roadside GetRoadWaypointRoadside(Tile tile)
Get the decorations of a road waypoint.
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
Track GetRailStationTrack(Tile t)
Get the rail track of a rail station tile.
static void ToggleRoadWaypointOnSnowOrDesert(Tile t)
Toggle the snow/desert state of a road waypoint tile.
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
bool HasStationTileRail(Tile t)
Has this station tile a rail?
StationGfx GetAirportGfx(Tile t)
Get the station graphics of this airport tile.
uint GetCustomStationSpecIndex(Tile t)
Get the custom station spec for this tile.
bool IsRailWaypoint(Tile t)
Is this station tile a rail waypoint?
bool IsRailStation(Tile t)
Is this station tile a rail station?
Definition station_map.h:92
bool IsDockTile(Tile t)
Is tile t a dock tile?
static void SetRoadWaypointRoadside(Tile tile, Roadside s)
Set the decorations of a road waypoint.
void SetStationTileRandomBits(Tile t, uint8_t random_bits)
Set the random bits for a station tile.
bool IsAnyRoadStop(Tile t)
Is the station at t a road station?
void MakeOilrig(Tile t, StationID sid, WaterClass wc)
Make the given tile an oilrig tile.
DiagDirection GetDockDirection(Tile t)
Get the direction of a dock.
Axis GetRailStationAxis(Tile t)
Get the rail direction of a rail station.
static bool IsRoadWaypointOnSnowOrDesert(Tile t)
Check if a road waypoint tile has snow/desert.
bool IsRoadWaypointTile(Tile t)
Is this tile a station tile and a road waypoint?
bool IsStationTileBlocked(Tile t)
Is tile t a blocked tile?
bool IsTruckStop(Tile t)
Is the station at t a truck stop?
bool IsStationRoadStop(Tile t)
Is the station at t a road station?
bool IsCustomStationSpecIndex(Tile t)
Is there a custom rail station spec on this tile?
bool HasStationRail(Tile t)
Has this station tile a rail?
static const int GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET
The offset for the drive through parts.
Definition station_map.h:36
Axis GetDriveThroughStopAxis(Tile t)
Gets the axis of the drive through stop.
void SetStationTileHavePylons(Tile t, bool b)
Set the catenary pylon state of the rail station.
bool IsOilRig(Tile t)
Is tile t part of an oilrig?
bool IsBuoyTile(Tile t)
Is tile t a buoy tile?
void MakeRailStation(Tile t, Owner o, StationID sid, Axis a, uint8_t section, RailType rt)
Make the given tile a rail station tile.
bool HasStationReservation(Tile t)
Get the reservation state of the rail station.
void MakeDock(Tile t, Owner o, StationID sid, DiagDirection d, WaterClass wc)
Make the given tile a dock tile.
DiagDirection GetBayRoadStopDir(Tile t)
Gets the direction the bay road stop entrance points towards.
bool IsDock(Tile t)
Is tile t a dock tile?
bool IsAnyRoadStopTile(Tile t)
Is tile t a road stop station?
void SetStationTileBlocked(Tile t, bool b)
Set the blocked state of the rail station.
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition station_map.h:56
void SetCustomRoadStopSpecIndex(Tile t, uint8_t specindex)
Set the custom road stop spec for this tile.
RoadStopType
Types of RoadStops.
@ Bus
A standard stop for buses.
@ Truck
A standard stop for trucks.
@ End
End of valid types.
@ Dock
Station with a dock.
@ Waypoint
Station is a waypoint.
@ TruckStop
Station with truck stops.
@ Train
Station with train station.
@ Airport
Station with an airport.
@ BusStop
Station with bus stops.
StationType
Station types.
@ Dock
Ship port.
@ Rail
Railways/train station.
@ Bus
Road stop for busses.
@ Truck
Road stop for trucks.
@ End
End marker.
@ RailWaypoint
Waypoint for trains.
@ Buoy
Waypoint for ships.
@ RoadWaypoint
Waypoint for trucks and busses.
@ Oilrig
Heliport on an oil rig.
@ Airport
Airports and heliports, excluding the ones on oil rigs.
@ NewCargo
Trigger station on new cargo arrival.
std::set< Station *, StationCompare > StationList
List of stations.
@ Built
Trigger tile when built.
@ TileLoop
Trigger in the periodic tile loop.
@ NewCargo
Trigger station on new cargo arrival.
@ AcceptanceTick
Trigger station every 250 ticks.
static const uint MAX_LENGTH_STATION_NAME_CHARS
The maximum length of a station name in characters including '\0'.
@ Built
Triggered when the airport is built (for all tiles at the same time).
@ TileLoop
Triggered in the periodic tile loop.
@ NewCargo
Triggered when new cargo arrives at the station (for all tiles at the same time).
@ AcceptanceTick
Triggered every 250 ticks (for all tiles at the same time).
Types related to the station widgets.
@ WID_SV_CLOSE_AIRPORT
'Close airport' button.
@ WID_SV_ROADVEHS
List of scheduled road vehs button.
@ WID_SV_ACCEPT_RATING_LIST
List of accepted cargoes / rating of cargoes.
@ WID_SV_SHIPS
List of scheduled ships button.
@ WID_SV_TRAINS
List of scheduled trains button.
Definition of base types and functions in a cross-platform compatible way.
size_t Utf8StringLength(std::string_view str)
Get the length of an UTF-8 encoded string in number of characters and thus not the number of bytes th...
Definition string.cpp:349
Functions related to low-level strings.
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:336
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
Functions related to OTTD's strings.
auto MakeParameters(Args &&... args)
Helper to create the StringParameters with its own buffer with the given parameter values.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames).
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:73
@ Airplanes
Can planes land on this airport type?
Definition airport.h:162
Defines the data structure for an airport.
StringID name
name of this airport
SubstituteGRFFileProps grf_prop
Properties related to the grf file.
std::vector< AirportTileLayout > layouts
List of layouts composing the airport.
bool IsWithinMapBounds(uint8_t table, TileIndex index) const
Check if the airport would be within the map bounds at the given tile.
uint8_t size_y
size of airport in y direction
uint8_t size_x
size of airport in x direction
static const AirportSpec * Get(uint8_t type)
Retrieve airport spec for the given airport.
std::span< const HangarTileTable > depots
Position of the depots on the airports.
bool IsAvailable() const
Check whether this airport is available to build.
uint8_t noise_level
noise that this airport generates
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.
AirportBlocks blocks
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
TileIndex GetRotatedTileFromOffset(TileIndexDiffC tidc) const
Add the tileoffset to the base tile of this airport but rotate it first.
uint GetHangarNum(TileIndex tile) const
Get the hangar number of the hangar at a specific tile.
Direction rotation
How this airport is rotated.
uint8_t type
Type of this airport,.
PersistentStorage * psa
Persistent storage for NewGRF airports.
uint GetNumHangars() const
Get the number of hangars on this airport.
TileIndex GetHangarTile(uint hangar_num) const
Get the first tile of the given hangar.
const AirportSpec * GetSpec() const
Get the AirportSpec that from the airport type of this airport.
uint8_t layout
Airport layout number.
Base class for all station-ish types.
StringID string_id
Default name (town area) of station.
TileIndex xy
Base tile of the station.
virtual void MoveSign(TileIndex new_xy)
Move this station's main coordinate somewhere else.
std::vector< SpecMapping< StationSpec > > speclist
List of rail station specs of this station.
StationFacilities facilities
The facilities that this station has.
std::string cached_name
NOSAVE: Cache of the resolved name of the station, if not using a custom name.
TileArea train_station
Tile area the train 'station' part covers.
StationAnimationTriggers cached_anim_triggers
NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
Owner owner
The owner of this station.
virtual void UpdateVirtCoord()=0
Update the coordinated of the sign (as shown in the viewport).
uint8_t delete_ctr
Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is ...
StationAnimationTriggers cached_roadstop_anim_triggers
NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing sh...
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
bool IsInUse() const
Check whether the base station currently is in use; in use means that it is not scheduled for deletio...
Town * town
The town this station is associated with.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
virtual bool TileBelongsToRailStation(TileIndex tile) const =0
Check whether a specific tile belongs to this station.
TrackedViewportSign sign
NOSAVE: Dimensions of sign.
TimerGameCalendar::Date build_date
Date of construction.
std::string name
Custom name.
VehicleType type
Type of vehicle.
Class for storing amounts of cargo.
Definition cargo_type.h:111
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:108
static IterateWrapper Iterate(size_t from=0)
Returns an iterable ensemble of all valid CargoSpec.
Definition cargotype.h:192
GUISettings gui
settings related to the GUI
std::array< uint32_t, ROADTYPE_END > road
Count of company owned track bits for each road type.
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.
bool build_on_slopes
allow building on slopes
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:52
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:53
std::string GetName() const
Get the name of this grf.
const struct GRFFile * grffile
grf file that introduced this entity
uint32_t grfid
grfid that introduced this entity.
bool HasGrfFile() const
Test if this entity was introduced by NewGRF.
bool show_track_reservation
highlight reserved tracks.
LandscapeType landscape
the landscape we're currently in
ConstructionSettings construction
construction of things in-game
GameCreationSettings game_creation
settings used during the creation of a game (map)
StationSettings station
settings related to station management
FlowStatMap flows
Planned flows through this station.
StationCargoList cargo
The cargo packets of cargo waiting in this station.
Stores station stats for a single cargo.
uint max_waiting_cargo
Max cargo from this station waiting at any station.
bool HasRating() const
Does this cargo have a rating at this station?
uint8_t last_speed
Maximum speed (up to 255) of the last vehicle that tried to load this cargo.
uint8_t last_age
Age in years (up to 255) of the last vehicle that tried to load this cargo.
uint8_t time_since_pickup
Number of rating-intervals (up to 255) since the last vehicle tried to load this cargo.
States status
Status of this cargo, see State.
NodeID node
ID of node in link graph referring to this goods entry.
@ LastMonth
Set when cargo was delivered for final delivery last month.
@ Acceptance
Set when the station accepts the cargo currently for final deliveries.
@ Rating
This indicates whether a cargo has a rating at the station.
@ AcceptedBigtick
Set when cargo was delivered for final delivery during the current STATION_ACCEPTANCE_TICKS interval.
@ CurrentMonth
Set when cargo was delivered for final delivery this month.
const GoodsEntryData & GetData() const
Get optional cargo packet/flow data.
uint8_t amount_fract
Fractional part of the amount in the cargo list.
LinkGraphID link_graph
Link graph this station belongs to.
bool HasVehicleEverTriedLoading() const
Reports whether a vehicle has ever tried to load the cargo at this station.
uint8_t rating
Station rating for this cargo.
bool HasData() const
Test if this goods entry has optional cargo packet/flow data.
GoodsEntryData & GetOrCreateData()
Get optional cargo packet/flow data.
uint AvailableCount() const
Returns sum of cargo still available for loading at the station.
StationID GetVia(StationID source) const
Get the best next hop for a cargo packet from station source.
Defines the data structure for constructing industry.
SubstituteGRFFileProps grf_prop
properties related to the grf file
StringID name
Displayed name of the industry.
StringID station_name
Default name for nearby station.
bool enabled
entity still available (by default true).newgrf can disable it, though
IndustryLifeTypes life_type
This is also known as Industry production flag, in newgrf specs.
Defines the internal data of a functional industry.
Definition industry.h:62
IndustryType type
type of industry.
Definition industry.h:115
ProducedCargoes produced
produced cargo slots
Definition industry.h:110
Owner owner
owner of the industry. Which SHOULD always be (imho) OWNER_NONE
Definition industry.h:116
static Industry * GetByTile(TileIndex tile)
Get the industry of the given tile.
Definition industry.h:251
TileArea location
Location of the industry.
Definition industry.h:106
Station * neutral_station
Associated neutral station.
Definition industry.h:108
StationList stations_near
NOSAVE: List of nearby stations.
Definition industry.h:123
static uint SizeX()
Get the size of the map along the X.
Definition map_func.h:262
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:271
static uint Size()
Get the size of the map.
Definition map_func.h:280
Tindex class_index
Class index of this spec, invalid until class is allocated.
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:274
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:66
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
uint16_t w
The width of the area.
void Add(TileIndex to_add)
Add a single tile to a tile area; enlarge if needed.
Definition tilearea.cpp:43
void Clear()
Clears the 'tile area', i.e.
TileIndex tile
The base tile of the area.
uint16_t h
The height of the area.
OrthogonalTileArea & Expand(int rad)
Expand a tile area by rad tiles in each direction, keeping within map bounds.
Definition tilearea.cpp:123
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed).
Definition gfx_type.h:24
static Pool::IterateWrapper< BaseStation > Iterate(size_t from=0)
static Industry * Get(auto index)
static T * Create(Targs &&... args)
static LinkGraph * GetIfValid(auto index)
Road stop specification.
Money GetBuildCost(Price category) const
Get the cost for building a road stop of this type.
std::array< BridgeableTileInfo, 6 > bridgeable_info
Per tile layout bridge information.
CargoGRFFileProps grf_prop
Link to NewGRF.
Money GetClearCost(Price category) const
Get the cost for clearing a road stop of this type.
A Stop for a Road Vehicle.
RoadStop * next
Next stop of the given type at this station.
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp:253
void MakeDriveThrough()
Join this road stop to another 'base' road stop if possible; fill all necessary data to become an act...
Definition roadstop.cpp:60
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition roadstop.cpp:122
Buses, trucks and trams belong to this class.
Definition roadveh.h:98
uint8_t state
Definition roadveh.h:100
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.
std::bitset< NUM_INDUSTRYTYPES > indtypes
Bit set indicating when an industry type has been found.
std::bitset< STR_SV_STNAME_FALLBACK - STR_SV_STNAME > used_names
Used default station suffixes.
StationRect - used to track station spread out rectangle - cheaper than scanning whole map.
bool PtInExtendedRect(int x, int y, int distance=0) const
Determines whether a given point (x, y) is within a certain distance of the station rectangle.
Definition station.cpp:564
bool serve_neutral_industries
company stations can serve industries with attached neutral stations
Station specification.
std::vector< BridgeableTileInfo > bridgeable_info
Per tile layout bridge information.
uint8_t disallowed_lengths
Bitmask of platform lengths available for the station.
StringID name
Name of this station.
uint8_t disallowed_platforms
Bitmask of number of platforms available for the station.
CargoGRFFileProps grf_prop
Link to NewGRF.
std::unordered_map< uint16_t, std::vector< uint8_t > > layouts
Custom platform layouts, keyed by platform and length combined.
std::vector< TileFlags > tileflags
List of tile flags.
std::vector< NewGRFSpriteLayout > renderdata
Number of tile layouts.
StationCallbackMasks callback_mask
Bitmask of station callbacks that have to be called.
@ NoWires
Tile should NOT contain catenary wires.
@ Pylons
Tile should contain catenary pylons.
@ Blocked
Tile is blocked to vehicles.
StationSpecFlags flags
Bitmask of flags, bit 0: use different sprite set; bit 1: divide cargo about by station size.
Station data structure.
TileArea GetTileArea(StationType type) const override
Get the tile area for a given station type.
RoadStop * bus_stops
All the road stops.
TileArea ship_station
Tile area the ship 'station' part covers.
IndustryType indtype
Industry type to get the name from.
TileArea docking_station
Tile area the docking tiles cover.
CargoTypes always_accepted
Bitmask of always accepted cargo types (by houses, HQs, industry tiles when industry doesn't accept c...
Industry * industry
NOSAVE: Associated industry for neutral stations. (Rebuilt on load from Industry->st).
void MoveSign(TileIndex new_xy) override
Move the station main coordinate somewhere else.
std::array< GoodsEntry, NUM_CARGO > goods
Goods at this station.
TileArea bus_station
Tile area the bus 'station' part covers.
BitmapTileArea catchment_tiles
NOSAVE: Set of individual tiles covered by catchment area.
void RecomputeCatchment(bool no_clear_nearby_lists=false)
Recompute tiles covered in our catchment area.
Definition station.cpp:466
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:230
uint16_t rail_speed
Speed limit of rail (bridges and track).
Definition tile_cmd.h:51
std::optional< std::string > grf
newGRF used for the tile contents
Definition tile_cmd.h:49
StringID station_name
Type of station within the class.
Definition tile_cmd.h:45
StringID str
Description of the tile.
Definition tile_cmd.h:39
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:43
std::array< Owner, 4 > owner
Name of the owner(s).
Definition tile_cmd.h:41
StringID airport_class
Name of the airport class.
Definition tile_cmd.h:46
StringID airport_name
Name of the airport.
Definition tile_cmd.h:47
uint16_t tram_speed
Speed limit of tram (bridges and track).
Definition tile_cmd.h:55
StringID roadtype
Type of road on the tile.
Definition tile_cmd.h:52
StringID tramtype
Type of tram on the tile.
Definition tile_cmd.h:54
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:50
uint16_t road_speed
Speed limit of road (bridges and track).
Definition tile_cmd.h:53
std::array< StringID, 4 > owner_type
Type of each owner.
Definition tile_cmd.h:42
StringID airport_tile_name
Name of the airport tile.
Definition tile_cmd.h:48
StringID station_class
Class of station.
Definition tile_cmd.h:44
Tile information, used while rendering the tile.
Definition tile_cmd.h:32
Slope tileh
Slope of the tile.
Definition tile_cmd.h:33
TileIndex tile
Tile index.
Definition tile_cmd.h:34
Town data structure.
Definition town.h:63
CompanyMask statues
which companies have a statue?
Definition town.h:79
TileIndex xy
town center tile
Definition town.h:64
uint16_t noise_reached
level of noise that all the airports are generating
Definition town.h:77
uint16_t MaxTownNoise() const
Calculate the max town noise.
Definition town.h:175
CompanyID exclusivity
which company has exclusivity
Definition town.h:84
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:85
'Train' is either a loco or a wagon.
Definition train.h:91
Trackdir GetVehicleTrackdir() const override
Get the tracks of the train vehicle.
Vehicle data structure.
Direction direction
facing
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
VehicleCargoList cargo
The cargo this vehicle is carrying.
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
VehStates vehstatus
Status.
CargoType cargo_type
type of cargo this vehicle is carrying
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.
uint16_t refit_cap
Capacity left over from before last refit.
Vehicle * NextShared() const
Get the next vehicle of the shared vehicle chain.
uint16_t cur_speed
current speed
bool IsFrontEngine() const
Check if the vehicle is a front engine.
TileIndex tile
Current tile index.
TileIndex dest_tile
Heading for this tile.
Owner owner
Which company owns the vehicle?
Representation of a waypoint.
TileArea road_waypoint_area
Tile area the road waypoint part covers.
uint16_t waypoint_flags
Waypoint flags, see WaypointFlags.
void UpdateVirtCoord() override
Update the virtual coords needed to draw the waypoint sign.
@ Source
town/industry is source of subsidised path
@ EnteredStation
The vehicle entered a station.
Definition tile_cmd.h:24
@ CannotEnter
The vehicle cannot enter the tile.
Definition tile_cmd.h:26
bool IsTileFlat(TileIndex tile, int *h)
Check if a given tile is flat.
Definition tile_map.cpp:94
std::tuple< Slope, int > GetTileSlopeZ(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.cpp:55
int GetTileMaxZ(TileIndex t)
Get top height of the tile inside the map.
Definition tile_map.cpp:135
int GetTileZ(TileIndex tile)
Get bottom height of the tile.
Definition tile_map.cpp:115
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
void SetTileOwner(Tile tile, Owner owner)
Sets the owner of a tile.
Definition tile_map.h:198
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
uint8_t GetAnimationFrame(Tile t)
Get the current animation frame.
Definition tile_map.h:250
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
TropicZone GetTropicZone(Tile tile)
Get the tropic zone.
Definition tile_map.h:238
void SetAnimationFrame(Tile t, uint8_t frame)
Set a new animation frame.
Definition tile_map.h:262
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:83
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static constexpr uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
@ Water
Water tile.
Definition tile_type.h:55
@ Station
A tile of a station or airport.
Definition tile_type.h:54
@ Industry
Part of an industry.
Definition tile_type.h:57
@ Trees
Tile with one or more trees.
Definition tile_type.h:53
@ House
A house by a town.
Definition tile_type.h:52
@ Road
A tile with road and/or tram tracks.
Definition tile_type.h:51
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
Definition of Interval and OneShot timers.
Definition of the game-calendar-timer.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.
Base of the town class.
Town * ClosestTownFromTile(TileIndex tile, uint threshold)
Return the town closest (in distance or ownership) to a given tile, within a given threshold.
Town * CalcClosestTownFromTile(TileIndex tile, uint threshold=UINT_MAX)
Return the town closest to the given tile within threshold.
HouseZone GetTownRadiusGroup(const Town *t, TileIndex tile)
Returns the bit corresponding to the town zone of the specified tile.
CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlags flags)
Checks whether the local authority allows construction of a new station (rail, road,...
TrackBits TrackToTrackBits(Track track)
Maps a Track to the corresponding TrackBits value.
Definition track_func.h:77
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
bool IsReversingRoadTrackdir(Trackdir dir)
Checks whether the trackdir means that we are reversing.
Definition track_func.h:673
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:247
TrackStatus CombineTrackStatus(TrackdirBits trackdirbits, TrackdirBits red_signals)
Builds a TrackStatus.
Definition track_func.h:388
TrackBits AxisToTrackBits(Axis a)
Maps an Axis to the corresponding TrackBits value.
Definition track_func.h:88
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:524
Track AxisToTrack(Axis a)
Convert an Axis to the corresponding Track AXIS_X -> TRACK_X AXIS_Y -> TRACK_Y Uses the fact that the...
Definition track_func.h:66
Track RemoveFirstTrack(TrackBits *tracks)
Removes first Track from TrackBits and returns it.
Definition track_func.h:131
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:439
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:39
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:41
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:38
@ TRACK_BIT_NONE
No track.
Definition track_type.h:36
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:37
@ TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:50
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:42
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
Track
These are used to specify a single track.
Definition track_type.h:19
@ TRACK_Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ TRACK_X
Track along the x-axis (north-east to south-west).
Definition track_type.h:21
Base for the train class.
bool TryPathReserve(Train *v, bool mark_as_stuck=false, bool first_tile_okay=false)
Try to reserve a path to a safe position.
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length)
Get the stop location of (the center) of the front vehicle of a train at a platform of a station.
@ TO_BUILDINGS
company buildings - depots, stations, HQ, ...
TransportType
Available types of transport.
@ TRANSPORT_RAIL
Transport by train.
@ TRANSPORT_ROAD
Transport by road vehicle.
@ TRANSPORT_WATER
Transport over water.
Functions that have tunnels and bridges in common.
CommandCost EnsureNoVehicleOnGround(TileIndex tile)
Ensure there is no vehicle at the ground at the given position.
Definition vehicle.cpp:528
@ TrainSlowing
Train is slowing down.
Functions related to vehicles.
@ VEH_INVALID
Non-existing type of vehicle.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
Functions and type for generating vehicle lists.
void FindVehiclesWithOrder(VehiclePredicate veh_pred, OrderPredicate ord_pred, VehicleFunc veh_func)
Find vehicles matching an order.
void OffsetGroundSprite(int x, int y)
Called when a foundation has been drawn for the current tile.
Definition viewport.cpp:591
void SetViewportStationRect(const Station *st, bool sel)
Select or deselect station for rectangle area highlight.
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:759
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int z, const SpriteBounds &bounds, bool transparent, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:658
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:769
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:579
Functions related to (drawing on) viewports.
static const int TILE_HEIGHT_STEP
One Z unit tile height difference is displayed as 50m.
Declarations for accessing the k-d tree of viewports.
Functions related to water management.
void TileLoop_Water(TileIndex tile)
Let a water tile floods its diagonal adjoining tiles called from tunnelbridge_cmd,...
void CheckForDockingTile(TileIndex t)
Mark the supplied tile as a docking tile if it is suitable for docking.
bool HasTileWaterGround(Tile t)
Checks whether the tile has water at the ground.
Definition water_map.h:352
bool IsTileOnWater(Tile t)
Tests if the tile was built on water.
Definition water_map.h:138
WaterClass
classes of water (for WaterTileType::Clear water tile type).
Definition water_map.h:39
@ Invalid
Used for industry tiles on land (also for oilrig if newgrf says so).
Definition water_map.h:43
@ Canal
Canal.
Definition water_map.h:41
@ Sea
Sea.
Definition water_map.h:40
bool HasTileWaterClass(Tile t)
Checks whether the tile has an waterclass associated.
Definition water_map.h:103
WaterClass GetWaterClass(Tile t)
Get the water class at a tile.
Definition water_map.h:114
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:373
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:363
bool IsWater(Tile t)
Is it a plain water tile?
Definition water_map.h:149
bool IsWaterTile(Tile t)
Is it a water tile with plain water?
Definition water_map.h:192
Base of waypoints.
@ WPF_ROAD
This is a road waypoint.
CommandCost RemoveBuoy(TileIndex tile, DoCommandFlags flags)
Remove a buoy.
Command definitions related to waypoints.
Functions related to waypoints.
void ShowWaypointWindow(const Waypoint *wp)
Show the window for the given waypoint.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1198
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3218
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:3310
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:3204
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3188
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:3328
@ WC_STATION_LIST
Station list; Window numbers:
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_SELECT_STATION
Select station (when joining stations); Window numbers:
@ WC_STATION_VIEW
Station view; Window numbers:
@ WC_TOWN_VIEW
Town view; Window numbers:
Entry point for OpenTTD to YAPF's cache.
void YapfNotifyTrackLayoutChange(TileIndex tile, Track track)
Use this function to notify YAPF that track layout (or signal configuration) has change.
@ Station
station encountered (could be a target next time)
Definition yapf_type.hpp:26
@ RoadVehicle
Default zoom level for the road vehicle view.
Definition zoom_type.h:41