OpenTTD Source 20251005-master-ga617d009cc
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 <http://www.gnu.org/licenses/>.
6 */
7
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 "strings_func.h"
33#include "clear_func.h"
35#include "vehicle_func.h"
36#include "string_func.h"
37#include "animated_tile_func.h"
38#include "elrail_func.h"
39#include "station_base.h"
40#include "station_func.h"
41#include "station_kdtree.h"
42#include "roadstop_base.h"
43#include "newgrf_railtype.h"
44#include "newgrf_roadtype.h"
45#include "waypoint_base.h"
46#include "waypoint_func.h"
47#include "pbs.h"
48#include "debug.h"
49#include "core/random_func.hpp"
51#include "company_base.h"
53#include "newgrf_airporttiles.h"
54#include "order_backup.h"
55#include "newgrf_house.h"
56#include "company_gui.h"
58#include "linkgraph/refresh.h"
59#include "tunnelbridge_map.h"
60#include "station_cmd.h"
61#include "waypoint_cmd.h"
62#include "landscape_cmd.h"
63#include "rail_cmd.h"
64#include "newgrf_roadstop.h"
65#include "timer/timer.h"
69#include "cheat_type.h"
70#include "road_func.h"
71#include "station_layout_type.h"
72
74
75#include "table/strings.h"
76#include "table/station_land.h"
77
78#include <bitset>
79
80#include "safeguards.h"
81
87/* static */ const FlowStat::SharesMap FlowStat::empty_sharesmap;
88
96{
97 assert(IsTileType(t, MP_STATION));
98
99 /* If the tile isn't an airport there's no chance it's a hangar. */
100 if (!IsAirport(t)) return false;
101
102 const Station *st = Station::GetByTile(t);
103 const AirportSpec *as = st->airport.GetSpec();
104
105 for (const auto &depot : as->depots) {
106 if (st->airport.GetRotatedTileFromOffset(depot.ti) == TileIndex(t)) return true;
107 }
108
109 return false;
110}
111
121template <class T, class F>
122CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
123{
124 ta.Expand(1);
125
126 /* check around to see if there are any stations there owned by the company */
127 for (TileIndex tile_cur : ta) {
128 if (IsTileType(tile_cur, MP_STATION)) {
129 StationID t = GetStationIndex(tile_cur);
130 if (!T::IsValidID(t) || T::Get(t)->owner != company || !filter(T::Get(t))) continue;
131 if (closest_station == StationID::Invalid()) {
132 closest_station = t;
133 } else if (closest_station != t) {
134 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
135 }
136 }
137 }
138 *st = (closest_station == StationID::Invalid()) ? nullptr : T::Get(closest_station);
139 return CommandCost();
140}
141
147typedef bool (*CMSAMatcher)(TileIndex tile);
148
156{
157 int num = 0;
158
159 for (int dx = -3; dx <= 3; dx++) {
160 for (int dy = -3; dy <= 3; dy++) {
161 TileIndex t = TileAddWrap(tile, dx, dy);
162 if (t != INVALID_TILE && cmp(t)) num++;
163 }
164 }
165
166 return num;
167}
168
174static bool CMSAMine(TileIndex tile)
175{
176 /* No industry */
177 if (!IsTileType(tile, MP_INDUSTRY)) return false;
178
179 const Industry *ind = Industry::GetByTile(tile);
180
181 /* No extractive industry */
183
184 for (const auto &p : ind->produced) {
185 /* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine.
186 * Also the production of passengers and mail is ignored. */
187 if (IsValidCargoType(p.cargo) &&
189 return true;
190 }
191 }
192
193 return false;
194}
195
201static bool CMSAWater(TileIndex tile)
202{
203 return IsTileType(tile, MP_WATER) && IsWater(tile);
204}
205
211static bool CMSATree(TileIndex tile)
212{
213 return IsTileType(tile, MP_TREES);
214}
215
216enum StationNaming : uint8_t {
217 STATIONNAMING_RAIL,
218 STATIONNAMING_ROAD,
219 STATIONNAMING_AIRPORT,
220 STATIONNAMING_OILRIG,
221 STATIONNAMING_DOCK,
222 STATIONNAMING_HELIPORT,
223};
224
227 std::bitset<STR_SV_STNAME_FALLBACK - STR_SV_STNAME> used_names;
228 std::bitset<NUM_INDUSTRYTYPES> indtypes;
229
230 bool IsAvailable(StringID str) const
231 {
232 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
233 return !this->used_names.test(str - STR_SV_STNAME);
234 }
235
236 void SetUsed(StringID str)
237 {
238 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
239 this->used_names.set(str - STR_SV_STNAME);
240 }
241};
242
243static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
244{
245 const Town *t = st->town;
246
248
249 for (const Station *s : Station::Iterate()) {
250 if (s != st && s->town == t) {
251 if (s->indtype != IT_INVALID) {
252 sni.indtypes[s->indtype] = true;
253 StringID name = GetIndustrySpec(s->indtype)->station_name;
254 if (name != STR_UNDEFINED) {
255 /* Filter for other industrytypes with the same name */
256 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
257 const IndustrySpec *indsp = GetIndustrySpec(it);
258 if (indsp->enabled && indsp->station_name == name) sni.indtypes[it] = true;
259 }
260 }
261 continue;
262 }
263 if (IsInsideMM(s->string_id, STR_SV_STNAME, STR_SV_STNAME_FALLBACK)) {
264 auto str = s->string_id;
265 if (str == STR_SV_STNAME_FOREST) str = STR_SV_STNAME_WOODS;
266 sni.SetUsed(str);
267 }
268 }
269 }
270
271 for (auto indtile : SpiralTileSequence(tile, 7)) {
272 if (!IsTileType(indtile, MP_INDUSTRY)) continue;
273
274 /* If the station name is undefined it means that it doesn't name a station */
275 IndustryType indtype = GetIndustryType(indtile);
276 const IndustrySpec *indsp = GetIndustrySpec(indtype);
277 if (indsp->station_name == STR_UNDEFINED) continue;
278
279 /* In all cases if an industry that provides a name is found two of
280 * the standard names will be disabled. */
281 sni.SetUsed(STR_SV_STNAME_OILFIELD);
282 sni.SetUsed(STR_SV_STNAME_MINES);
283 if (sni.indtypes[indtype]) continue;
284
285 /* STR_NULL means it only disables oil rig/mines */
286 if (indsp->station_name != STR_NULL) {
287 st->indtype = indtype;
288 return STR_SV_STNAME_FALLBACK;
289 }
290 break;
291 }
292
293 /* check default names
294 * Oil rigs/mines name could be marked not free by looking for a near by industry. */
295 switch (name_class) {
296 case STATIONNAMING_AIRPORT:
297 if (sni.IsAvailable(STR_SV_STNAME_AIRPORT)) return STR_SV_STNAME_AIRPORT;
298 break;
299 case STATIONNAMING_OILRIG:
300 if (sni.IsAvailable(STR_SV_STNAME_OILFIELD)) return STR_SV_STNAME_OILFIELD;
301 break;
302 case STATIONNAMING_DOCK:
303 if (sni.IsAvailable(STR_SV_STNAME_DOCKS)) return STR_SV_STNAME_DOCKS;
304 break;
305 case STATIONNAMING_HELIPORT:
306 if (sni.IsAvailable(STR_SV_STNAME_HELIPORT)) return STR_SV_STNAME_HELIPORT;
307 break;
308 default:
309 break;
310 };
311
312 /* check mine? */
313 if (sni.IsAvailable(STR_SV_STNAME_MINES)) {
314 if (CountMapSquareAround(tile, CMSAMine) >= 2) {
315 return STR_SV_STNAME_MINES;
316 }
317 }
318
319 /* check close enough to town to get central as name? */
320 if (DistanceMax(tile, t->xy) < 8) {
321 if (sni.IsAvailable(STR_SV_STNAME)) return STR_SV_STNAME;
322 if (sni.IsAvailable(STR_SV_STNAME_CENTRAL)) return STR_SV_STNAME_CENTRAL;
323 }
324
325 /* Check lakeside */
326 if (sni.IsAvailable(STR_SV_STNAME_LAKESIDE) &&
327 DistanceFromEdge(tile) < 20 &&
328 CountMapSquareAround(tile, CMSAWater) >= 5) {
329 return STR_SV_STNAME_LAKESIDE;
330 }
331
332 /* Check woods */
333 if (sni.IsAvailable(STR_SV_STNAME_WOODS) && (
334 CountMapSquareAround(tile, CMSATree) >= 8 ||
336 ) {
337 return _settings_game.game_creation.landscape == LandscapeType::Tropic ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
338 }
339
340 /* check elevation compared to town */
341 int z = GetTileZ(tile);
342 int z2 = GetTileZ(t->xy);
343 if (z < z2) {
344 if (sni.IsAvailable(STR_SV_STNAME_VALLEY)) return STR_SV_STNAME_VALLEY;
345 } else if (z > z2) {
346 if (sni.IsAvailable(STR_SV_STNAME_HEIGHTS)) return STR_SV_STNAME_HEIGHTS;
347 }
348
349 /* check direction compared to town */
350 if (TileX(tile) < TileX(t->xy)) {
351 sni.SetUsed(STR_SV_STNAME_SOUTH);
352 sni.SetUsed(STR_SV_STNAME_WEST);
353 } else {
354 sni.SetUsed(STR_SV_STNAME_NORTH);
355 sni.SetUsed(STR_SV_STNAME_EAST);
356 }
357 if (TileY(tile) < TileY(t->xy)) {
358 sni.SetUsed(STR_SV_STNAME_SOUTH);
359 sni.SetUsed(STR_SV_STNAME_EAST);
360 } else {
361 sni.SetUsed(STR_SV_STNAME_NORTH);
362 sni.SetUsed(STR_SV_STNAME_WEST);
363 }
364
366 static const StringID fallback_names[] = {
367 STR_SV_STNAME_NORTH,
368 STR_SV_STNAME_SOUTH,
369 STR_SV_STNAME_EAST,
370 STR_SV_STNAME_WEST,
371 STR_SV_STNAME_TRANSFER,
372 STR_SV_STNAME_HALT,
373 STR_SV_STNAME_EXCHANGE,
374 STR_SV_STNAME_ANNEXE,
375 STR_SV_STNAME_SIDINGS,
376 STR_SV_STNAME_BRANCH,
377 STR_SV_STNAME_UPPER,
378 STR_SV_STNAME_LOWER,
379 };
380 for (auto str : fallback_names) {
381 if (sni.IsAvailable(str)) return str;
382 }
383 return STR_SV_STNAME_FALLBACK;
384}
385
392{
393 uint threshold = 8;
394
395 Station *best_station = nullptr;
396 ForAllStationsRadius(tile, threshold, [&](Station *st) {
397 if (!st->IsInUse() && st->owner == _current_company) {
398 uint cur_dist = DistanceManhattan(tile, st->xy);
399
400 if (cur_dist < threshold) {
401 threshold = cur_dist;
402 best_station = st;
403 } else if (cur_dist == threshold && best_station != nullptr) {
404 /* In case of a tie, lowest station ID wins */
405 if (st->index < best_station->index) best_station = st;
406 }
407 }
408 });
409
410 return best_station;
411}
412
413
415{
416 switch (type) {
417 case StationType::Rail: return this->train_station;
418 case StationType::Airport: return this->airport;
419 case StationType::Truck: return this->truck_station;
420 case StationType::Bus: return this->bus_station;
421 case StationType::Dock:
422 case StationType::Oilrig: return this->docking_station;
423 default: NOT_REACHED();
424 }
425}
426
431{
432 Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
433
434 pt.y -= 32 * ZOOM_BASE;
435 if (this->facilities.Test(StationFacility::Airport) && this->airport.type == AT_OILRIG) pt.y -= 16 * ZOOM_BASE;
436
437 if (this->sign.kdtree_valid) _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeStation(this->index));
438
439 this->sign.UpdatePosition(pt.x, pt.y, GetString(STR_VIEWPORT_STATION, this->index, this->facilities), GetString(STR_STATION_NAME, this->index, this->facilities));
440
441 _viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeStation(this->index));
442
444}
445
451{
452 if (this->xy == new_xy) return;
453
454 _station_kdtree.Remove(this->index);
455
456 this->BaseStation::MoveSign(new_xy);
457
458 _station_kdtree.Insert(this->index);
459}
460
463{
464 for (BaseStation *st : BaseStation::Iterate()) {
465 st->UpdateVirtCoord();
466 }
467}
468
469void BaseStation::FillCachedName() const
470{
471 auto tmp_params = MakeParameters(this->index);
472 this->cached_name = GetStringWithArgs(Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params);
473}
474
475void ClearAllStationCachedNames()
476{
477 for (BaseStation *st : BaseStation::Iterate()) {
478 st->cached_name.clear();
479 }
480}
481
487CargoTypes GetAcceptanceMask(const Station *st)
488{
489 CargoTypes mask = 0;
490
491 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
492 if (it->status.Test(GoodsEntry::State::Acceptance)) SetBit(mask, std::distance(std::begin(st->goods), it));
493 }
494 return mask;
495}
496
502CargoTypes GetEmptyMask(const Station *st)
503{
504 CargoTypes mask = 0;
505
506 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
507 if (!it->HasData() || it->GetData().cargo.TotalCount() == 0) SetBit(mask, std::distance(std::begin(st->goods), it));
508 }
509 return mask;
510}
511
518static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
519{
520 StringID msg = reject ? STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_LIST : STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST;
522}
523
531CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
532{
533 CargoArray produced{};
534 FlatSet<IndustryID> industries;
535 TileArea ta = TileArea(north_tile, w, h).Expand(rad);
536
537 /* Loop over all tiles to get the produced cargo of
538 * everything except industries */
539 for (TileIndex tile : ta) {
540 if (IsTileType(tile, MP_INDUSTRY)) industries.insert(GetIndustryIndex(tile));
541 AddProducedCargo(tile, produced);
542 }
543
544 /* Loop over the seen industries. They produce cargo for
545 * anything that is within 'rad' of any one of their tiles.
546 */
547 for (IndustryID industry : industries) {
548 const Industry *i = Industry::Get(industry);
549 /* Skip industry with neutral station */
550 if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) continue;
551
552 for (const auto &p : i->produced) {
553 if (IsValidCargoType(p.cargo)) produced[p.cargo]++;
554 }
555 }
556
557 return produced;
558}
559
569CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted)
570{
571 CargoArray acceptance{};
572 if (always_accepted != nullptr) *always_accepted = 0;
573
574 TileArea ta = TileArea(center_tile, w, h).Expand(rad);
575
576 for (TileIndex tile : ta) {
577 /* Ignore industry if it has a neutral station. */
579
580 AddAcceptedCargo(tile, acceptance, always_accepted);
581 }
582
583 return acceptance;
584}
585
591static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
592{
593 CargoArray acceptance{};
594 if (always_accepted != nullptr) *always_accepted = 0;
595
597 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
598 AddAcceptedCargo(tile, acceptance, always_accepted);
599 }
600
601 return acceptance;
602}
603
609void UpdateStationAcceptance(Station *st, bool show_msg)
610{
611 /* old accepted goods types */
612 CargoTypes old_acc = GetAcceptanceMask(st);
613
614 /* And retrieve the acceptance. */
615 CargoArray acceptance{};
616 if (!st->rect.IsEmpty()) {
617 acceptance = GetAcceptanceAroundStation(st, &st->always_accepted);
618 }
619
620 /* Adjust in case our station only accepts fewer kinds of goods */
621 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
622 uint amt = acceptance[cargo];
623
624 /* Make sure the station can accept the goods type. */
625 bool is_passengers = IsCargoInClass(cargo, CargoClass::Passengers);
626 if ((!is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::TruckStop, StationFacility::Airport, StationFacility::Dock})) ||
627 (is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::BusStop, StationFacility::Airport, StationFacility::Dock}))) {
628 amt = 0;
629 }
630
631 GoodsEntry &ge = st->goods[cargo];
634 (*LinkGraph::Get(ge.link_graph))[ge.node].SetDemand(amt / 8);
635 }
636 }
637
638 /* Only show a message in case the acceptance was actually changed. */
639 CargoTypes new_acc = GetAcceptanceMask(st);
640 if (old_acc == new_acc) return;
641
642 /* show a message to report that the acceptance was changed? */
643 if (show_msg && st->owner == _local_company && st->IsInUse()) {
644 /* Combine old and new masks to get changes */
645 CargoTypes accepts = new_acc & ~old_acc;
646 CargoTypes rejects = ~new_acc & old_acc;
647
648 /* Show news message if there are any changes */
649 if (accepts != 0) ShowRejectOrAcceptNews(st, accepts, false);
650 if (rejects != 0) ShowRejectOrAcceptNews(st, rejects, true);
651 }
652
653 /* redraw the station view since acceptance changed */
655}
656
657static void UpdateStationSignCoord(BaseStation *st)
658{
659 const StationRect *r = &st->rect;
660
661 if (r->IsEmpty()) return; // no tiles belong to this station
662
663 /* clamp sign coord to be inside the station rect */
664 TileIndex new_xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
665 st->MoveSign(new_xy);
666
667 if (!Station::IsExpected(st)) return;
668 Station *full_station = Station::From(st);
669 for (const GoodsEntry &ge : full_station->goods) {
670 LinkGraphID lg = ge.link_graph;
671 if (!LinkGraph::IsValidID(lg)) continue;
672 (*LinkGraph::Get(lg))[ge.node].UpdateLocation(st->xy);
673 }
674}
675
685static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
686{
687 /* Find a deleted station close to us */
688 if (*st == nullptr && reuse) *st = GetClosestDeletedStation(area.tile);
689
690 if (*st != nullptr) {
691 if ((*st)->owner != _current_company) {
692 return CommandCost(CMD_ERROR);
693 }
694
695 CommandCost ret = (*st)->rect.BeforeAddRect(area.tile, area.w, area.h, StationRect::ADD_TEST);
696 if (ret.Failed()) return ret;
697 } else {
698 /* allocate and initialize new station */
699 if (!Station::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_STATIONS_LOADING);
700
701 if (flags.Test(DoCommandFlag::Execute)) {
702 *st = new Station(area.tile);
703 _station_kdtree.Insert((*st)->index);
704
705 (*st)->town = ClosestTownFromTile(area.tile, UINT_MAX);
706 (*st)->string_id = GenerateStationName(*st, area.tile, name_class);
707
709 (*st)->town->have_ratings.Set(_current_company);
710 }
711 }
712 }
713 return CommandCost();
714}
715
723{
724 if (!st->IsInUse()) {
725 st->delete_ctr = 0;
727 }
728 /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
729 UpdateStationSignCoord(st);
730}
731
738{
739 this->UpdateVirtCoord();
741
742 if (adding) {
743 this->RecomputeCatchment();
744 MarkCatchmentTilesDirty();
746 } else {
747 MarkCatchmentTilesDirty();
748 }
749
750 switch (type) {
751 case StationType::Rail:
753 break;
754 case StationType::Airport:
755 break;
756 case StationType::Truck:
757 case StationType::Bus:
759 break;
760 case StationType::Dock:
762 break;
763 default: NOT_REACHED();
764 }
765
766 if (adding) {
767 UpdateStationAcceptance(this, false);
769 } else {
771 this->RecomputeCatchment();
772 }
773
774}
775
777
787CommandCost CheckBuildableTile(TileIndex tile, DiagDirections invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge = true)
788{
789 if (check_bridge && IsBridgeAbove(tile)) {
790 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
791 }
792
794 if (ret.Failed()) return ret;
795
796 auto [tileh, z] = GetTileSlopeZ(tile);
797
798 /* Prohibit building if
799 * 1) The tile is "steep" (i.e. stretches two height levels).
800 * 2) The tile is non-flat and the build_on_slopes switch is disabled.
801 */
802 if ((!allow_steep && IsSteepSlope(tileh)) ||
804 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
805 }
806
808 int flat_z = z + GetSlopeMaxZ(tileh);
809 if (tileh != SLOPE_FLAT) {
810 /* Forbid building if the tile faces a slope in a invalid direction. */
811 for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) {
812 if (invalid_dirs.Test(dir) && !CanBuildDepotByTileh(dir, tileh)) {
813 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
814 }
815 }
816 cost.AddCost(_price[PR_BUILD_FOUNDATION]);
817 }
818
819 /* The level of this tile must be equal to allowed_z. */
820 if (allowed_z < 0) {
821 /* First tile. */
822 allowed_z = flat_z;
823 } else if (allowed_z != flat_z) {
824 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
825 }
826
827 return cost;
828}
829
837{
839 int allowed_z = -1;
840
841 for (; tile_iter != INVALID_TILE; ++tile_iter) {
842 CommandCost ret = CheckBuildableTile(tile_iter, {}, allowed_z, true);
843 if (ret.Failed()) return ret;
844 cost.AddCost(ret.GetCost());
845
846 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_iter);
847 if (ret.Failed()) return ret;
848 cost.AddCost(ret.GetCost());
849 }
850
851 return cost;
852}
853
860{
861 static constexpr std::array<StringID, to_underlying(StationType::End)> too_low_msgs = {
862 STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION, // Rail
863 INVALID_STRING_ID, // Airport
864 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Truck
865 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Bus
866 INVALID_STRING_ID, // Oilrig
867 STR_ERROR_BRIDGE_TOO_LOW_FOR_DOCK, // Dock
868 STR_ERROR_BRIDGE_TOO_LOW_FOR_BUOY, // Buoy
869 STR_ERROR_BRIDGE_TOO_LOW_FOR_RAIL_WAYPOINT, // RailWaypoint
870 STR_ERROR_BRIDGE_TOO_LOW_FOR_ROAD_WAYPOINT, // RoadWaypoint
871 };
872 return too_low_msgs[to_underlying(type)];
873};
874
885static CommandCost IsStationBridgeAboveOk(TileIndex tile, std::span<const BridgeableTileInfo> bridgeable_info, StationType type, StationGfx layout, int bridge_height, StringID disallowed_msg = INVALID_STRING_ID)
886{
887 int height = layout < std::size(bridgeable_info) ? bridgeable_info[layout].height : 0;
888
889 if (height == 0) {
890 if (disallowed_msg != INVALID_STRING_ID) return CommandCost{disallowed_msg};
891 /* Get normal error message associated with clearing the tile. */
893 }
894 if (GetTileMaxZ(tile) + height > bridge_height) return CommandCost{GetBridgeTooLowMessageForStationType(type)};
895
896 return CommandCost{};
897}
898
904static std::span<const BridgeableTileInfo> GetStationBridgeableTileInfo(StationType type)
905{
906 return _station_bridgeable_info[to_underlying(type)];
907}
908
918{
919 if (!IsBridgeAbove(tile)) return CommandCost();
920
921 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
922 auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info;
923 return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
924}
925
935{
936 if (!IsBridgeAbove(tile)) return CommandCost();
937
938 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
939 auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info;
940 return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
941}
942
950{
951 if (!IsBridgeAbove(tile)) return CommandCost();
952
953 TileIndex rampsouth = GetSouthernBridgeEnd(tile);
954 auto bridgeable_info = GetStationBridgeableTileInfo(StationType::Dock);
955 return IsStationBridgeAboveOk(tile, bridgeable_info, StationType::Dock, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
956}
957
974static 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)
975{
977 DiagDirections invalid_dirs = AxisToDiagDirs(axis);
978
979 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
980 bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
981
982 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, false);
983 if (ret.Failed()) return ret;
984 cost.AddCost(ret.GetCost());
985
986 if (slope_cb) {
987 /* Do slope check if requested. */
988 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
989 if (ret.Failed()) return ret;
990 }
991
992 /* if station is set, then we have special handling to allow building on top of already existing stations.
993 * so station points to StationID::Invalid() if we can build on any station.
994 * Or it points to a station if we're only allowed to build on exactly that station. */
995 if (station != nullptr && IsTileType(tile_cur, MP_STATION)) {
996 if (!IsRailStation(tile_cur)) {
997 return ClearTile_Station(tile_cur, DoCommandFlag::Auto); // get error message
998 } else {
999 StationID st = GetStationIndex(tile_cur);
1000 if (*station == StationID::Invalid()) {
1001 *station = st;
1002 } else if (*station != st) {
1003 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1004 }
1005 }
1006 } else {
1007 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
1008 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
1009 /* Don't overbuild signals. */
1010 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
1011
1012 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
1013 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
1014 TrackBits tracks = GetTrackBits(tile_cur);
1015 Track track = RemoveFirstTrack(&tracks);
1016 Track expected_track = invalid_dirs.Test(DIAGDIR_NE) ? TRACK_X : TRACK_Y;
1017
1018 /* The existing track must align with the desired station axis. */
1019 if (tracks == TRACK_BIT_NONE && track == expected_track) {
1020 /* Check for trains having a reservation for this tile. */
1021 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
1022 Train *v = GetTrainForReservation(tile_cur, track);
1023 if (v != nullptr) {
1024 affected_vehicles.push_back(v);
1025 }
1026 }
1027 ret = Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile_cur, track);
1028 if (ret.Failed()) return ret;
1029 cost.AddCost(ret.GetCost());
1030 /* With DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) CmdLandscapeClear would fail since the rail still exists */
1031 return cost;
1032 }
1033 }
1034 }
1035 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
1036 if (ret.Failed()) return ret;
1037 cost.AddCost(ret.GetCost());
1038 }
1039
1040 return cost;
1041}
1042
1057static 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)
1058{
1060
1061 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, false);
1062 if (ret.Failed()) return ret;
1063 cost.AddCost(ret.GetCost());
1064
1065 ret = IsRoadStationBridgeAboveOk(cur_tile, spec, station_type, is_drive_through ? GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + axis : FindFirstBit(invalid_dirs.base()));
1066 if (ret.Failed()) return ret;
1067
1068 /* If station is set, then we have special handling to allow building on top of already existing stations.
1069 * Station points to StationID::Invalid() if we can build on any station.
1070 * Or it points to a station if we're only allowed to build on exactly that station. */
1071 if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
1072 if (!IsAnyRoadStop(cur_tile)) {
1073 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1074 } else {
1075 if (station_type != GetStationType(cur_tile) ||
1076 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
1077 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1078 }
1079 /* Drive-through station in the wrong direction. */
1080 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
1081 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1082 }
1083 StationID st = GetStationIndex(cur_tile);
1084 if (*station == StationID::Invalid()) {
1085 *station = st;
1086 } else if (*station != st) {
1087 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1088 }
1089 }
1090 } else {
1091 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
1092 /* Road bits in the wrong direction. */
1093 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
1094 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
1095 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1096 switch (CountBits(rb)) {
1097 case 1:
1098 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1099
1100 case 2:
1101 if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1102 return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
1103
1104 default: // 3 or 4
1105 return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
1106 }
1107 }
1108
1109 if (build_over_road) {
1110 /* There is a road, check if we can build road+tram stop over it. */
1111 RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
1112 if (road_rt != INVALID_ROADTYPE) {
1113 Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
1114 if (road_owner == OWNER_TOWN) {
1115 if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
1117 ret = CheckOwnership(road_owner);
1118 if (ret.Failed()) return ret;
1119 }
1120 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
1121
1122 if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1123
1124 if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
1125 ret = CheckOwnership(road_owner);
1126 if (ret.Failed()) return ret;
1127 }
1128
1129 cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
1130 } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
1131 cost.AddCost(RoadBuildCost(rt) * 2);
1132 }
1133
1134 /* There is a tram, check if we can build road+tram stop over it. */
1135 RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
1136 if (tram_rt != INVALID_ROADTYPE) {
1137 Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
1138 if (Company::IsValidID(tram_owner) &&
1140 /* Disallow breaking end-of-line of someone else
1141 * so trams can still reverse on this tile. */
1142 HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
1143 ret = CheckOwnership(tram_owner);
1144 if (ret.Failed()) return ret;
1145 }
1146 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
1147
1148 if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1149
1150 cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
1151 } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
1152 cost.AddCost(RoadBuildCost(rt) * 2);
1153 }
1154 } else if (rt == INVALID_ROADTYPE) {
1155 return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
1156 } else {
1157 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
1158 if (ret.Failed()) return ret;
1159 cost.AddCost(ret.GetCost());
1160 cost.AddCost(RoadBuildCost(rt) * 2);
1161 }
1162 }
1163
1164 return cost;
1165}
1166
1174{
1175 TileArea cur_ta = st->train_station;
1176
1177 /* determine new size of train station region.. */
1178 int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
1179 int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
1180 new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
1181 new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
1182 new_ta.tile = TileXY(x, y);
1183
1184 /* make sure the final size is not too big. */
1186 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1187 }
1188
1189 return CommandCost();
1190}
1191
1192RailStationTileLayout::RailStationTileLayout(const StationSpec *spec, uint8_t platforms, uint8_t length) : platforms(platforms), length(length)
1193{
1194 if (spec == nullptr) return;
1195
1196 /* Look for a predefined layout for the required size. */
1197 auto found = spec->layouts.find(GetStationLayoutKey(platforms, length));
1198 if (found != std::end(spec->layouts)) this->layout = found->second;
1199}
1200
1201StationGfx RailStationTileLayout::Iterator::operator*() const
1202{
1203 /* Use predefined layout if it exists. Mask bit zero which will indicate axis. */
1204 if (!stl.layout.empty()) return this->stl.layout[this->position] & ~1;
1205
1206 if (this->stl.length == 1) {
1207 /* Special case for 1-long platforms, all bare platforms except one small building. */
1208 return this->position == ((this->stl.platforms - 1) / 2) ? 2 : 0;
1209 }
1210
1211 if ((this->position < this->stl.length && (this->stl.platforms % 2 == 1))) {
1212 /* Number of tracks is odd, make the first platform bare with a small building. */
1213 return this->position == ((this->stl.length - 1) / 2) ? 2 : 0;
1214 }
1215
1216 if (this->stl.length > 4 && ((this->position % this->stl.length) == 0 || (this->position % this->stl.length) == this->stl.length - 1)) {
1217 /* Station is longer than 4 tiles, place bare platforms at either end. */
1218 return 0;
1219 }
1220
1221 /* None of the above so must be north or south part of larger station. */
1222 return (((this->position / this->stl.length) % 2) == (this->stl.platforms % 2)) ? 4 : 6;
1223}
1224
1237template <class T, StringID error_message, class F>
1238CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1239{
1240 assert(*st == nullptr);
1241 bool check_surrounding = true;
1242
1243 if (existing_station != StationID::Invalid()) {
1244 if (adjacent && existing_station != station_to_join) {
1245 /* You can't build an adjacent station over the top of one that
1246 * already exists. */
1247 return CommandCost(error_message);
1248 } else {
1249 /* Extend the current station, and don't check whether it will
1250 * be near any other stations. */
1251 T *candidate = T::GetIfValid(existing_station);
1252 if (candidate != nullptr && filter(candidate)) *st = candidate;
1253 check_surrounding = (*st == nullptr);
1254 }
1255 } else {
1256 /* There's no station here. Don't check the tiles surrounding this
1257 * one if the company wanted to build an adjacent station. */
1258 if (adjacent) check_surrounding = false;
1259 }
1260
1261 if (check_surrounding) {
1262 /* Make sure there is no more than one other station around us that is owned by us. */
1263 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1264 if (ret.Failed()) return ret;
1265 }
1266
1267 /* Distant join */
1268 if (*st == nullptr && station_to_join != StationID::Invalid()) *st = T::GetIfValid(station_to_join);
1269
1270 return CommandCost();
1271}
1272
1282static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1283{
1284 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1285}
1286
1297CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1298{
1299 if (is_road) {
1300 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); });
1301 } else {
1302 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); });
1303 }
1304}
1305
1317
1329
1344static 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)
1345{
1347 bool length_price_ready = true;
1348 uint8_t tracknum = 0;
1349 int allowed_z = -1;
1350 for (TileIndex cur_tile : tile_area) {
1351 /* Clear the land below the station. */
1352 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1353 if (ret.Failed()) return ret;
1354
1355 /* Only add _price[PR_BUILD_STATION_RAIL_LENGTH] once for each valid plat_len. */
1356 if (tracknum == numtracks) {
1357 length_price_ready = true;
1358 tracknum = 0;
1359 } else {
1360 tracknum++;
1361 }
1362
1363 /* AddCost for new or rotated rail stations. */
1364 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1365 cost.AddCost(ret.GetCost());
1366 cost.AddCost(_price[PR_BUILD_STATION_RAIL]);
1367 cost.AddCost(RailBuildCost(rt));
1368
1369 if (length_price_ready) {
1370 cost.AddCost(_price[PR_BUILD_STATION_RAIL_LENGTH]);
1371 length_price_ready = false;
1372 }
1373 }
1374 }
1375
1376 return cost;
1377}
1378
1386{
1387 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1388 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlag::Pylons : StationSpec::TileFlags{};
1389 return statspec->tileflags[gfx];
1390}
1391
1398{
1399 const auto flags = GetStationTileFlags(GetStationGfx(tile), statspec);
1403}
1404
1419CommandCost 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)
1420{
1421 /* Does the authority allow this? */
1422 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1423 if (ret.Failed()) return ret;
1424
1425 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1426
1427 /* Check if the given station class is valid */
1428 if (static_cast<uint>(spec_class) >= StationClass::GetClassCount()) return CMD_ERROR;
1429 const StationClass *cls = StationClass::Get(spec_class);
1430 if (IsWaypointClass(*cls)) return CMD_ERROR;
1431 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1432 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1433
1434 int w_org, h_org;
1435 if (axis == AXIS_X) {
1436 w_org = plat_len;
1437 h_org = numtracks;
1438 } else {
1439 h_org = plat_len;
1440 w_org = numtracks;
1441 }
1442
1443 /* Check if the first tile and the last tile are valid */
1444 if (!IsValidTile(tile_org) || TileAddWrap(tile_org, w_org - 1, h_org - 1) == INVALID_TILE) return CMD_ERROR;
1445
1446 bool reuse = (station_to_join != NEW_STATION);
1447 if (!reuse) station_to_join = StationID::Invalid();
1448 bool distant_join = (station_to_join != StationID::Invalid());
1449
1450 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1451
1453
1454 /* these values are those that will be stored in train_tile and station_platforms */
1455 TileArea new_location(tile_org, w_org, h_org);
1456
1457 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1458 StationID est = StationID::Invalid();
1459 std::vector<Train *> affected_vehicles;
1460 /* Add construction and clearing expenses. */
1461 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1462 if (cost.Failed()) return cost;
1463
1464 Station *st = nullptr;
1465 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1466 if (ret.Failed()) return ret;
1467
1468 ret = BuildStationPart(&st, flags, reuse, new_location, STATIONNAMING_RAIL);
1469 if (ret.Failed()) return ret;
1470
1471 if (st != nullptr && st->train_station.tile != INVALID_TILE) {
1472 ret = CanExpandRailStation(st, new_location);
1473 if (ret.Failed()) return ret;
1474 }
1475
1476 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1477 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1478 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1479
1480 RailStationTileLayout stl{statspec, numtracks, plat_len};
1481 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1482 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1483 /* Don't check the layout if there's no bridge above anyway. */
1484 if (!IsBridgeAbove(tile)) continue;
1485
1486 StationGfx gfx = *it + axis;
1487 if (statspec != nullptr) {
1488 uint32_t platinfo = GetPlatformInfo(AXIS_X, gfx, plat_len, numtracks, j, i, false);
1489 /* As the station is not yet completely finished, the station does not yet exist. */
1490 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1491 if (callback != CALLBACK_FAILED && callback <= UINT8_MAX) gfx = (callback & ~1) + axis;
1492 }
1493
1494 ret = IsRailStationBridgeAboveOk(tile, statspec, StationType::Rail, gfx);
1495 if (ret.Failed()) return ret;
1496 }
1497 }
1498
1499 /* Check if we can allocate a custom stationspec to this station */
1500 auto specindex = AllocateSpecToStation(statspec, st);
1501 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1502
1503 if (statspec != nullptr) {
1504 /* Perform NewStation checks */
1505
1506 /* Check if the station size is permitted */
1507 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1508 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1509
1510 /* Check if the station is buildable */
1512 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1514 }
1515 }
1516
1517 if (flags.Test(DoCommandFlag::Execute)) {
1518 st->train_station = new_location;
1519 st->AddFacility(StationFacility::Train, new_location.tile);
1520
1521 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
1522
1523 if (specindex.has_value()) AssignSpecToStation(statspec, st, *specindex);
1524 if (statspec != nullptr) {
1525 /* Include this station spec's animation trigger bitmask
1526 * in the station's cached copy. */
1527 st->cached_anim_triggers.Set(statspec->animation.triggers);
1528 }
1529
1530 Track track = AxisToTrack(axis);
1531 Company *c = Company::Get(st->owner);
1532 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1533 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1534 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1535 /* Check for trains having a reservation for this tile. */
1537 if (v != nullptr) {
1538 affected_vehicles.push_back(v);
1540 }
1541 }
1542
1543 /* Railtype can change when overbuilding. */
1544 if (IsRailStationTile(tile)) {
1545 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1547 }
1548
1549 /* Remove animation if overbuilding */
1550 DeleteAnimatedTile(tile);
1551 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1552
1553 MakeRailStation(tile, st->owner, st->index, axis, *it, rt);
1554 /* Free the spec if we overbuild something */
1555 DeallocateSpecFromStation(st, old_specindex);
1556
1557 SetCustomStationSpecIndex(tile, *specindex);
1558 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1559 SetAnimationFrame(tile, 0);
1560
1561 if (statspec != nullptr) {
1562 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1563 uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks, j, i, false);
1564
1565 /* As the station is not yet completely finished, the station does not yet exist. */
1566 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1567 if (callback != CALLBACK_FAILED) {
1568 if (callback <= UINT8_MAX) {
1569 SetStationGfx(tile, (callback & ~1) + axis);
1570 } else {
1572 }
1573 }
1574
1575 /* Trigger station animation -- after building? */
1576 TriggerStationAnimation(st, tile, StationAnimationTrigger::Built);
1577 }
1578
1579 SetRailStationTileFlags(tile, statspec);
1580
1581 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1583 }
1584 AddTrackToSignalBuffer(tile_track, track, _current_company);
1585 YapfNotifyTrackLayoutChange(tile_track, track);
1586 }
1587
1588 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1589 /* Restore reservations of trains. */
1590 RestoreTrainReservation(affected_vehicles[i]);
1591 }
1592
1593 /* Check whether we need to expand the reservation of trains already on the station. */
1594 TileArea update_reservation_area;
1595 if (axis == AXIS_X) {
1596 update_reservation_area = TileArea(tile_org, 1, numtracks);
1597 } else {
1598 update_reservation_area = TileArea(tile_org, numtracks, 1);
1599 }
1600
1601 for (TileIndex tile : update_reservation_area) {
1602 /* Don't even try to make eye candy parts reserved. */
1603 if (IsStationTileBlocked(tile)) continue;
1604
1605 DiagDirection dir = AxisToDiagDir(axis);
1606 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1607 TileIndex platform_begin = tile;
1608 TileIndex platform_end = tile;
1609
1610 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1611 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1612 platform_begin = next_tile;
1613 }
1614 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1615 platform_end = next_tile;
1616 }
1617
1618 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1619 bool reservation = false;
1620 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1621 reservation = HasStationReservation(t);
1622 }
1623
1624 if (reservation) {
1625 SetRailStationPlatformReservation(platform_begin, dir, true);
1626 }
1627 }
1628
1629 st->MarkTilesDirty(false);
1630 st->AfterStationTileSetChange(true, StationType::Rail);
1631 }
1632
1633 return cost;
1634}
1635
1636static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1637{
1638restart:
1639
1640 /* too small? */
1641 if (ta.w != 0 && ta.h != 0) {
1642 /* check the left side, x = constant, y changes */
1643 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1644 /* the left side is unused? */
1645 if (++i == ta.h) {
1646 ta.tile += TileDiffXY(1, 0);
1647 ta.w--;
1648 goto restart;
1649 }
1650 }
1651
1652 /* check the right side, x = constant, y changes */
1653 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1654 /* the right side is unused? */
1655 if (++i == ta.h) {
1656 ta.w--;
1657 goto restart;
1658 }
1659 }
1660
1661 /* check the upper side, y = constant, x changes */
1662 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1663 /* the left side is unused? */
1664 if (++i == ta.w) {
1665 ta.tile += TileDiffXY(0, 1);
1666 ta.h--;
1667 goto restart;
1668 }
1669 }
1670
1671 /* check the lower side, y = constant, x changes */
1672 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1673 /* the left side is unused? */
1674 if (++i == ta.w) {
1675 ta.h--;
1676 goto restart;
1677 }
1678 }
1679 } else {
1680 ta.Clear();
1681 }
1682
1683 return ta;
1684}
1685
1686static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1687{
1688 return st->TileBelongsToRailStation(tile);
1689}
1690
1691static void MakeRailStationAreaSmaller(BaseStation *st)
1692{
1693 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1694}
1695
1696static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1697{
1698 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1699}
1700
1701static void MakeShipStationAreaSmaller(Station *st)
1702{
1703 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1704 UpdateStationDockingTiles(st);
1705}
1706
1707static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1708{
1709 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1710}
1711
1712void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1713{
1714 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1715}
1716
1727template <class T>
1728CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
1729{
1730 /* Count of the number of tiles removed */
1731 int quantity = 0;
1733 /* Accumulator for the errors seen during clearing. If no errors happen,
1734 * and the quantity is 0 there is no station. Otherwise it will be one
1735 * of the other error that got accumulated. */
1736 CommandCost error;
1737
1738 /* Do the action for every tile into the area */
1739 for (TileIndex tile : ta) {
1740 /* Make sure the specified tile is a rail station */
1741 if (!HasStationTileRail(tile)) continue;
1742
1743 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1745 error.AddCost(std::move(ret));
1746 if (error.Failed()) continue;
1747
1748 /* Check ownership of station */
1749 T *st = T::GetByTile(tile);
1750 if (st == nullptr) continue;
1751
1753 ret = CheckOwnership(st->owner);
1754 error.AddCost(std::move(ret));
1755 if (error.Failed()) continue;
1756 }
1757
1758 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1759 quantity++;
1760
1761 if (keep_rail || IsStationTileBlocked(tile)) {
1762 /* Don't refund the 'steel' of the track when we keep the
1763 * rail, or when the tile didn't have any rail at all. */
1764 total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
1765 }
1766
1767 if (flags.Test(DoCommandFlag::Execute)) {
1768 /* read variables before the station tile is removed */
1769 uint specindex = GetCustomStationSpecIndex(tile);
1770 Track track = GetRailStationTrack(tile);
1771 Owner owner = GetTileOwner(tile);
1772 RailType rt = GetRailType(tile);
1773 Train *v = nullptr;
1774
1775 if (HasStationReservation(tile)) {
1776 v = GetTrainForReservation(tile, track);
1777 if (v != nullptr) FreeTrainReservation(v);
1778 }
1779
1780 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1781 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1782
1783 DoClearSquare(tile);
1784 DeleteNewGRFInspectWindow(GSF_STATIONS, tile.base());
1785 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
1786 Company::Get(owner)->infrastructure.station--;
1788
1789 st->rect.AfterRemoveTile(st, tile);
1790 AddTrackToSignalBuffer(tile, track, owner);
1791 YapfNotifyTrackLayoutChange(tile, track);
1792
1793 DeallocateSpecFromStation(st, specindex);
1794
1795 include(affected_stations, st);
1796
1797 if (v != nullptr) RestoreTrainReservation(v);
1798 }
1799 }
1800
1801 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1802
1803 for (T *st : affected_stations) {
1804
1805 /* now we need to make the "spanned" area of the railway station smaller
1806 * if we deleted something at the edges.
1807 * we also need to adjust train_tile. */
1808 MakeRailStationAreaSmaller(st);
1809 UpdateStationSignCoord(st);
1810
1811 /* if we deleted the whole station, delete the train facility. */
1812 if (st->train_station.tile == INVALID_TILE) {
1816 MarkCatchmentTilesDirty();
1817 st->UpdateVirtCoord();
1819 }
1820 }
1821
1822 total_cost.AddCost(quantity * removal_cost);
1823 return total_cost;
1824}
1825
1836{
1837 if (end == 0) end = start;
1838 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1839
1840 TileArea ta(start, end);
1841 std::vector<Station *> affected_stations;
1842
1843 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], keep_rail);
1844 if (ret.Failed()) return ret;
1845
1846 /* Do all station specific functions here. */
1847 for (Station *st : affected_stations) {
1848
1850 st->MarkTilesDirty(false);
1851 MarkCatchmentTilesDirty();
1852 st->RecomputeCatchment();
1853 }
1854
1855 /* Now apply the rail cost to the number that we deleted */
1856 return ret;
1857}
1858
1869{
1870 if (end == 0) end = start;
1871 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1872
1873 TileArea ta(start, end);
1874 std::vector<Waypoint *> affected_stations;
1875
1876 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], keep_rail);
1877}
1878
1879
1888template <class T>
1890{
1891 /* Current company owns the station? */
1893 CommandCost ret = CheckOwnership(st->owner);
1894 if (ret.Failed()) return ret;
1895 }
1896
1897 /* determine width and height of platforms */
1898 TileArea ta = st->train_station;
1899
1900 assert(ta.w != 0 && ta.h != 0);
1901
1903 /* clear all areas of the station */
1904 for (TileIndex tile : ta) {
1905 /* only remove tiles that are actually train station tiles */
1906 if (st->TileBelongsToRailStation(tile)) {
1907 std::vector<T*> affected_stations; // dummy
1908 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1909 if (ret.Failed()) return ret;
1910 cost.AddCost(ret.GetCost());
1911 }
1912 }
1913
1914 return cost;
1915}
1916
1924{
1925 /* if there is flooding, remove platforms tile by tile */
1928 }
1929
1930 Station *st = Station::GetByTile(tile);
1931 CommandCost cost = RemoveRailStation(st, flags, _price[PR_CLEAR_STATION_RAIL]);
1932
1934
1935 return cost;
1936}
1937
1945{
1946 /* if there is flooding, remove waypoints tile by tile */
1949 }
1950
1951 return RemoveRailStation(Waypoint::GetByTile(tile), flags, _price[PR_CLEAR_WAYPOINT_RAIL]);
1952}
1953
1954
1960static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
1961{
1962 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1963
1964 if (*primary_stop == nullptr) {
1965 /* we have no roadstop of the type yet, so write a "primary stop" */
1966 return primary_stop;
1967 } else {
1968 /* there are stops already, so append to the end of the list */
1969 RoadStop *stop = *primary_stop;
1970 while (stop->next != nullptr) stop = stop->next;
1971 return &stop->next;
1972 }
1973}
1974
1975static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1976CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1977
1987static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1988{
1989 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1990}
1991
2006CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
2007{
2008 DiagDirections invalid_dirs{};
2009 if (is_drive_through) {
2010 invalid_dirs.Set(AxisToDiagDir(axis));
2011 invalid_dirs.Set(ReverseDiagDir(AxisToDiagDir(axis)));
2012 } else {
2013 invalid_dirs.Set(ddir);
2014 }
2015
2016 /* Check every tile in the area. */
2017 int allowed_z = -1;
2019 for (TileIndex cur_tile : tile_area) {
2020 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, roadstopspec, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
2021 if (ret.Failed()) return ret;
2022
2023 bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
2024
2025 /* Only add costs if a stop doesn't already exist in the location */
2026 if (!is_preexisting_roadstop) {
2027 cost.AddCost(ret.GetCost());
2028 cost.AddCost(unit_cost);
2029 }
2030 }
2031
2032 return cost;
2033}
2034
2051CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
2052 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
2053{
2054 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= RoadStopType::End) return CMD_ERROR;
2055 bool reuse = (station_to_join != NEW_STATION);
2056 if (!reuse) station_to_join = StationID::Invalid();
2057 bool distant_join = (station_to_join != StationID::Invalid());
2058
2059 /* Check if the given station class is valid */
2060 if (static_cast<uint>(spec_class) >= RoadStopClass::GetClassCount()) return CMD_ERROR;
2061 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
2062 if (IsWaypointClass(*cls)) return CMD_ERROR;
2063 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
2064
2065 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
2066 if (roadstopspec != nullptr) {
2067 if (stop_type == RoadStopType::Truck && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2068 if (stop_type == RoadStopType::Bus && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2069 if (!is_drive_through && roadstopspec->flags.Test(RoadStopSpecFlag::DriveThroughOnly)) return CMD_ERROR;
2070 }
2071
2072 /* Check if the requested road stop is too big */
2073 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2074 /* Check for incorrect width / length. */
2075 if (width == 0 || length == 0) return CMD_ERROR;
2076 /* Check if the first tile and the last tile are valid */
2077 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
2078
2079 TileArea roadstop_area(tile, width, length);
2080
2081 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2082
2083 /* Trams only have drive through stops */
2084 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
2085
2086 Axis axis = DiagDirToAxis(ddir);
2087
2089 if (ret.Failed()) return ret;
2090
2091 bool is_truck_stop = stop_type != RoadStopType::Bus;
2092
2093 /* Total road stop cost. */
2094 Money unit_cost;
2095 if (roadstopspec != nullptr) {
2096 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS);
2097 } else {
2098 unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
2099 }
2100 StationID est = StationID::Invalid();
2101 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, roadstopspec, axis, ddir, &est, rt, unit_cost);
2102 if (cost.Failed()) return cost;
2103
2104 Station *st = nullptr;
2105 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2106 if (ret.Failed()) return ret;
2107
2108 /* Check if this number of road stops can be allocated. */
2109 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);
2110
2111 ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD);
2112 if (ret.Failed()) return ret;
2113
2114 /* Check if we can allocate a custom stationspec to this station */
2115 auto specindex = AllocateSpecToRoadStop(roadstopspec, st);
2116 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2117
2118 if (roadstopspec != nullptr) {
2119 /* Perform NewGRF checks */
2120
2121 /* Check if the road stop is buildable */
2122 if (roadstopspec->callback_mask.Test(RoadStopCallbackMask::Avail)) {
2123 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? StationType::Truck : StationType::Bus, 0);
2124 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2125 }
2126 }
2127
2128 if (flags.Test(DoCommandFlag::Execute)) {
2129 if (specindex.has_value()) AssignSpecToRoadStop(roadstopspec, st, *specindex);
2130 /* Check every tile in the area. */
2131 for (TileIndex cur_tile : roadstop_area) {
2132 /* Get existing road types and owners before any tile clearing */
2133 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
2134 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
2135 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
2136 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
2137
2138 if (IsTileType(cur_tile, MP_STATION) && IsStationRoadStop(cur_tile)) {
2139 RemoveRoadStop(cur_tile, flags, *specindex);
2140 }
2141
2142 if (roadstopspec != nullptr) {
2143 /* Include this road stop spec's animation trigger bitmask
2144 * in the station's cached copy. */
2145 st->cached_roadstop_anim_triggers.Set(roadstopspec->animation.triggers);
2146 }
2147
2148 RoadStop *road_stop = new RoadStop(cur_tile);
2149 /* Insert into linked list of RoadStops. */
2150 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2151 *currstop = road_stop;
2152
2153 if (is_truck_stop) {
2154 st->truck_station.Add(cur_tile);
2155 } else {
2156 st->bus_station.Add(cur_tile);
2157 }
2158
2159 /* Initialize an empty station. */
2160 st->AddFacility(is_truck_stop ? StationFacility::TruckStop : StationFacility::BusStop, cur_tile);
2161
2162 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
2163
2164 RoadStopType rs_type = is_truck_stop ? RoadStopType::Truck : RoadStopType::Bus;
2165 if (is_drive_through) {
2166 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2167 * bits first. */
2168 if (IsNormalRoadTile(cur_tile)) {
2169 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
2170 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
2171 }
2172
2173 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2174 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2175
2176 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == RoadStopType::Bus ? StationType::Bus : StationType::Truck), road_rt, tram_rt, axis);
2177 road_stop->MakeDriveThrough();
2178 } else {
2179 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2180 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2181 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2182 }
2185 Company::Get(st->owner)->infrastructure.station++;
2186
2187 SetCustomRoadStopSpecIndex(cur_tile, *specindex);
2188 if (roadstopspec != nullptr) {
2189 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2190 TriggerRoadStopAnimation(st, cur_tile, StationAnimationTrigger::Built);
2191 }
2192
2193 MarkTileDirtyByTile(cur_tile);
2194 }
2195
2196 if (st != nullptr) {
2197 st->AfterStationTileSetChange(true, is_truck_stop ? StationType::Truck: StationType::Bus);
2198 }
2199 }
2200 return cost;
2201}
2202
2210static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2211{
2212 Station *st = Station::GetByTile(tile);
2213
2215 CommandCost ret = CheckOwnership(st->owner);
2216 if (ret.Failed()) return ret;
2217 }
2218
2219 bool is_truck = IsTruckStop(tile);
2220
2221 RoadStop **primary_stop;
2222 RoadStop *cur_stop;
2223 if (is_truck) { // truck stop
2224 primary_stop = &st->truck_stops;
2225 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Truck);
2226 } else {
2227 primary_stop = &st->bus_stops;
2228 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Bus);
2229 }
2230
2231 assert(cur_stop != nullptr);
2232
2233 /* don't do the check for drive-through road stops when company bankrupts */
2235 /* remove the 'going through road stop' status from all vehicles on that tile */
2236 if (flags.Test(DoCommandFlag::Execute)) {
2237 for (Vehicle *v : VehiclesOnTile(tile)) {
2238 if (v->type != VEH_ROAD) continue;
2239 /* Okay... we are a road vehicle on a drive through road stop.
2240 * But that road stop has just been removed, so we need to make
2241 * sure we are in a valid state... however, vehicles can also
2242 * turn on road stop tiles, so only clear the 'road stop' state
2243 * bits and only when the state was 'in road stop', otherwise
2244 * we'll end up clearing the turn around bits. */
2247 }
2248 }
2249 } else {
2251 if (ret.Failed()) return ret;
2252 }
2253
2254 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2255
2256 if (flags.Test(DoCommandFlag::Execute)) {
2257 if (*primary_stop == cur_stop) {
2258 /* removed the first stop in the list */
2259 *primary_stop = cur_stop->next;
2260 /* removed the only stop? */
2261 if (*primary_stop == nullptr) {
2264 }
2265 } else {
2266 /* tell the predecessor in the list to skip this stop */
2267 RoadStop *pred = *primary_stop;
2268 while (pred->next != cur_stop) pred = pred->next;
2269 pred->next = cur_stop->next;
2270 }
2271
2272 /* Update company infrastructure counts. */
2273 for (RoadTramType rtt : _roadtramtypes) {
2274 RoadType rt = GetRoadType(tile, rtt);
2276 }
2277
2278 Company::Get(st->owner)->infrastructure.station--;
2280
2281 uint specindex = GetCustomRoadStopSpecIndex(tile);
2282
2283 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2284
2285 if (IsDriveThroughStopTile(tile)) {
2286 /* Clears the tile for us */
2287 cur_stop->ClearDriveThrough();
2288 DeleteAnimatedTile(tile);
2289 } else {
2290 DoClearSquare(tile);
2291 }
2292
2293 delete cur_stop;
2294
2295 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2296 * this station, then look for any currently heading to the tile. */
2297 StationID station_id = st->index;
2299 [](const Vehicle *v) { return v->type == VEH_ROAD; },
2300 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2301 [station_id, tile](Vehicle *v) {
2302 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2303 v->SetDestTile(v->GetOrderStationLocation(station_id));
2304 }
2305 }
2306 );
2307
2308 st->rect.AfterRemoveTile(st, tile);
2309
2310 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
2311
2312 st->RemoveRoadStopTileData(tile);
2313 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2314
2315 /* Update the tile area of the truck/bus stop */
2316 if (is_truck) {
2317 st->truck_station.Clear();
2318 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2319 } else {
2320 st->bus_station.Clear();
2321 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2322 }
2323 }
2324
2325 Price category = is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS;
2326 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2327}
2328
2336CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2337{
2338 Waypoint *wp = Waypoint::GetByTile(tile);
2339
2341 CommandCost ret = CheckOwnership(wp->owner);
2342 if (ret.Failed()) return ret;
2343 }
2344
2345 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2346 if (!flags.Test(DoCommandFlag::Bankrupt)) {
2348 if (ret.Failed()) return ret;
2349 }
2350
2351 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2352
2353 if (flags.Test(DoCommandFlag::Execute)) {
2354 /* Update company infrastructure counts. */
2355 for (RoadTramType rtt : _roadtramtypes) {
2356 RoadType rt = GetRoadType(tile, rtt);
2358 }
2359
2360 Company::Get(wp->owner)->infrastructure.station--;
2362
2363 uint specindex = GetCustomRoadStopSpecIndex(tile);
2364
2365 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2366
2367 DoClearSquare(tile);
2368
2369 wp->rect.AfterRemoveTile(wp, tile);
2370
2371 wp->RemoveRoadStopTileData(tile);
2372 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2373
2374 if (replacement_spec_index < 0) {
2375 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2376
2377 UpdateStationSignCoord(wp);
2378
2379 /* if we deleted the whole waypoint, delete the road facility. */
2383 wp->UpdateVirtCoord();
2385 }
2386 }
2387 }
2388
2389 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(PR_CLEAR_STATION_TRUCK) : _price[PR_CLEAR_STATION_TRUCK]);
2390}
2391
2400static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
2401{
2403 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2404 bool had_success = false;
2405
2406 for (TileIndex cur_tile : roadstop_area) {
2407 /* Make sure the specified tile is a road stop of the correct type */
2408 if (!IsTileType(cur_tile, MP_STATION) || !IsAnyRoadStop(cur_tile) || IsRoadWaypoint(cur_tile) != road_waypoint) continue;
2409
2410 /* Save information on to-be-restored roads before the stop is removed. */
2411 RoadBits road_bits = ROAD_NONE;
2412 RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
2413 Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
2414 if (IsDriveThroughStopTile(cur_tile)) {
2415 for (RoadTramType rtt : _roadtramtypes) {
2416 road_type[rtt] = GetRoadType(cur_tile, rtt);
2417 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2418 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2419 /* If we don't want to preserve our roads then restore only roads of others. */
2420 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2421 }
2422 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2423 }
2424
2425 CommandCost ret;
2426 if (road_waypoint) {
2427 ret = RemoveRoadWaypointStop(cur_tile, flags);
2428 } else {
2429 ret = RemoveRoadStop(cur_tile, flags);
2430 }
2431 if (ret.Failed()) {
2432 last_error = std::move(ret);
2433 continue;
2434 }
2435 cost.AddCost(ret.GetCost());
2436 had_success = true;
2437
2438 /* Restore roads. */
2439 if (flags.Test(DoCommandFlag::Execute) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
2440 MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2441 road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
2442
2443 /* Update company infrastructure counts. */
2444 int count = CountBits(road_bits);
2445 UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
2446 UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
2447 }
2448 }
2449
2450 return had_success ? cost : last_error;
2451}
2452
2463CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2464{
2465 if (stop_type >= RoadStopType::End) return CMD_ERROR;
2466 /* Check for incorrect width / height. */
2467 if (width == 0 || height == 0) return CMD_ERROR;
2468 /* Check if the first tile and the last tile are valid */
2469 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2470 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2471 if (remove_road && flags.Test(DoCommandFlag::Bankrupt)) return CMD_ERROR;
2472
2473 TileArea roadstop_area(tile, width, height);
2474
2475 return RemoveGenericRoadStop(flags, roadstop_area, false, remove_road);
2476}
2477
2486{
2487 if (end == 0) end = start;
2488 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2489
2490 TileArea roadstop_area(start, end);
2491
2492 return RemoveGenericRoadStop(flags, roadstop_area, true, false);
2493}
2494
2503uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2504{
2505 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2506 * So no need to go any further*/
2507 if (as->noise_level < 2) return as->noise_level;
2508
2509 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2510 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2511 * Basically, it says that the less tolerant a town is, the bigger the distance before
2512 * an actual decrease can be granted */
2513 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2514
2515 /* now, we want to have the distance segmented using the distance judged bareable by town
2516 * This will give us the coefficient of reduction the distance provides. */
2517 uint noise_reduction = distance / town_tolerance_distance;
2518
2519 /* If the noise reduction equals the airport noise itself, don't give it for free.
2520 * Otherwise, simply reduce the airport's level. */
2521 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2522}
2523
2534Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2535{
2536 assert(Town::GetNumItems() > 0);
2537
2538 Town *nearest = nullptr;
2539
2540 auto width = as->size_x;
2541 auto height = as->size_y;
2542 if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
2543
2544 uint perimeter_min_x = TileX(tile);
2545 uint perimeter_min_y = TileY(tile);
2546 uint perimeter_max_x = perimeter_min_x + width - 1;
2547 uint perimeter_max_y = perimeter_min_y + height - 1;
2548
2549 mindist = UINT_MAX - 1; // prevent overflow
2550
2551 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2552 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2553 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2554 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) {
2555 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2556 if (t == nullptr) continue;
2557
2558 uint dist = DistanceManhattan(t->xy, cur_tile);
2559 if (dist == mindist && t->index < nearest->index) nearest = t;
2560 if (dist < mindist) {
2561 nearest = t;
2562 mindist = dist;
2563 }
2564 }
2565 }
2566
2567 return nearest;
2568}
2569
2577static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2578{
2580}
2581
2582
2585{
2586 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2587
2588 for (const Station *st : Station::Iterate()) {
2589 if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
2590 uint dist;
2591 Town *nearest = AirportGetNearestTown(st, dist);
2592 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2593 }
2594 }
2595}
2596
2607CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2608{
2609 bool reuse = (station_to_join != NEW_STATION);
2610 if (!reuse) station_to_join = StationID::Invalid();
2611 bool distant_join = (station_to_join != StationID::Invalid());
2612
2613 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2614
2615 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2616
2618 if (ret.Failed()) return ret;
2619
2620 /* Check if a valid, buildable airport was chosen for construction */
2621 const AirportSpec *as = AirportSpec::Get(airport_type);
2622 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2623 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2624
2625 Direction rotation = as->layouts[layout].rotation;
2626 int w = as->size_x;
2627 int h = as->size_y;
2628 if (rotation == DIR_E || rotation == DIR_W) std::swap(w, h);
2629 TileArea airport_area = TileArea(tile, w, h);
2630
2632 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2633 }
2634
2635 AirportTileTableIterator tile_iter(as->layouts[layout].tiles, tile);
2636 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2637 if (cost.Failed()) return cost;
2638
2639 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2640 uint dist;
2641 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2642 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2643
2644 /* Check if local auth would allow a new airport */
2645 StringID authority_refuse_message = STR_NULL;
2646 Town *authority_refuse_town = nullptr;
2647
2649 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2650 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2651 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2652 authority_refuse_town = nearest;
2653 }
2654 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2655 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2656 uint num = 0;
2657 for (const Station *st : Station::Iterate()) {
2658 if (st->town == t && st->facilities.Test(StationFacility::Airport) && st->airport.type != AT_OILRIG) num++;
2659 }
2660 if (num >= 2) {
2661 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2662 authority_refuse_town = t;
2663 }
2664 }
2665
2666 if (authority_refuse_message != STR_NULL) {
2667 return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index);
2668 }
2669
2670 Station *st = nullptr;
2671 ret = FindJoiningStation(StationID::Invalid(), station_to_join, allow_adjacent, airport_area, &st);
2672 if (ret.Failed()) return ret;
2673
2674 /* Distant join */
2675 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2676
2677 ret = BuildStationPart(&st, flags, reuse, airport_area, GetAirport(airport_type)->flags.Test(AirportFTAClass::Flag::Airplanes) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT);
2678 if (ret.Failed()) return ret;
2679
2680 if (st != nullptr && st->airport.tile != INVALID_TILE) {
2681 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2682 }
2683
2684 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2685 cost.AddCost(_price[PR_BUILD_STATION_AIRPORT]);
2686 }
2687
2688 if (flags.Test(DoCommandFlag::Execute)) {
2689 /* Always add the noise, so there will be no need to recalculate when option toggles */
2690 nearest->noise_reached += newnoise_level;
2691
2693 st->airport.type = airport_type;
2694 st->airport.layout = layout;
2695 st->airport.blocks = {};
2696 st->airport.rotation = rotation;
2697
2698 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
2699
2700 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2701 Tile t(iter);
2702 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID);
2703 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2704 st->airport.Add(iter);
2705
2707 }
2708
2709 /* Only call the animation trigger after all tiles have been built */
2710 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2711 TriggerAirportTileAnimation(st, iter, AirportAnimationTrigger::Built);
2712 }
2713
2715
2716 Company::Get(st->owner)->infrastructure.airport++;
2717
2718 st->AfterStationTileSetChange(true, StationType::Airport);
2720
2723 }
2724 }
2725
2726 return cost;
2727}
2728
2736{
2737 Station *st = Station::GetByTile(tile);
2738
2740 CommandCost ret = CheckOwnership(st->owner);
2741 if (ret.Failed()) return ret;
2742 }
2743
2744 tile = st->airport.tile;
2745
2747
2748 for (const Aircraft *a : Aircraft::Iterate()) {
2749 if (!a->IsNormalAircraft()) continue;
2750 if (a->targetairport == st->index && a->state != FLYING) {
2751 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2752 }
2753 }
2754
2755 if (flags.Test(DoCommandFlag::Execute)) {
2756 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2757 TileIndex tile_cur = st->airport.GetHangarTile(i);
2758 OrderBackup::Reset(tile_cur, false);
2760 }
2761
2762 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2763 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2764 * need of recalculation */
2765 uint dist;
2766 Town *nearest = AirportGetNearestTown(st, dist);
2768
2771 }
2772 }
2773
2774 for (TileIndex tile_cur : st->airport) {
2775 if (!st->TileBelongsToAirport(tile_cur)) continue;
2776
2777 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2778 if (ret.Failed()) return ret;
2779
2780 cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
2781
2782 if (flags.Test(DoCommandFlag::Execute)) {
2783 DoClearSquare(tile_cur);
2784 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur.base());
2785 }
2786 }
2787
2788 if (flags.Test(DoCommandFlag::Execute)) {
2789 /* Clear the persistent storage. */
2790 delete st->airport.psa;
2791
2792 st->rect.AfterRemoveRect(st, st->airport);
2793
2794 st->airport.Clear();
2797
2799
2800 Company::Get(st->owner)->infrastructure.airport--;
2801
2802 st->AfterStationTileSetChange(false, StationType::Airport);
2803
2804 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
2805 }
2806
2807 return cost;
2808}
2809
2817{
2818 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2819 Station *st = Station::Get(station_id);
2820
2822
2823 CommandCost ret = CheckOwnership(st->owner);
2824 if (ret.Failed()) return ret;
2825
2826 if (flags.Test(DoCommandFlag::Execute)) {
2829 }
2830 return CommandCost();
2831}
2832
2839bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2840{
2841 for (const OrderList *orderlist : OrderList::Iterate()) {
2842 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2843 assert(v != nullptr);
2844 if ((v->owner == company) != include_company) continue;
2845
2846 for (const Order &order : orderlist->GetOrders()) {
2847 if (order.GetDestination() == station && (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT))) {
2848 return true;
2849 }
2850 }
2851 }
2852 return false;
2853}
2854
2855static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
2856 {-1, 0},
2857 { 0, 0},
2858 { 0, 0},
2859 { 0, -1}
2860};
2861static const uint8_t _dock_w_chk[4] = { 2, 1, 2, 1 };
2862static const uint8_t _dock_h_chk[4] = { 1, 2, 1, 2 };
2863
2872CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
2873{
2874 bool reuse = (station_to_join != NEW_STATION);
2875 if (!reuse) station_to_join = StationID::Invalid();
2876 bool distant_join = (station_to_join != StationID::Invalid());
2877
2878 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2879
2881 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2882 direction = ReverseDiagDir(direction);
2883
2884 /* Docks cannot be placed on rapids */
2885 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2886
2888 if (ret.Failed()) return ret;
2889
2890 ret = IsDockBridgeAboveOk(tile, to_underlying(direction));
2891 if (ret.Failed()) return ret;
2892
2893 CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
2894 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
2895 if (ret.Failed()) return ret;
2896 cost.AddCost(ret.GetCost());
2897
2898 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2899
2900 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2901 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2902 }
2903
2905 if (ret.Failed()) return ret;
2906
2907 /* Get the water class of the water tile before it is cleared.*/
2908 WaterClass wc = GetWaterClass(tile_cur);
2909
2910 bool add_cost = !IsWaterTile(tile_cur);
2911 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
2912 if (ret.Failed()) return ret;
2913 if (add_cost) cost.AddCost(ret.GetCost());
2914
2915 tile_cur += TileOffsByDiagDir(direction);
2916 if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) {
2917 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2918 }
2919
2920 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2921 _dock_w_chk[direction], _dock_h_chk[direction]);
2922
2923 /* middle */
2924 Station *st = nullptr;
2925 ret = FindJoiningStation(StationID::Invalid(), station_to_join, adjacent, dock_area, &st);
2926 if (ret.Failed()) return ret;
2927
2928 /* Distant join */
2929 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2930
2931 ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
2932 if (ret.Failed()) return ret;
2933
2934 if (flags.Test(DoCommandFlag::Execute)) {
2935 st->ship_station.Add(tile);
2936 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2937 st->ship_station.Add(flat_tile);
2939
2940 st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
2941
2942 /* If the water part of the dock is on a canal, update infrastructure counts.
2943 * This is needed as we've cleared that tile before.
2944 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2945 * See: MakeWaterKeepingClass() */
2946 if (wc == WATER_CLASS_CANAL && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WATER_CLASS_CANAL && IsTileOwner(flat_tile, _current_company))) {
2947 Company::Get(st->owner)->infrastructure.water++;
2948 }
2949 Company::Get(st->owner)->infrastructure.station += 2;
2950
2951 MakeDock(tile, st->owner, st->index, direction, wc);
2952 UpdateStationDockingTiles(st);
2953
2954 st->AfterStationTileSetChange(true, StationType::Dock);
2955 }
2956
2957 return cost;
2958}
2959
2960void RemoveDockingTile(TileIndex t)
2961{
2962 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2963 TileIndex tile = t + TileOffsByDiagDir(d);
2964 if (!IsValidTile(tile)) continue;
2965
2966 if (IsTileType(tile, MP_STATION)) {
2967 Station *st = Station::GetByTile(tile);
2968 if (st != nullptr) UpdateStationDockingTiles(st);
2969 } else if (IsTileType(tile, MP_INDUSTRY)) {
2971 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
2972 }
2973 }
2974}
2975
2982{
2983 assert(IsValidTile(tile));
2984
2985 /* Clear and maybe re-set docking tile */
2986 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2987 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
2988 if (!IsValidTile(docking_tile)) continue;
2989
2990 if (IsPossibleDockingTile(docking_tile)) {
2991 SetDockingTile(docking_tile, false);
2992 CheckForDockingTile(docking_tile);
2993 }
2994 }
2995}
2996
3003{
3004 assert(IsDockTile(t));
3005
3006 StationGfx gfx = GetStationGfx(t);
3007 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
3008
3009 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
3010 TileIndex tile = t + TileOffsByDiagDir(d);
3011 if (!IsValidTile(tile)) continue;
3012 if (!IsDockTile(tile)) continue;
3013 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
3014 }
3015
3016 return INVALID_TILE;
3017}
3018
3026{
3027 Station *st = Station::GetByTile(tile);
3028 CommandCost ret = CheckOwnership(st->owner);
3029 if (ret.Failed()) return ret;
3030
3031 if (!IsDockTile(tile)) return CMD_ERROR;
3032
3033 TileIndex tile1 = FindDockLandPart(tile);
3034 if (tile1 == INVALID_TILE) return CMD_ERROR;
3035 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
3036
3037 ret = EnsureNoVehicleOnGround(tile1);
3038 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
3039 if (ret.Failed()) return ret;
3040
3041 if (flags.Test(DoCommandFlag::Execute)) {
3042 DoClearSquare(tile1);
3043 MarkTileDirtyByTile(tile1);
3044 MakeWaterKeepingClass(tile2, st->owner);
3045
3046 st->rect.AfterRemoveTile(st, tile1);
3047 st->rect.AfterRemoveTile(st, tile2);
3048
3049 MakeShipStationAreaSmaller(st);
3050 if (st->ship_station.tile == INVALID_TILE) {
3051 st->ship_station.Clear();
3052 st->docking_station.Clear();
3055 }
3056
3057 Company::Get(st->owner)->infrastructure.station -= 2;
3058
3059 st->AfterStationTileSetChange(false, StationType::Dock);
3060
3063
3064 for (Ship *s : Ship::Iterate()) {
3065 /* Find all ships going to our dock. */
3066 if (s->current_order.GetDestination() != st->index) {
3067 continue;
3068 }
3069
3070 /* Find ships that are marked as "loading" but are no longer on a
3071 * docking tile. Force them to leave the station (as they were loading
3072 * on the removed dock). */
3073 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
3074 s->LeaveStation();
3075 }
3076
3077 /* If we no longer have a dock, mark the order as invalid and send
3078 * the ship to the next order (or, if there is none, make it
3079 * wander the world). */
3080 if (s->current_order.IsType(OT_GOTO_STATION) && !st->facilities.Test(StationFacility::Dock)) {
3081 s->SetDestTile(s->GetOrderStationLocation(st->index));
3082 }
3083 }
3084 }
3085
3086 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
3087}
3088
3096{
3097 const auto &layouts = _station_display_datas[to_underlying(st)];
3098 if (gfx >= layouts.size()) gfx &= 1;
3099 return layouts.data() + gfx;
3100}
3101
3111bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3112{
3113 bool snow_desert;
3114 switch (*ground) {
3115 case SPR_RAIL_TRACK_X:
3116 case SPR_MONO_TRACK_X:
3117 case SPR_MGLV_TRACK_X:
3118 snow_desert = false;
3119 *overlay_offset = RTO_X;
3120 break;
3121
3122 case SPR_RAIL_TRACK_Y:
3123 case SPR_MONO_TRACK_Y:
3124 case SPR_MGLV_TRACK_Y:
3125 snow_desert = false;
3126 *overlay_offset = RTO_Y;
3127 break;
3128
3129 case SPR_RAIL_TRACK_X_SNOW:
3130 case SPR_MONO_TRACK_X_SNOW:
3131 case SPR_MGLV_TRACK_X_SNOW:
3132 snow_desert = true;
3133 *overlay_offset = RTO_X;
3134 break;
3135
3136 case SPR_RAIL_TRACK_Y_SNOW:
3137 case SPR_MONO_TRACK_Y_SNOW:
3138 case SPR_MGLV_TRACK_Y_SNOW:
3139 snow_desert = true;
3140 *overlay_offset = RTO_Y;
3141 break;
3142
3143 default:
3144 return false;
3145 }
3146
3147 if (ti != nullptr) {
3148 /* Decide snow/desert from tile */
3150 case LandscapeType::Arctic:
3151 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3152 break;
3153
3154 case LandscapeType::Tropic:
3155 snow_desert = GetTropicZone(ti->tile) == TROPICZONE_DESERT;
3156 break;
3157
3158 default:
3159 break;
3160 }
3161 }
3162
3163 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3164 return true;
3165}
3166
3173static BridgePillarFlags GetStationBlockedPillars(std::span<const BridgeableTileInfo> bridgeable_info, uint8_t layout)
3174{
3175 if (layout < std::size(bridgeable_info)) return bridgeable_info[layout].disallowed_pillars;
3176 return BRIDGEPILLARFLAGS_ALL;
3177}
3178
3188{
3189 if (statspec == nullptr || !statspec->flags.Test(StationSpecFlag::CustomFoundations)) return false;
3190
3191 /* Station has custom foundations.
3192 * Check whether the foundation continues beyond the tile's upper sides. */
3193 uint edge_info = 0;
3194 auto [slope, z] = GetFoundationPixelSlope(ti->tile);
3195 if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
3196 if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
3197
3198 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, gfx, edge_info);
3199 if (image == 0) return false;
3200
3202 /* Station provides extended foundations. */
3203 static constexpr uint8_t foundation_parts[] = {
3204 UINT8_MAX, UINT8_MAX, UINT8_MAX, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3205 UINT8_MAX, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3206 UINT8_MAX, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3207 7, 8, 9, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3208 };
3209 assert(ti->tileh < std::size(foundation_parts));
3210 if (foundation_parts[ti->tileh] == UINT8_MAX) return false;
3211
3212 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3213 } else {
3214 /* Draw simple foundations, built up from 8 possible foundation sprites.
3215 * Each set bit represents one of the eight composite sprites to be drawn.
3216 * 'Invalid' entries will not drawn but are included for completeness. */
3217 static constexpr uint8_t composite_foundation_parts[] = {
3218 0b0000'0000, 0b1101'0001, 0b1110'0100, 0b1110'0000, // Invalid, Invalid, Invalid, SLOPE_SW
3219 0b1100'1010, 0b1100'1001, 0b1100'0100, 0b1100'0000, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3220 0b1101'0010, 0b1001'0001, 0b1110'0100, 0b1010'0000, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3221 0b0100'1010, 0b0000'1001, 0b0100'0100, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3222 };
3223 assert(ti->tileh < std::size(composite_foundation_parts));
3224
3225 uint8_t parts = composite_foundation_parts[ti->tileh];
3226
3227 /* If foundations continue beyond the tile's upper sides then mask out the last two pieces. */
3228 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3229 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3230
3231 /* We always have to draw at least one sprite, so if we have no parts to draw fall back to default foundation. */
3232 if (parts == 0) return false;
3233
3235 for (uint i : SetBitIterator(parts)) {
3236 AddSortableSpriteToDraw(image + i, PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3237 }
3239 }
3240
3241 OffsetGroundSprite(0, -static_cast<int>(TILE_HEIGHT));
3243
3244 return true;
3245}
3246
3247static void DrawTile_Station(TileInfo *ti)
3248{
3249 const NewGRFSpriteLayout *layout = nullptr;
3250 SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_layout and t
3251 DrawTileSpriteSpan tmp_layout;
3252 const DrawTileSprites *t = nullptr;
3253 int32_t total_offset;
3254 const RailTypeInfo *rti = nullptr;
3255 uint32_t relocation = 0;
3256 uint32_t ground_relocation = 0;
3257 BaseStation *st = nullptr;
3258 const StationSpec *statspec = nullptr;
3259 uint tile_layout = 0;
3260 auto bridgeable_info = GetStationBridgeableTileInfo(GetStationType(ti->tile));
3261
3262 if (HasStationRail(ti->tile)) {
3263 rti = GetRailTypeInfo(GetRailType(ti->tile));
3264 total_offset = rti->GetRailtypeSpriteOffset();
3265
3266 if (IsCustomStationSpecIndex(ti->tile)) {
3267 /* look for customization */
3268 st = BaseStation::GetByTile(ti->tile);
3269 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3270
3271 if (statspec != nullptr) {
3272 tile_layout = GetStationGfx(ti->tile);
3273
3275 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3276 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
3277 }
3278
3279 /* Ensure the chosen tile layout is valid for this custom station */
3280 if (!statspec->renderdata.empty()) {
3281 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3282 if (!layout->NeedsPreprocessing()) {
3283 t = layout;
3284 layout = nullptr;
3285 }
3286 }
3287 }
3288 }
3289 if (statspec != nullptr) bridgeable_info = statspec->bridgeable_info;
3290 } else {
3291 total_offset = 0;
3292 }
3293
3294 StationGfx gfx = GetStationGfx(ti->tile);
3295 if (IsAirport(ti->tile)) {
3296 gfx = GetAirportGfx(ti->tile);
3297 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3298 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3299 if (ats->grf_prop.HasSpriteGroups() && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3300 return;
3301 }
3302 /* No sprite group (or no valid one) found, meaning no graphics associated.
3303 * Use the substitute one instead */
3304 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3305 gfx = ats->grf_prop.subst_id;
3306 }
3307 switch (gfx) {
3308 case APT_RADAR_GRASS_FENCE_SW:
3309 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3310 break;
3311 case APT_GRASS_FENCE_NE_FLAG:
3312 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3313 break;
3314 case APT_RADAR_FENCE_SW:
3315 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3316 break;
3317 case APT_RADAR_FENCE_NE:
3318 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3319 break;
3320 case APT_GRASS_FENCE_NE_FLAG_2:
3321 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3322 break;
3323 }
3324 }
3325
3326 Owner owner = GetTileOwner(ti->tile);
3327
3328 PaletteID palette;
3329 if (Company::IsValidID(owner)) {
3330 palette = GetCompanyPalette(owner);
3331 } else {
3332 /* Some stations are not owner by a company, namely oil rigs */
3333 palette = PALETTE_TO_GREY;
3334 }
3335
3336 if (layout == nullptr && t == nullptr) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3337
3338 /* don't show foundation for docks */
3339 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3340 if (!DrawCustomStationFoundations(statspec, st, ti, tile_layout)) {
3342 }
3343 }
3344
3345 bool draw_ground = false;
3346
3347 if (IsBuoy(ti->tile)) {
3348 DrawWaterClassGround(ti);
3349 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
3350 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3351 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3352 if (ti->tileh == SLOPE_FLAT) {
3353 DrawWaterClassGround(ti);
3354 } else {
3355 assert(IsDock(ti->tile));
3356 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3357 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WATER_CLASS_INVALID;
3358 if (wc == WATER_CLASS_SEA) {
3359 DrawShoreTile(ti->tileh);
3360 } else {
3361 DrawClearLandTile(ti, 3);
3362 }
3363 }
3364 } else if (IsRoadWaypointTile(ti->tile)) {
3366 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3367 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3368 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3369 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3370 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3371 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3372
3373 if (ti->tileh != SLOPE_FLAT) {
3375 }
3376
3377 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3378 } else {
3379 if (layout != nullptr) {
3380 /* Sprite layout which needs preprocessing */
3381 bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround);
3382 processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3383 GetCustomStationRelocation(processor, statspec, st, ti->tile);
3384 tmp_layout = processor.GetLayout();
3385 t = &tmp_layout;
3386 total_offset = 0;
3387 } else if (statspec != nullptr) {
3388 /* Simple sprite layout */
3389 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3391 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3392 }
3393 ground_relocation += rti->fallback_railtype;
3394 }
3395
3396 draw_ground = true;
3397 }
3398
3399 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3400 SpriteID image = t->ground.sprite;
3401 PaletteID pal = t->ground.pal;
3402 RailTrackOffset overlay_offset;
3403 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3404 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
3405 DrawGroundSprite(image, PAL_NONE);
3406 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3407
3408 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3409 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
3410 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3411 }
3412 } else {
3413 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3414 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3415 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3416
3417 /* PBS debugging, draw reserved tracks darker */
3418 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3419 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
3420 }
3421 }
3422 }
3423
3425
3426 if (IsAnyRoadStop(ti->tile)) {
3427 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3428 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3429 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3430 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3431
3432 StationGfx view = GetStationGfx(ti->tile);
3433 StationType type = GetStationType(ti->tile);
3434
3435 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3436 RoadStopDrawModes stop_draw_mode{};
3437 if (stopspec != nullptr) {
3438 stop_draw_mode = stopspec->draw_mode;
3439 st = BaseStation::GetByTile(ti->tile);
3440 std::array<int32_t, 1> regs100;
3441 auto result = GetRoadStopLayout(ti, stopspec, st, type, view, regs100);
3442 if (result.has_value()) {
3443 if (stopspec->flags.Test(RoadStopSpecFlag::DrawModeRegister)) {
3444 stop_draw_mode = static_cast<RoadStopDrawModes>(regs100[0]);
3445 }
3446 if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) {
3447 draw_ground = true;
3448 }
3449 processor = std::move(*result);
3450 tmp_layout = processor.GetLayout();
3451 t = &tmp_layout;
3452 }
3453 }
3454
3455 /* Draw ground sprite */
3456 if (draw_ground) {
3457 SpriteID image = t->ground.sprite;
3458 PaletteID pal = t->ground.pal;
3459 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3460 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3461 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3462 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3463 }
3464 }
3465
3466 if (IsDriveThroughStopTile(ti->tile)) {
3467 if (type != StationType::RoadWaypoint && (stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Overlay))) {
3468 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
3469 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3470 }
3471 } else {
3472 /* Non-drivethrough road stops are only valid for roads. */
3473 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3474
3475 if ((stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Road)) && road_rti->UsesOverlay()) {
3476 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
3477 DrawGroundSprite(ground + view, PAL_NONE);
3478 }
3479 }
3480 if (stopspec != nullptr) bridgeable_info = stopspec->bridgeable_info;
3481
3482 if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
3483 /* Draw road, tram catenary */
3484 DrawRoadCatenary(ti);
3485 }
3486 }
3487
3488 if (IsRailWaypoint(ti->tile)) {
3489 /* Don't offset the waypoint graphics; they're always the same. */
3490 total_offset = 0;
3491 }
3492
3493 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
3494 DrawBridgeMiddle(ti, GetStationBlockedPillars(bridgeable_info, GetStationGfx(ti->tile)));
3495}
3496
3497void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3498{
3499 int32_t total_offset = 0;
3501 const DrawTileSprites *t = GetStationTileLayout(st, image);
3502 const RailTypeInfo *railtype_info = nullptr;
3503
3504 if (railtype != INVALID_RAILTYPE) {
3505 railtype_info = GetRailTypeInfo(railtype);
3506 total_offset = railtype_info->GetRailtypeSpriteOffset();
3507 }
3508
3509 SpriteID img = t->ground.sprite;
3510 RailTrackOffset overlay_offset;
3511 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3512 SpriteID ground = GetCustomRailSprite(railtype_info, INVALID_TILE, RTSG_GROUND);
3513 DrawSprite(img, PAL_NONE, x, y);
3514 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3515 } else {
3516 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3517 }
3518
3519 if (roadtype != INVALID_ROADTYPE) {
3520 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3521 if (image >= 4) {
3522 /* Drive-through stop */
3523 uint sprite_offset = 5 - image;
3524
3525 /* Road underlay takes precedence over tram */
3526 if (roadtype_info->UsesOverlay()) {
3527 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_GROUND);
3528 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3529
3530 SpriteID overlay = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_OVERLAY);
3531 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3532 } else if (RoadTypeIsTram(roadtype)) {
3533 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3534 }
3535 } else {
3536 /* Bay stop */
3537 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3538 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_ROADSTOP);
3539 DrawSprite(ground + image, PAL_NONE, x, y);
3540 }
3541 }
3542 }
3543
3544 /* Default waypoint has no railtype specific sprites */
3545 DrawRailTileSeqInGUI(x, y, t, (st == StationType::RailWaypoint || st == StationType::RoadWaypoint) ? 0 : total_offset, 0, pal);
3546}
3547
3548static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)
3549{
3550 return GetTileMaxPixelZ(tile);
3551}
3552
3553static Foundation GetFoundation_Station(TileIndex, Slope tileh)
3554{
3555 return FlatteningFoundation(tileh);
3556}
3557
3558static void FillTileDescRoadStop(TileIndex tile, TileDesc &td)
3559{
3560 RoadType road_rt = GetRoadTypeRoad(tile);
3561 RoadType tram_rt = GetRoadTypeTram(tile);
3562 Owner road_owner = INVALID_OWNER;
3563 Owner tram_owner = INVALID_OWNER;
3564 if (road_rt != INVALID_ROADTYPE) {
3565 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3566 td.roadtype = rti->strings.name;
3567 td.road_speed = rti->max_speed / 2;
3568 road_owner = GetRoadOwner(tile, RTT_ROAD);
3569 }
3570
3571 if (tram_rt != INVALID_ROADTYPE) {
3572 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3573 td.tramtype = rti->strings.name;
3574 td.tram_speed = rti->max_speed / 2;
3575 tram_owner = GetRoadOwner(tile, RTT_TRAM);
3576 }
3577
3578 if (IsDriveThroughStopTile(tile)) {
3579 /* Is there a mix of owners? */
3580 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
3581 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
3582 uint i = 1;
3583 if (road_owner != INVALID_OWNER) {
3584 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3585 td.owner[i] = road_owner;
3586 i++;
3587 }
3588 if (tram_owner != INVALID_OWNER) {
3589 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3590 td.owner[i] = tram_owner;
3591 }
3592 }
3593 }
3594}
3595
3596void FillTileDescRailStation(TileIndex tile, TileDesc &td)
3597{
3598 const StationSpec *spec = GetStationSpec(tile);
3599
3600 if (spec != nullptr) {
3602 td.station_name = spec->name;
3603
3604 if (spec->grf_prop.HasGrfFile()) {
3605 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3606 td.grf = gc->GetName();
3607 }
3608 }
3609
3610 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3611 td.rail_speed = rti->max_speed;
3612 td.railtype = rti->strings.name;
3613}
3614
3615void FillTileDescAirport(TileIndex tile, TileDesc &td)
3616{
3617 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3619 td.airport_name = as->name;
3620
3622 td.airport_tile_name = ats->name;
3623
3624 if (as->grf_prop.HasGrfFile()) {
3625 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3626 td.grf = gc->GetName();
3627 } else if (ats->grf_prop.HasGrfFile()) {
3628 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3629 td.grf = gc->GetName();
3630 }
3631}
3632
3633static void GetTileDesc_Station(TileIndex tile, TileDesc &td)
3634{
3635 td.owner[0] = GetTileOwner(tile);
3637
3638 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3639 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3640 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3641
3642 StringID str;
3643 switch (GetStationType(tile)) {
3644 default: NOT_REACHED();
3645 case StationType::Rail: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3646 case StationType::Airport:
3647 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3648 break;
3649 case StationType::Truck: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3650 case StationType::Bus: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3651 case StationType::Oilrig: {
3652 const Industry *i = Station::GetByTile(tile)->industry;
3653 const IndustrySpec *is = GetIndustrySpec(i->type);
3654 td.owner[0] = i->owner;
3655 str = is->name;
3656 if (is->grf_prop.HasGrfFile()) td.grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3657 break;
3658 }
3659 case StationType::Dock: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3660 case StationType::Buoy: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3661 case StationType::RailWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3662 case StationType::RoadWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3663 }
3664 td.str = str;
3665}
3666
3667
3668static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
3669{
3670 TrackBits trackbits = TRACK_BIT_NONE;
3671
3672 switch (mode) {
3673 case TRANSPORT_RAIL:
3674 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3675 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
3676 }
3677 break;
3678
3679 case TRANSPORT_WATER:
3680 /* buoy is coded as a station, it is always on open water */
3681 if (IsBuoy(tile)) {
3682 trackbits = TRACK_BIT_ALL;
3683 /* remove tracks that connect NE map edge */
3684 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
3685 /* remove tracks that connect NW map edge */
3686 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
3687 }
3688 break;
3689
3690 case TRANSPORT_ROAD:
3691 if (IsAnyRoadStop(tile)) {
3692 RoadTramType rtt = (RoadTramType)sub_mode;
3693 if (!HasTileRoadType(tile, rtt)) break;
3694
3695 if (IsBayRoadStopTile(tile)) {
3696 DiagDirection dir = GetBayRoadStopDir(tile);
3697 if (side != INVALID_DIAGDIR && dir != side) break;
3698 trackbits = DiagDirToDiagTrackBits(dir);
3699 } else {
3700 Axis axis = GetDriveThroughStopAxis(tile);
3701 if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
3702 trackbits = AxisToTrackBits(axis);
3703 }
3704 }
3705 break;
3706
3707 default:
3708 break;
3709 }
3710
3712}
3713
3714
3715static void TileLoop_Station(TileIndex tile)
3716{
3717 auto *st = BaseStation::GetByTile(tile);
3718 switch (GetStationType(tile)) {
3719 case StationType::Airport:
3720 TriggerAirportTileAnimation(Station::From(st), tile, AirportAnimationTrigger::TileLoop);
3721 break;
3722
3723 case StationType::Rail:
3724 case StationType::RailWaypoint:
3725 TriggerStationAnimation(st, tile, StationAnimationTrigger::TileLoop);
3726 break;
3727
3728 case StationType::Dock:
3729 if (!IsTileFlat(tile)) break; // only handle water part
3730 [[fallthrough]];
3731
3732 case StationType::Oilrig: //(station part)
3733 case StationType::Buoy:
3734 TileLoop_Water(tile);
3735 break;
3736
3737 case StationType::Truck:
3738 case StationType::Bus:
3739 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3740 break;
3741
3742 case StationType::RoadWaypoint: {
3744 case LandscapeType::Arctic:
3745 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3747 MarkTileDirtyByTile(tile);
3748 }
3749 break;
3750
3751 case LandscapeType::Tropic:
3754 MarkTileDirtyByTile(tile);
3755 }
3756 break;
3757
3758 default: break;
3759 }
3760
3761 HouseZone new_zone = HouseZone::TownEdge;
3762 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3763 if (t != nullptr) {
3764 new_zone = GetTownRadiusGroup(t, tile);
3765 }
3766
3767 /* Adjust road ground type depending on 'new_zone' */
3768 Roadside new_rs = new_zone != HouseZone::TownEdge ? ROADSIDE_PAVED : ROADSIDE_GRASS;
3769 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3770
3771 if (new_rs != cur_rs) {
3772 SetRoadWaypointRoadside(tile, cur_rs == ROADSIDE_BARREN ? new_rs : ROADSIDE_BARREN);
3773 MarkTileDirtyByTile(tile);
3774 }
3775
3776 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3777 break;
3778 }
3779
3780 default: break;
3781 }
3782}
3783
3784
3785static void AnimateTile_Station(TileIndex tile)
3786{
3787 if (HasStationRail(tile)) {
3788 AnimateStationTile(tile);
3789 return;
3790 }
3791
3792 if (IsAirport(tile)) {
3793 AnimateAirportTile(tile);
3794 return;
3795 }
3796
3797 if (IsAnyRoadStopTile(tile)) {
3798 AnimateRoadStopTile(tile);
3799 return;
3800 }
3801}
3802
3803
3804static bool ClickTile_Station(TileIndex tile)
3805{
3806 const BaseStation *bst = BaseStation::GetByTile(tile);
3807
3810 } else if (IsHangar(tile)) {
3811 const Station *st = Station::From(bst);
3813 } else {
3815 }
3816 return true;
3817}
3818
3819static VehicleEnterTileStates VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
3820{
3821 if (v->type == VEH_TRAIN) {
3822 StationID station_id = GetStationIndex(tile);
3823 if (!v->current_order.ShouldStopAtStation(v, station_id)) return {};
3824 if (!IsRailStation(tile) || !v->IsFrontEngine()) return {};
3825
3826 int station_ahead;
3827 int station_length;
3828 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3829
3830 /* Stop whenever that amount of station ahead + the distance from the
3831 * begin of the platform to the stop location is longer than the length
3832 * of the platform. Station ahead 'includes' the current tile where the
3833 * vehicle is on, so we need to subtract that. */
3834 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return {};
3835
3837
3838 x &= 0xF;
3839 y &= 0xF;
3840
3841 if (DiagDirToAxis(dir) != AXIS_X) std::swap(x, y);
3842 if (y == TILE_SIZE / 2) {
3843 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
3844 stop &= TILE_SIZE - 1;
3845
3846 if (x == stop) {
3847 return VehicleEnterTileState::EnteredStation; // enter station
3848 } else if (x < stop) {
3850 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3851 if (spd < v->cur_speed) v->cur_speed = spd;
3852 }
3853 }
3854 } else if (v->type == VEH_ROAD) {
3856 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
3857 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3858 /* Attempt to allocate a parking bay in a road stop */
3859 if (RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv)) return {};
3861 }
3862 }
3863 }
3864
3865 return {};
3866}
3867
3873{
3874 /* Collect cargoes accepted since the last big tick. */
3875 CargoTypes cargoes = 0;
3876 for (CargoType cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
3877 if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) SetBit(cargoes, cargo_type);
3878 }
3879
3880 /* Anything to do? */
3881 if (cargoes == 0) return;
3882
3883 /* Loop over all houses in the catchment. */
3885 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3886 if (IsTileType(tile, MP_HOUSE)) {
3888 }
3889 }
3890}
3891
3899{
3900 if (!st->IsInUse()) {
3901 if (++st->delete_ctr >= 8) delete st;
3902 return false;
3903 }
3904
3905 if (Station::IsExpected(st)) {
3907
3908 for (GoodsEntry &ge : Station::From(st)->goods) {
3909 ge.status.Reset(GoodsEntry::State::AcceptedBigtick);
3910 }
3911 }
3912
3913
3915
3916 return true;
3917}
3918
3919static inline void byte_inc_sat(uint8_t *p)
3920{
3921 uint8_t b = *p + 1;
3922 if (b != 0) *p = b;
3923}
3924
3931static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3932{
3933 /* If truncating also punish the source stations' ratings to
3934 * decrease the flow of incoming cargo. */
3935
3936 if (!ge->HasData()) return;
3937
3938 StationCargoAmountMap waiting_per_source;
3939 ge->GetData().cargo.Truncate(amount, &waiting_per_source);
3940 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3941 Station *source_station = Station::GetIfValid(i->first);
3942 if (source_station == nullptr) continue;
3943
3944 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3945 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3946 }
3947}
3948
3954{
3955 bool waiting_changed = false;
3956
3957 byte_inc_sat(&st->time_since_load);
3958 byte_inc_sat(&st->time_since_unload);
3959
3960 for (const CargoSpec *cs : CargoSpec::Iterate()) {
3961 GoodsEntry *ge = &st->goods[cs->Index()];
3962
3963 /* The station might not currently be moving this cargo. */
3964 if (!ge->HasRating()) {
3965 /* Slowly increase the rating back to its original level in the case we
3966 * didn't deliver cargo yet to this station. This happens when a bribe
3967 * failed while you didn't moved that cargo yet to a station. */
3968 if (ge->rating < INITIAL_STATION_RATING) ge->rating++;
3969
3970 /* Nothing else to do with this cargo. */
3971 continue;
3972 }
3973
3974 byte_inc_sat(&ge->time_since_pickup);
3975
3976 /* If this cargo hasn't been picked up in a long time, get rid of it. */
3979 ge->last_speed = 0;
3980 TruncateCargo(cs, ge);
3981 waiting_changed = true;
3982 continue;
3983 }
3984
3985 bool skip = false;
3986 int rating = 0;
3987 uint waiting = ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0;
3988
3989 /* num_dests is at least 1 if there is any cargo as
3990 * StationID::Invalid() is also a destination.
3991 */
3992 uint num_dests = ge->HasData() ? static_cast<uint>(ge->GetData().cargo.Packets()->MapSize()) : 0;
3993
3994 /* Average amount of cargo per next hop, but prefer solitary stations
3995 * with only one or two next hops. They are allowed to have more
3996 * cargo waiting per next hop.
3997 * With manual cargo distribution waiting_avg = waiting / 2 as then
3998 * StationID::Invalid() is the only destination.
3999 */
4000 uint waiting_avg = waiting / (num_dests + 1);
4001
4003 ge->rating = rating = MAX_STATION_RATING;
4004 skip = true;
4005 } else if (cs->callback_mask.Test(CargoCallbackMask::StationRatingCalc)) {
4006 /* Perform custom station rating. If it succeeds the speed, days in transit and
4007 * waiting cargo ratings must not be executed. */
4008
4009 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
4010 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
4011
4012 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
4013 | (ClampTo<uint16_t>(ge->max_waiting_cargo) << 8)
4014 | (ClampTo<uint8_t>(last_speed) << 24);
4015 /* Convert to the 'old' vehicle types */
4016 uint32_t var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
4017 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
4018 if (callback != CALLBACK_FAILED) {
4019 skip = true;
4020 rating = GB(callback, 0, 14);
4021
4022 /* Simulate a 15 bit signed value */
4023 if (HasBit(callback, 14)) rating -= 0x4000;
4024 }
4025 }
4026
4027 if (!skip) {
4028 int b = ge->last_speed - 85;
4029 if (b >= 0) rating += b >> 2;
4030
4031 uint8_t waittime = ge->time_since_pickup;
4032 if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
4033 if (waittime <= 21) rating += 25;
4034 if (waittime <= 12) rating += 25;
4035 if (waittime <= 6) rating += 45;
4036 if (waittime <= 3) rating += 35;
4037
4038 rating -= 90;
4039 if (ge->max_waiting_cargo <= 1500) rating += 55;
4040 if (ge->max_waiting_cargo <= 1000) rating += 35;
4041 if (ge->max_waiting_cargo <= 600) rating += 10;
4042 if (ge->max_waiting_cargo <= 300) rating += 20;
4043 if (ge->max_waiting_cargo <= 100) rating += 10;
4044 }
4045
4046 if (Company::IsValidID(st->owner) && st->town->statues.Test(st->owner)) rating += 26;
4047
4048 uint8_t age = ge->last_age;
4049 if (age < 3) rating += 10;
4050 if (age < 2) rating += 10;
4051 if (age < 1) rating += 13;
4052
4053 {
4054 int or_ = ge->rating; // old rating
4055
4056 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
4057 ge->rating = rating = ClampTo<uint8_t>(or_ + Clamp(rating - or_, -2, 2));
4058
4059 /* if rating is <= 64 and more than 100 items waiting on average per destination,
4060 * remove some random amount of goods from the station */
4061 if (rating <= 64 && waiting_avg >= 100) {
4062 int dec = Random() & 0x1F;
4063 if (waiting_avg < 200) dec &= 7;
4064 waiting -= (dec + 1) * num_dests;
4065 waiting_changed = true;
4066 }
4067
4068 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
4069 if (rating <= 127 && waiting != 0) {
4070 uint32_t r = Random();
4071 if (rating <= (int)GB(r, 0, 7)) {
4072 /* Need to have int, otherwise it will just overflow etc. */
4073 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) - 1) * num_dests), 0);
4074 waiting_changed = true;
4075 }
4076 }
4077
4078 /* At some point we really must cap the cargo. Previously this
4079 * was a strict 4095, but now we'll have a less strict, but
4080 * increasingly aggressive truncation of the amount of cargo. */
4081 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
4082 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
4083 static const uint MAX_WAITING_CARGO = 1 << 15;
4084
4085 if (waiting > WAITING_CARGO_THRESHOLD) {
4086 uint difference = waiting - WAITING_CARGO_THRESHOLD;
4087 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
4088
4089 waiting = std::min(waiting, MAX_WAITING_CARGO);
4090 waiting_changed = true;
4091 }
4092
4093 /* We can't truncate cargo that's already reserved for loading.
4094 * Thus StoredCount() here. */
4095 if (waiting_changed && waiting < (ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0)) {
4096 /* Feed back the exact own waiting cargo at this station for the
4097 * next rating calculation. */
4098 ge->max_waiting_cargo = 0;
4099
4100 TruncateCargo(cs, ge, ge->GetData().cargo.AvailableCount() - waiting);
4101 } else {
4102 /* If the average number per next hop is low, be more forgiving. */
4103 ge->max_waiting_cargo = waiting_avg;
4104 }
4105 }
4106 }
4107
4108 StationID index = st->index;
4109 if (waiting_changed) {
4110 SetWindowDirty(WC_STATION_VIEW, index); // update whole window
4111 } else {
4112 SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
4113 }
4114}
4115
4124void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
4125{
4126 GoodsEntry &ge = st->goods[cargo];
4127
4128 /* Reroute cargo in station. */
4129 if (ge.HasData()) ge.GetData().cargo.Reroute(UINT_MAX, &ge.GetData().cargo, avoid, avoid2, &ge);
4130
4131 /* Reroute cargo staged to be transferred. */
4132 for (Vehicle *v : st->loading_vehicles) {
4133 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4134 if (u->cargo_type != cargo) continue;
4135 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4136 }
4137 }
4138}
4139
4149{
4150 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
4151 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL);
4152 GoodsEntry &ge = from->goods[cargo];
4154 if (lg == nullptr) continue;
4155 std::vector<NodeID> to_remove{};
4156 for (Edge &edge : (*lg)[ge.node].edges) {
4157 Station *to = Station::Get((*lg)[edge.dest_node].station);
4158 assert(to->goods[cargo].node == edge.dest_node);
4159 assert(TimerGameEconomy::date >= edge.LastUpdate());
4161 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4162 bool updated = false;
4163
4164 if (auto_distributed) {
4165 /* Have all vehicles refresh their next hops before deciding to
4166 * remove the node. */
4167 std::vector<Vehicle *> vehicles;
4168 for (const OrderList *l : OrderList::Iterate()) {
4169 bool found_from = false;
4170 bool found_to = false;
4171 for (const Order &order : l->GetOrders()) {
4172 if (!order.IsType(OT_GOTO_STATION) && !order.IsType(OT_IMPLICIT)) continue;
4173 if (order.GetDestination() == from->index) {
4174 found_from = true;
4175 if (found_to) break;
4176 } else if (order.GetDestination() == to->index) {
4177 found_to = true;
4178 if (found_from) break;
4179 }
4180 }
4181 if (!found_to || !found_from) continue;
4182 vehicles.push_back(l->GetFirstSharedVehicle());
4183 }
4184
4185 auto iter = vehicles.begin();
4186 while (iter != vehicles.end()) {
4187 Vehicle *v = *iter;
4188 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4190 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4191 }
4192 if (edge.LastUpdate() == TimerGameEconomy::date) {
4193 updated = true;
4194 break;
4195 }
4196
4197 Vehicle *next_shared = v->NextShared();
4198 if (next_shared) {
4199 *iter = next_shared;
4200 ++iter;
4201 } else {
4202 iter = vehicles.erase(iter);
4203 }
4204
4205 if (iter == vehicles.end()) iter = vehicles.begin();
4206 }
4207 }
4208
4209 if (!updated) {
4210 /* If it's still considered dead remove it. */
4211 to_remove.emplace_back(to->goods[cargo].node);
4212 if (ge.HasData()) ge.GetData().flows.DeleteFlows(to->index);
4213 RerouteCargo(from, cargo, to->index, from->index);
4214 }
4215 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4216 edge.Restrict();
4217 if (ge.HasData()) ge.GetData().flows.RestrictFlows(to->index);
4218 RerouteCargo(from, cargo, to->index, from->index);
4219 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4220 edge.Release();
4221 }
4222 }
4223 /* Remove dead edges. */
4224 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4225
4226 assert(TimerGameEconomy::date >= lg->LastCompression());
4228 lg->Compress();
4229 }
4230 }
4231}
4232
4242void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
4243{
4244 GoodsEntry &ge1 = st->goods[cargo];
4245 Station *st2 = Station::Get(next_station_id);
4246 GoodsEntry &ge2 = st2->goods[cargo];
4247 LinkGraph *lg = nullptr;
4248 if (ge1.link_graph == LinkGraphID::Invalid()) {
4249 if (ge2.link_graph == LinkGraphID::Invalid()) {
4251 lg = new LinkGraph(cargo);
4253 ge2.link_graph = lg->index;
4254 ge2.node = lg->AddNode(st2);
4255 } else {
4256 Debug(misc, 0, "Can't allocate link graph");
4257 }
4258 } else {
4259 lg = LinkGraph::Get(ge2.link_graph);
4260 }
4261 if (lg) {
4262 ge1.link_graph = lg->index;
4263 ge1.node = lg->AddNode(st);
4264 }
4265 } else if (ge2.link_graph == LinkGraphID::Invalid()) {
4266 lg = LinkGraph::Get(ge1.link_graph);
4267 ge2.link_graph = lg->index;
4268 ge2.node = lg->AddNode(st2);
4269 } else {
4270 lg = LinkGraph::Get(ge1.link_graph);
4271 if (ge1.link_graph != ge2.link_graph) {
4273 if (lg->Size() < lg2->Size()) {
4275 lg2->Merge(lg); // Updates GoodsEntries of lg
4276 lg = lg2;
4277 } else {
4279 lg->Merge(lg2); // Updates GoodsEntries of lg2
4280 }
4281 }
4282 }
4283 if (lg != nullptr) {
4284 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, modes);
4285 }
4286}
4287
4294void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4295{
4296 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4297 if (v->refit_cap > 0) {
4298 /* The cargo count can indeed be higher than the refit_cap if
4299 * wagons have been auto-replaced and subsequently auto-
4300 * refitted to a higher capacity. The cargo gets redistributed
4301 * among the wagons in that case.
4302 * As usage is not such an important figure anyway we just
4303 * ignore the additional cargo then.*/
4304 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4305 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EdgeUpdateMode::Increase);
4306 }
4307 }
4308}
4309
4310/* called for every station each tick */
4311static void StationHandleSmallTick(BaseStation *st)
4312{
4313 if (st->facilities.Test(StationFacility::Waypoint) || !st->IsInUse()) return;
4314
4315 uint8_t b = st->delete_ctr + 1;
4316 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4317 st->delete_ctr = b;
4318
4319 if (b == 0) UpdateStationRating(Station::From(st));
4320}
4321
4322void OnTick_Station()
4323{
4324 if (_game_mode == GM_EDITOR) return;
4325
4326 for (BaseStation *st : BaseStation::Iterate()) {
4327 StationHandleSmallTick(st);
4328
4329 /* Clean up the link graph about once a week. */
4332 };
4333
4334 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4336 /* Stop processing this station if it was deleted */
4337 if (!StationHandleBigTick(st)) continue;
4338 }
4339
4340 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4342 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4343 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4344 if (Station::IsExpected(st)) TriggerAirportAnimation(Station::From(st), AirportAnimationTrigger::AcceptanceTick);
4345 }
4346 }
4347}
4348
4350static const IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto)
4351{
4352 for (Station *st : Station::Iterate()) {
4353 for (GoodsEntry &ge : st->goods) {
4354 ge.status.Set(GoodsEntry::State::LastMonth, ge.status.Test(GoodsEntry::State::CurrentMonth));
4355 ge.status.Reset(GoodsEntry::State::CurrentMonth);
4356 }
4357 }
4358});
4359
4368void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4369{
4370 ForAllStationsRadius(tile, radius, [&](Station *st) {
4371 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4372 for (GoodsEntry &ge : st->goods) {
4373 if (ge.status.Any()) {
4374 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4375 }
4376 }
4377 }
4378 });
4379}
4380
4381static uint UpdateStationWaiting(Station *st, CargoType cargo, uint amount, Source source)
4382{
4383 /* We can't allocate a CargoPacket? Then don't do anything
4384 * at all; i.e. just discard the incoming cargo. */
4385 if (!CargoPacket::CanAllocateItem()) return 0;
4386
4387 GoodsEntry &ge = st->goods[cargo];
4388 amount += ge.amount_fract;
4389 ge.amount_fract = GB(amount, 0, 8);
4390
4391 amount >>= 8;
4392 /* No new "real" cargo item yet. */
4393 if (amount == 0) return 0;
4394
4395 StationID next = ge.GetVia(st->index);
4396 ge.GetOrCreateData().cargo.Append(new CargoPacket(st->index, amount, source), next);
4397 LinkGraph *lg = nullptr;
4398 if (ge.link_graph == LinkGraphID::Invalid()) {
4400 lg = new LinkGraph(cargo);
4402 ge.link_graph = lg->index;
4403 ge.node = lg->AddNode(st);
4404 } else {
4405 Debug(misc, 0, "Can't allocate link graph");
4406 }
4407 } else {
4408 lg = LinkGraph::Get(ge.link_graph);
4409 }
4410 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4411
4412 if (!ge.HasRating()) {
4415 }
4416
4418 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4419 TriggerAirportAnimation(st, AirportAnimationTrigger::NewCargo, cargo);
4420 TriggerRoadStopRandomisation(st, st->xy, StationRandomTrigger::NewCargo, cargo);
4421 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4422
4423
4425 st->MarkTilesDirty(true);
4426 return amount;
4427}
4428
4429static bool IsUniqueStationName(const std::string &name)
4430{
4431 for (const Station *st : Station::Iterate()) {
4432 if (!st->name.empty() && st->name == name) return false;
4433 }
4434
4435 return true;
4436}
4437
4445CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
4446{
4447 Station *st = Station::GetIfValid(station_id);
4448 if (st == nullptr) return CMD_ERROR;
4449
4450 CommandCost ret = CheckOwnership(st->owner);
4451 if (ret.Failed()) return ret;
4452
4453 bool reset = text.empty();
4454
4455 if (!reset) {
4457 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4458 }
4459
4460 if (flags.Test(DoCommandFlag::Execute)) {
4461 st->cached_name.clear();
4462 if (reset) {
4463 st->name.clear();
4464 } else {
4465 st->name = text;
4466 }
4467
4468 st->UpdateVirtCoord();
4470 }
4471
4472 return CommandCost();
4473}
4474
4475static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4476{
4477 for (Station *st : nearby) {
4478 if (st->TileIsInCatchment(tile)) stations.insert(st);
4479 }
4480}
4481
4487{
4488 if (this->tile != INVALID_TILE) {
4489 if (IsTileType(this->tile, MP_HOUSE)) {
4490 /* Town nearby stations need to be filtered per tile. */
4491 assert(this->w == 1 && this->h == 1);
4492 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4493 } else {
4494 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4495 this->stations.insert(st);
4496 return true;
4497 });
4498 }
4499 this->tile = INVALID_TILE;
4500 }
4501 return this->stations;
4502}
4503
4504
4505static bool CanMoveGoodsToStation(const Station *st, CargoType cargo)
4506{
4507 /* Is the station reserved exclusively for somebody else? */
4508 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4509
4510 /* Lowest possible rating, better not to give cargo anymore. */
4511 if (st->goods[cargo].rating == 0) return false;
4512
4513 /* Selectively servicing stations, and not this one. */
4514 if (_settings_game.order.selectgoods && !st->goods[cargo].HasVehicleEverTriedLoading()) return false;
4515
4517 /* Passengers are never served by just a truck stop. */
4518 if (st->facilities == StationFacility::TruckStop) return false;
4519 } else {
4520 /* Non-passengers are never served by just a bus stop. */
4521 if (st->facilities == StationFacility::BusStop) return false;
4522 }
4523 return true;
4524}
4525
4526uint MoveGoodsToStation(CargoType cargo, uint amount, Source source, const StationList &all_stations, Owner exclusivity)
4527{
4528 /* Return if nothing to do. Also the rounding below fails for 0. */
4529 if (all_stations.empty()) return 0;
4530 if (amount == 0) return 0;
4531
4532 Station *first_station = nullptr;
4533 typedef std::pair<Station *, uint> StationInfo;
4534 std::vector<StationInfo> used_stations;
4535
4536 for (Station *st : all_stations) {
4537 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4538 if (!CanMoveGoodsToStation(st, cargo)) continue;
4539
4540 /* Avoid allocating a vector if there is only one station to significantly
4541 * improve performance in this common case. */
4542 if (first_station == nullptr) {
4543 first_station = st;
4544 continue;
4545 }
4546 if (used_stations.empty()) {
4547 used_stations.reserve(2);
4548 used_stations.emplace_back(first_station, 0);
4549 }
4550 used_stations.emplace_back(st, 0);
4551 }
4552
4553 /* no stations around at all? */
4554 if (first_station == nullptr) return 0;
4555
4556 if (used_stations.empty()) {
4557 /* only one station around */
4558 amount *= first_station->goods[cargo].rating + 1;
4559 return UpdateStationWaiting(first_station, cargo, amount, source);
4560 }
4561
4562 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_best = {}; // best rating for each company, including OWNER_NONE
4563 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_sum = {}; // sum of ratings for each company
4564 uint best_rating = 0;
4565 uint best_sum = 0; // sum of best ratings for each company
4566
4567 for (auto &p : used_stations) {
4568 auto owner = p.first->owner;
4569 auto rating = p.first->goods[cargo].rating;
4570 if (rating > company_best[owner]) {
4571 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4572 company_best[owner] = rating;
4573 if (rating > best_rating) best_rating = rating;
4574 }
4575 company_sum[owner] += rating;
4576 }
4577
4578 /* From now we'll calculate with fractional cargo amounts.
4579 * First determine how much cargo we really have. */
4580 amount *= best_rating + 1;
4581
4582 uint moving = 0;
4583 for (auto &p : used_stations) {
4584 Owner owner = p.first->owner;
4585 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4586 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4587 p.second = amount * company_best[owner] * p.first->goods[cargo].rating / best_sum / company_sum[owner];
4588 moving += p.second;
4589 }
4590
4591 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4592 if (amount > moving) {
4593 std::stable_sort(used_stations.begin(), used_stations.end(), [cargo](const StationInfo &a, const StationInfo &b) {
4594 return b.first->goods[cargo].rating < a.first->goods[cargo].rating;
4595 });
4596
4597 assert(amount - moving <= used_stations.size());
4598 for (uint i = 0; i < amount - moving; i++) {
4599 used_stations[i].second++;
4600 }
4601 }
4602
4603 uint moved = 0;
4604 for (auto &p : used_stations) {
4605 moved += UpdateStationWaiting(p.first, cargo, p.second, source);
4606 }
4607
4608 return moved;
4609}
4610
4611void UpdateStationDockingTiles(Station *st)
4612{
4613 st->docking_station.Clear();
4614
4615 /* For neutral stations, start with the industry area instead of dock area */
4616 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4617
4618 if (area->tile == INVALID_TILE) return;
4619
4620 int x = TileX(area->tile);
4621 int y = TileY(area->tile);
4622
4623 /* Expand the area by a tile on each side while
4624 * making sure that we remain inside the map. */
4625 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4626 int x1 = std::max<int>(x - 1, 0);
4627
4628 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4629 int y1 = std::max<int>(y - 1, 0);
4630
4631 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4632 for (TileIndex tile : ta) {
4633 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4634 }
4635}
4636
4637void BuildOilRig(TileIndex tile)
4638{
4639 if (!Station::CanAllocateItem()) {
4640 Debug(misc, 0, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4641 return;
4642 }
4643
4644 Station *st = new Station(tile);
4645 _station_kdtree.Insert(st->index);
4646 st->town = ClosestTownFromTile(tile, UINT_MAX);
4647
4648 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
4649
4650 assert(IsTileType(tile, MP_INDUSTRY));
4651 /* Mark industry as associated both ways */
4652 st->industry = Industry::GetByTile(tile);
4653 st->industry->neutral_station = st;
4654 DeleteAnimatedTile(tile);
4655 MakeOilrig(tile, st->index, GetWaterClass(tile));
4656
4657 st->owner = OWNER_NONE;
4658 st->airport.type = AT_OILRIG;
4659 st->airport.rotation = DIR_N;
4660 st->airport.Add(tile);
4661 st->ship_station.Add(tile);
4664 UpdateStationDockingTiles(st);
4665
4666 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
4667
4668 st->UpdateVirtCoord();
4669
4670 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4671 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4672 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4674 StationList nearby = std::move(st->industry->stations_near);
4675 st->industry->stations_near.clear();
4676 for (Station *near : nearby) {
4677 near->RecomputeCatchment(true);
4678 UpdateStationAcceptance(near, true);
4679 }
4680 }
4681
4682 st->RecomputeCatchment();
4683 UpdateStationAcceptance(st, false);
4684}
4685
4686void DeleteOilRig(TileIndex tile)
4687{
4688 Station *st = Station::GetByTile(tile);
4689
4690 MakeWaterKeepingClass(tile, OWNER_NONE);
4691
4692 /* The oil rig station is not supposed to be shared with anything else */
4693 [[maybe_unused]] static constexpr StationFacilities expected_facility{StationFacility::Airport, StationFacility::Dock};
4694 assert(st->facilities == expected_facility && st->airport.type == AT_OILRIG);
4695 if (st->industry != nullptr && st->industry->neutral_station == st) {
4696 /* Don't leave dangling neutral station pointer */
4697 st->industry->neutral_station = nullptr;
4698 }
4699 delete st;
4700}
4701
4702static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4703{
4704
4705 if (IsAnyRoadStopTile(tile)) {
4706 for (RoadTramType rtt : _roadtramtypes) {
4707 /* Update all roadtypes, no matter if they are present */
4708 if (GetRoadOwner(tile, rtt) == old_owner) {
4709 RoadType rt = GetRoadType(tile, rtt);
4710 if (rt != INVALID_ROADTYPE) {
4711 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4712 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4713 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4714 }
4715 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4716 }
4717 }
4718 }
4719
4720 if (!IsTileOwner(tile, old_owner)) return;
4721
4722 if (new_owner != INVALID_OWNER) {
4723 /* Update company infrastructure counts. Only do it here
4724 * if the new owner is valid as otherwise the clear
4725 * command will do it for us. No need to dirty windows
4726 * here, we'll redraw the whole screen anyway.*/
4727 Company *old_company = Company::Get(old_owner);
4728 Company *new_company = Company::Get(new_owner);
4729
4730 /* Update counts for underlying infrastructure. */
4731 switch (GetStationType(tile)) {
4732 case StationType::Rail:
4733 case StationType::RailWaypoint:
4734 if (!IsStationTileBlocked(tile)) {
4735 old_company->infrastructure.rail[GetRailType(tile)]--;
4736 new_company->infrastructure.rail[GetRailType(tile)]++;
4737 }
4738 break;
4739
4740 case StationType::Bus:
4741 case StationType::Truck:
4742 case StationType::RoadWaypoint:
4743 /* Road stops were already handled above. */
4744 break;
4745
4746 case StationType::Buoy:
4747 case StationType::Dock:
4748 if (GetWaterClass(tile) == WATER_CLASS_CANAL) {
4749 old_company->infrastructure.water--;
4750 new_company->infrastructure.water++;
4751 }
4752 break;
4753
4754 default:
4755 break;
4756 }
4757
4758 /* Update station tile count. */
4759 if (!IsBuoy(tile) && !IsAirport(tile)) {
4760 old_company->infrastructure.station--;
4761 new_company->infrastructure.station++;
4762 }
4763
4764 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4765 SetTileOwner(tile, new_owner);
4767 } else {
4768 if (IsDriveThroughStopTile(tile)) {
4769 /* Remove the drive-through road stop */
4770 if (IsRoadWaypoint(tile)) {
4772 } else {
4774 }
4775 assert(IsTileType(tile, MP_ROAD));
4776 /* Change owner of tile and all roadtypes */
4777 ChangeTileOwner(tile, old_owner, new_owner);
4778 } else {
4780 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4781 * Update owner of buoy if it was not removed (was in orders).
4782 * Do not update when owned by OWNER_WATER (sea and rivers). */
4783 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4784 }
4785 }
4786}
4787
4797{
4798 /* Water flooding can always clear road stops. */
4799 if (_current_company == OWNER_WATER) return CommandCost();
4800
4801 CommandCost ret;
4802
4803 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4804 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
4805 if (tram_owner != OWNER_NONE) {
4806 ret = CheckOwnership(tram_owner);
4807 if (ret.Failed()) return ret;
4808 }
4809 }
4810
4811 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4812 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
4813 if (road_owner == OWNER_TOWN) {
4814 ret = CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags);
4815 if (ret.Failed()) return ret;
4816 } else if (road_owner != OWNER_NONE) {
4817 ret = CheckOwnership(road_owner);
4818 if (ret.Failed()) return ret;
4819 }
4820 }
4821
4822 return CommandCost();
4823}
4824
4832{
4833 if (flags.Test(DoCommandFlag::Auto)) {
4834 switch (GetStationType(tile)) {
4835 default: break;
4836 case StationType::Rail: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4837 case StationType::RailWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4838 case StationType::Airport: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4839 case StationType::Truck: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4840 case StationType::Bus: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4841 case StationType::RoadWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4842 case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4843 case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4844 case StationType::Oilrig:
4845 return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG);
4846 }
4847 }
4848
4849 switch (GetStationType(tile)) {
4850 case StationType::Rail: return RemoveRailStation(tile, flags);
4851 case StationType::RailWaypoint: return RemoveRailWaypoint(tile, flags);
4852 case StationType::Airport: return RemoveAirport(tile, flags);
4853 case StationType::Truck: [[fallthrough]];
4854 case StationType::Bus:
4855 if (IsDriveThroughStopTile(tile)) {
4856 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4857 if (remove_road.Failed()) return remove_road;
4858 }
4859 return RemoveRoadStop(tile, flags);
4860 case StationType::RoadWaypoint: {
4861 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4862 if (remove_road.Failed()) return remove_road;
4863 return RemoveRoadWaypointStop(tile, flags);
4864 }
4865 case StationType::Buoy: return RemoveBuoy(tile, flags);
4866 case StationType::Dock: return RemoveDock(tile, flags);
4867 default: break;
4868 }
4869
4870 return CMD_ERROR;
4871}
4872
4873static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
4874{
4876 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4877 * TTDP does not call it.
4878 */
4879 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4880 switch (GetStationType(tile)) {
4881 case StationType::RailWaypoint:
4882 case StationType::Rail: {
4883 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4884 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4885 }
4886
4887 case StationType::Airport:
4888 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4889
4890 case StationType::Truck:
4891 case StationType::Bus:
4892 case StationType::RoadWaypoint: {
4893 if (IsDriveThroughStopTile(tile)) {
4894 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4895 } else {
4896 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4897 }
4898 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4899 }
4900
4901 default: break;
4902 }
4903 }
4904 }
4905 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
4906}
4907
4914{
4915 uint32_t prev = 0;
4916 for (const auto &it : this->shares) {
4917 if (it.second == st) {
4918 return it.first - prev;
4919 } else {
4920 prev = it.first;
4921 }
4922 }
4923 return 0;
4924}
4925
4933{
4934 if (this->unrestricted == 0) return StationID::Invalid();
4935 assert(!this->shares.empty());
4936 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
4937 assert(it != this->shares.end() && it->first <= this->unrestricted);
4938 if (it->second != excluded && it->second != excluded2) return it->second;
4939
4940 /* We've hit one of the excluded stations.
4941 * Draw another share, from outside its range. */
4942
4943 uint end = it->first;
4944 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
4945 uint interval = end - begin;
4946 if (interval >= this->unrestricted) return StationID::Invalid(); // Only one station in the map.
4947 uint new_max = this->unrestricted - interval;
4948 uint rand = RandomRange(new_max);
4949 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
4950 this->shares.upper_bound(rand + interval);
4951 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
4952 if (it2->second != excluded && it2->second != excluded2) return it2->second;
4953
4954 /* We've hit the second excluded station.
4955 * Same as before, only a bit more complicated. */
4956
4957 uint end2 = it2->first;
4958 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
4959 uint interval2 = end2 - begin2;
4960 if (interval2 >= new_max) return StationID::Invalid(); // Only the two excluded stations in the map.
4961 new_max -= interval2;
4962 if (begin > begin2) {
4963 std::swap(begin, begin2);
4964 std::swap(end, end2);
4965 std::swap(interval, interval2);
4966 }
4967 rand = RandomRange(new_max);
4968 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
4969 if (rand < begin) {
4970 it3 = this->shares.upper_bound(rand);
4971 } else if (rand < begin2 - interval) {
4972 it3 = this->shares.upper_bound(rand + interval);
4973 } else {
4974 it3 = this->shares.upper_bound(rand + interval + interval2);
4975 }
4976 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
4977 return it3->second;
4978}
4979
4986{
4987 assert(!this->shares.empty());
4988 SharesMap new_shares;
4989 uint i = 0;
4990 for (const auto &it : this->shares) {
4991 new_shares[++i] = it.second;
4992 if (it.first == this->unrestricted) this->unrestricted = i;
4993 }
4994 this->shares.swap(new_shares);
4995 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
4996}
4997
5005{
5006 /* We assert only before changing as afterwards the shares can actually
5007 * be empty. In that case the whole flow stat must be deleted then. */
5008 assert(!this->shares.empty());
5009
5010 uint removed_shares = 0;
5011 uint added_shares = 0;
5012 uint last_share = 0;
5013 SharesMap new_shares;
5014 for (const auto &it : this->shares) {
5015 if (it.second == st) {
5016 if (flow < 0) {
5017 uint share = it.first - last_share;
5018 if (flow == INT_MIN || (uint)(-flow) >= share) {
5019 removed_shares += share;
5020 if (it.first <= this->unrestricted) this->unrestricted -= share;
5021 if (flow != INT_MIN) flow += share;
5022 last_share = it.first;
5023 continue; // remove the whole share
5024 }
5025 removed_shares += (uint)(-flow);
5026 } else {
5027 added_shares += (uint)(flow);
5028 }
5029 if (it.first <= this->unrestricted) this->unrestricted += flow;
5030
5031 /* If we don't continue above the whole flow has been added or
5032 * removed. */
5033 flow = 0;
5034 }
5035 new_shares[it.first + added_shares - removed_shares] = it.second;
5036 last_share = it.first;
5037 }
5038 if (flow > 0) {
5039 new_shares[last_share + (uint)flow] = st;
5040 if (this->unrestricted < last_share) {
5041 this->ReleaseShare(st);
5042 } else {
5043 this->unrestricted += flow;
5044 }
5045 }
5046 this->shares.swap(new_shares);
5047}
5048
5055{
5056 assert(!this->shares.empty());
5057 uint flow = 0;
5058 uint last_share = 0;
5059 SharesMap new_shares;
5060 for (auto &it : this->shares) {
5061 if (flow == 0) {
5062 if (it.first > this->unrestricted) return; // Not present or already restricted.
5063 if (it.second == st) {
5064 flow = it.first - last_share;
5065 this->unrestricted -= flow;
5066 } else {
5067 new_shares[it.first] = it.second;
5068 }
5069 } else {
5070 new_shares[it.first - flow] = it.second;
5071 }
5072 last_share = it.first;
5073 }
5074 if (flow == 0) return;
5075 new_shares[last_share + flow] = st;
5076 this->shares.swap(new_shares);
5077 assert(!this->shares.empty());
5078}
5079
5086{
5087 assert(!this->shares.empty());
5088 uint flow = 0;
5089 uint next_share = 0;
5090 bool found = false;
5091 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
5092 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
5093 if (found) {
5094 flow = next_share - it->first;
5095 this->unrestricted += flow;
5096 break;
5097 } else {
5098 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
5099 if (it->second == st) found = true;
5100 }
5101 next_share = it->first;
5102 }
5103 if (flow == 0) return;
5104 SharesMap new_shares;
5105 new_shares[flow] = st;
5106 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
5107 if (it->second != st) {
5108 new_shares[flow + it->first] = it->second;
5109 } else {
5110 flow = 0;
5111 }
5112 }
5113 this->shares.swap(new_shares);
5114 assert(!this->shares.empty());
5115}
5116
5123{
5124 assert(runtime > 0);
5125 SharesMap new_shares;
5126 uint share = 0;
5127 for (auto i : this->shares) {
5128 share = std::max(share + 1, i.first * 30 / runtime);
5129 new_shares[share] = i.second;
5130 if (this->unrestricted == i.first) this->unrestricted = share;
5131 }
5132 this->shares.swap(new_shares);
5133}
5134
5141void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5142{
5143 FlowStatMap::iterator origin_it = this->find(origin);
5144 if (origin_it == this->end()) {
5145 this->emplace(origin, FlowStat(via, flow));
5146 } else {
5147 origin_it->second.ChangeShare(via, flow);
5148 assert(!origin_it->second.GetShares()->empty());
5149 }
5150}
5151
5160void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5161{
5162 FlowStatMap::iterator prev_it = this->find(origin);
5163 if (prev_it == this->end()) {
5164 FlowStat fs(via, flow);
5165 fs.AppendShare(StationID::Invalid(), flow);
5166 this->emplace(origin, fs);
5167 } else {
5168 prev_it->second.ChangeShare(via, flow);
5169 prev_it->second.ChangeShare(StationID::Invalid(), flow);
5170 assert(!prev_it->second.GetShares()->empty());
5171 }
5172}
5173
5179{
5180 for (auto &i : *this) {
5181 FlowStat &fs = i.second;
5182 uint local = fs.GetShare(StationID::Invalid());
5183 if (local > INT_MAX) { // make sure it fits in an int
5184 fs.ChangeShare(self, -INT_MAX);
5185 fs.ChangeShare(StationID::Invalid(), -INT_MAX);
5186 local -= INT_MAX;
5187 }
5188 fs.ChangeShare(self, -(int)local);
5189 fs.ChangeShare(StationID::Invalid(), -(int)local);
5190
5191 /* If the local share is used up there must be a share for some
5192 * remote station. */
5193 assert(!fs.GetShares()->empty());
5194 }
5195}
5196
5204{
5205 StationIDStack ret;
5206 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5207 FlowStat &s_flows = f_it->second;
5208 s_flows.ChangeShare(via, INT_MIN);
5209 if (s_flows.GetShares()->empty()) {
5210 ret.Push(f_it->first);
5211 this->erase(f_it++);
5212 } else {
5213 ++f_it;
5214 }
5215 }
5216 return ret;
5217}
5218
5224{
5225 for (auto &it : *this) {
5226 it.second.RestrictShare(via);
5227 }
5228}
5229
5235{
5236 for (auto &it : *this) {
5237 it.second.ReleaseShare(via);
5238 }
5239}
5240
5246{
5247 uint ret = 0;
5248 for (const auto &it : *this) {
5249 ret += (--(it.second.GetShares()->end()))->first;
5250 }
5251 return ret;
5252}
5253
5260{
5261 uint ret = 0;
5262 for (const auto &it : *this) {
5263 ret += it.second.GetShare(via);
5264 }
5265 return ret;
5266}
5267
5274{
5275 FlowStatMap::const_iterator i = this->find(from);
5276 if (i == this->end()) return 0;
5277 return (--(i->second.GetShares()->end()))->first;
5278}
5279
5287{
5288 FlowStatMap::const_iterator i = this->find(from);
5289 if (i == this->end()) return 0;
5290 return i->second.GetShare(via);
5291}
5292
5293static CommandCost CheckBuildAbove_Station(TileIndex tile, DoCommandFlags, Axis, int height)
5294{
5295 StationType type = GetStationType(tile);
5296 auto bridgeable_info = GetStationBridgeableTileInfo(type);
5297
5298 switch (type) {
5299 case StationType::Rail:
5300 case StationType::RailWaypoint:
5301 if (const StationSpec *spec = GetStationSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5302 break;
5303
5304 case StationType::Bus:
5305 case StationType::Truck:
5306 case StationType::RoadWaypoint:
5307 if (const RoadStopSpec *spec = GetRoadStopSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5308 break;
5309
5310 default: break;
5311 }
5312
5313 return IsStationBridgeAboveOk(tile, bridgeable_info, type, GetStationGfx(tile), height);
5314}
5315
5316extern const TileTypeProcs _tile_type_station_procs = {
5317 DrawTile_Station, // draw_tile_proc
5318 GetSlopePixelZ_Station, // get_slope_z_proc
5319 ClearTile_Station, // clear_tile_proc
5320 nullptr, // add_accepted_cargo_proc
5321 GetTileDesc_Station, // get_tile_desc_proc
5322 GetTileTrackStatus_Station, // get_tile_track_status_proc
5323 ClickTile_Station, // click_tile_proc
5324 AnimateTile_Station, // animate_tile_proc
5325 TileLoop_Station, // tile_loop_proc
5326 ChangeTileOwner_Station, // change_tile_owner_proc
5327 nullptr, // add_produced_cargo_proc
5328 VehicleEnter_Station, // vehicle_enter_tile_proc
5329 GetFoundation_Station, // get_foundation_proc
5330 TerraformTile_Station, // terraform_tile_proc
5331 CheckBuildAbove_Station, // check_build_above_proc
5332};
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.
@ 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
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr bool HasExactlyOneBit(T value)
Test whether value has exactly 1 bit set.
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.
debug_inline 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 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:23
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
@ Mail
Mail.
@ Liquid
Liquids (Oil, Water, Rubber)
@ Passengers
Passengers.
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.
const Tcont * Packets() const
Returns a pointer to the cargo packet list (so you can iterate over it etc).
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?
Enum-as-bit-set wrapper.
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.
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.
StationIDStack DeleteFlows(StationID via)
Delete 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.
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...
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
void Remove(const T &element)
Remove a single element from the tree, if it exists.
Definition kdtree.hpp:415
static LinkGraphSchedule instance
Static instance of LinkGraphSchedule.
void Queue(LinkGraph *lg)
Queue a link graph for execution.
void Dequeue(LinkGraph *lg)
Remove a link graph from the execution queue.
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
size_t MapSize() const
Count the number of ranges with equal keys in this MultiMap.
Definition multimap.hpp:291
Struct containing information relating to NewGRF classes for stations and airports.
StringID name
Name of this class.
static NewGRFClass * Get(Tindex class_index)
Get a particular class.
uint GetSpecCount() const
Get the number of allocated specs within the class.
static uint GetClassCount()
Get the number of allocated classes.
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.
uint platforms
Number of platforms.
std::span< const StationGfx > layout
Predefined tile layout.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:116
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:126
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:221
StringID name
Name of this rail type.
Definition rail.h:166
uint8_t fallback_railtype
Original railtype number to use when drawing non-newgrf railtypes, or when drawing stations.
Definition rail.h:191
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:287
struct RailTypeInfo::@22 strings
Strings associated with the rail type.
struct RailTypeInfo::@19 base_sprites
Struct containing the main sprites.
struct RoadTypeInfo::@25 strings
Strings associated with the rail type.
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:131
StringID name
Name of this rail type.
Definition road.h:92
Minimal stack that uses a pool to avoid pointers.
void Push(const Titem &item)
Pushes a new item onto the stack if there is still space in the underlying pool.
Generate TileIndices around a center tile or tile area, with increasing distance.
Add dynamic register values to a sprite layout.
DrawTileSpriteSpan GetLayout() const
Returns the result spritelayout after preprocessing.
uint Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
Routes packets with station "avoid" as next hop to a different place.
uint AvailableCount() const
Returns sum of cargo still available for loading at the station.
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
Representation of an invalid date.
static Date date
Current date in days (day counter).
static TickCounter counter
Monotonic counter, in ticks, since start of game.
A sort-of mixin that implements 'at(pos)' and 'operator[](pos)' only for a specific type.
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 (MP_CLEAR) land.
CommandCost CommandCostWithParam(StringID str, uint64_t value)
Return an error status, with string and parameter.
Definition command.cpp:417
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
@ Auto
don't allow building on structures
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.
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:1032
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
Definition house.h:55
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.).
@ Random
Randomise borders.
Some typedefs for the main game.
@ Increase
Increase capacity.
@ DT_MANUAL
Manual distribution. No link graph calculations are run.
uint DistanceFromEdge(TileIndex tile)
Param the minimum distance to an edge.
Definition map.cpp:202
uint DistanceMax(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles.
Definition map.cpp:174
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:93
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:142
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:372
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:439
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:388
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:554
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:424
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:414
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:569
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.
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.
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.
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.
@ Small
Small news item. (Information window with text and viewport)
@ InColour
News item is shown in colour (otherwise it is shown in black & white).
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:429
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition rail.h:377
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:301
@ RTSG_GROUND
Main group of ground images.
Definition rail.h:44
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:43
RailTrackOffset
Offsets for sprites within an overlay/underlay set.
Definition rail.h:62
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:64
@ RTO_X
Piece of rail in X direction.
Definition rail.h:63
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
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
static debug_inline bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
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.
Definition of link refreshing utility.
bool ValParamRoadType(RoadType roadtype)
Validate functions for rail building.
Definition road.cpp:164
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:245
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:230
@ ROTSG_OVERLAY
Optional: Images for overlaying track.
Definition road.h:53
@ ROTSG_ROADSTOP
Required: Bay stop surface.
Definition road.h:62
@ ROTSG_GROUND
Required: Main group of ground images.
Definition road.h:54
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:255
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
static debug_inline bool IsNormalRoadTile(Tile t)
Return whether a tile is a normal road tile.
Definition road_map.h:58
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:615
RoadBits GetAllRoadBits(Tile tile)
Get all set RoadBits on the given tile.
Definition road_map.h:125
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
@ ROADSIDE_PAVED
Road with paved sidewalks.
Definition road_map.h:460
@ ROADSIDE_BARREN
Road on barren land.
Definition road_map.h:458
@ ROADSIDE_GRASS
Road on grass.
Definition road_map.h:459
RoadBits
Enumeration for the road parts on a tile.
Definition road_type.h:40
@ ROAD_NONE
No road-part is build.
Definition road_type.h:41
@ ROAD_Y
Full road along the y-axis (north-west + south-east)
Definition road_type.h:47
@ ROAD_X
Full road along the x-axis (south-west + north-east)
Definition road_type.h:46
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:62
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
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:48
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:49
Foundation
Enumeration for Foundations.
Definition slope_type.h:93
@ FOUNDATION_LEVELED
The tile is leveled up to a flat slope.
Definition slope_type.h:95
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:105
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:95
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:174
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.
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.
static const IntervalTimer< TimerGameEconomy > _economy_stations_monthly({TimerGameEconomy::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.
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.
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 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.
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.
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
Calculates cost of new road stops within the area.
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? In other words, is this station tile a rail station or rail waypoint?
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? In other words, is this station tile a rail station or rail waypoint?
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.
@ 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:347
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:72
@ Airplanes
Can planes land on this airport type?
Defines the data structure for an airport.
StringID name
name of this airport
SubstituteGRFFileProps grf_prop
Properties related to the grf file.
std::vector< AirportTileLayout > layouts
List of layouts composing the airport.
bool IsWithinMapBounds(uint8_t table, TileIndex index) const
Check if the airport would be within the map bounds at the given tile.
uint8_t size_y
size of airport in y direction
uint8_t size_x
size of airport in x direction
static const AirportSpec * Get(uint8_t type)
Retrieve airport spec for the given airport.
std::span< const HangarTileTable > depots
Position of the depots on the airports.
bool IsAvailable() const
Check whether this airport is available to build.
uint8_t noise_level
noise that this airport generates
Defines the data structure of each individual tile of an airport.
static const AirportTileSpec * Get(StationGfx gfx)
Retrieve airport tile spec for the given airport tile.
SubstituteGRFFileProps grf_prop
properties related the the grf file
StringID name
Tile Subname string, land information on this tile will give you "AirportName (TileSubname)".
AnimationInfo< AirportAnimationTriggers > animation
Information about the animation.
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.
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:113
Container for cargo from the same location and time.
Definition cargopacket.h:41
Specification of a cargo type.
Definition cargotype.h:74
CargoClasses classes
Classes of this cargo type.
Definition cargotype.h:81
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
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat station_rating
Fix station ratings at 100%.
Definition cheat_type.h:35
GUISettings gui
settings related to the GUI
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
bool road_stop_on_town_road
allow building of drive-through road stops on town owned roads
bool road_stop_on_competitor_road
allow building of drive-through road stops on roads owned by competitors
T y
Y coordinate.
T x
X coordinate.
T z
Z coordinate.
uint8_t town_council_tolerance
minimum required town ratings to be allowed to demolish stuff
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:67
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
bool station_noise_level
build new airports when the town noise level is still within accepted limits
Information about GRF, used in the game and (part of it) in savegames.
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
EconomySettings economy
settings to change the economy
ConstructionSettings construction
construction of things in-game
DifficultySettings difficulty
settings related to the difficulty
GameCreationSettings game_creation
settings used during the creation of a game (map)
StationSettings station
settings related to station management
LinkGraphSettings linkgraph
settings for link graph calculations
OrderSettings order
settings related to orders
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.
debug_inline const GoodsEntryData & GetData() const
Get optional cargo packet/flow data.
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.
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.
GoodsEntryData & GetOrCreateData()
Get optional cargo packet/flow data.
debug_inline bool HasData() const
Test if this goods entry has optional cargo packet/flow data.
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
An edge in the link graph.
Definition linkgraph.h:42
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:278
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:269
static debug_inline uint Size()
Get the size of the map.
Definition map_func.h:287
Tindex class_index
Class index of this spec, invalid until class is allocated.
NewGRF supplied spritelayout.
static void Reset(TileIndex tile=INVALID_TILE, bool from_gui=true)
Reset the OrderBackups from GUI/game logic.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:264
bool selectgoods
only send the goods to station if a train has been there
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...
Represents the covered area of e.g.
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< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
static size_t GetNumItems()
Returns number of valid items in the pool.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(auto index)
Returns Titem with given 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:255
void MakeDriveThrough()
Join this road stop to another 'base' road stop if possible; fill all necessary data to become an act...
Definition roadstop.cpp:62
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition roadstop.cpp:124
Buses, trucks and trams belong to this class.
Definition roadveh.h:98
uint8_t state
Definition roadveh.h:100
Iterable ensemble of each set bit in a value.
All ships have this type.
Definition ship.h:32
A location from where cargo can come from (or go to).
Definition source_type.h:32
static bool IsValidID(auto index)
Tests whether given index is a valid index for station of this type.
static bool IsExpected(const BaseStation *st)
Helper for checking whether the given station is of this type.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
Returns an iterable ensemble of all valid stations of type T.
static Station * Get(auto index)
Gets station with given index.
static Station * GetIfValid(auto index)
Returns station if the index is a valid index for this station type.
static Station * From(BaseStation *st)
Converts a BaseStation to SpecializedStation with type checking.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
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.
uint8_t station_spread
amount a station may spread
bool serve_neutral_industries
company stations can serve industries with attached neutral stations
bool distant_join_stations
allow to join non-adjacent 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
Tile description for the 'land area information' tool.
Definition tile_cmd.h:36
uint16_t rail_speed
Speed limit of rail (bridges and track)
Definition tile_cmd.h:49
std::optional< std::string > grf
newGRF used for the tile contents
Definition tile_cmd.h:47
StringID station_name
Type of station within the class.
Definition tile_cmd.h:43
StringID str
Description of the tile.
Definition tile_cmd.h:37
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:41
std::array< Owner, 4 > owner
Name of the owner(s)
Definition tile_cmd.h:39
StringID airport_class
Name of the airport class.
Definition tile_cmd.h:44
StringID airport_name
Name of the airport.
Definition tile_cmd.h:45
uint16_t tram_speed
Speed limit of tram (bridges and track)
Definition tile_cmd.h:53
StringID roadtype
Type of road on the tile.
Definition tile_cmd.h:50
StringID tramtype
Type of tram on the tile.
Definition tile_cmd.h:52
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:48
uint16_t road_speed
Speed limit of road (bridges and track)
Definition tile_cmd.h:51
std::array< StringID, 4 > owner_type
Type of each owner.
Definition tile_cmd.h:40
StringID airport_tile_name
Name of the airport tile.
Definition tile_cmd.h:46
StringID station_class
Class of station.
Definition tile_cmd.h:42
A pair-construct of a TileIndexDiff.
Definition map_type.h:31
Tile information, used while rendering the tile.
Definition tile_cmd.h:30
Slope tileh
Slope of the tile.
Definition tile_cmd.h:31
TileIndex tile
Tile index.
Definition tile_cmd.h:32
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:152
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:174
CompanyID exclusivity
which company has exclusivity
Definition town.h:84
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:85
void UpdatePosition(int center, int top, std::string_view str, std::string_view str_small={})
Update the position of the viewport sign.
bool kdtree_valid
Are the sign data valid for use with the _viewport_sign_kdtree?
'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
debug_inline bool IsFrontEngine() const
Check if the vehicle is a front engine.
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
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.
@ EnteredStation
The vehicle entered a station.
@ CannotEnter
The vehicle cannot enter the tile.
bool IsTileFlat(TileIndex tile, int *h)
Check if a given tile is flat.
Definition tile_map.cpp:95
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:136
int GetTileZ(TileIndex tile)
Get bottom height of the tile.
Definition tile_map.cpp:116
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
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:78
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
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:87
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
@ MP_TREES
Tile got trees.
Definition tile_type.h:52
@ MP_ROAD
A tile with road (or tram tracks)
Definition tile_type.h:50
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
@ MP_HOUSE
A house by a town.
Definition tile_type.h:51
@ MP_WATER
Water tile.
Definition tile_type.h:54
@ MP_INDUSTRY
Part of an industry.
Definition tile_type.h:56
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
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:527
@ 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 StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:764
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:663
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:774
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.
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 WATER_TILE_CLEAR water tile type).
Definition water_map.h:39
@ WATER_CLASS_SEA
Sea.
Definition water_map.h:40
@ WATER_CLASS_CANAL
Canal.
Definition water_map.h:41
@ WATER_CLASS_INVALID
Used for industry tiles on land (also for oilrig if newgrf says so).
Definition water_map.h:43
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:1193
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3184
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:3276
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:3171
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3158
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:3293
@ 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.