OpenTTD Source 20250908-master-g16cd420e4c
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 INVALID_STRING_ID, // 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
959static 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)
960{
962 DiagDirections invalid_dirs = AxisToDiagDirs(axis);
963
964 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
965 bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
966
967 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, false);
968 if (ret.Failed()) return ret;
969 cost.AddCost(ret.GetCost());
970
971 if (slope_cb) {
972 /* Do slope check if requested. */
973 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
974 if (ret.Failed()) return ret;
975 }
976
977 /* if station is set, then we have special handling to allow building on top of already existing stations.
978 * so station points to StationID::Invalid() if we can build on any station.
979 * Or it points to a station if we're only allowed to build on exactly that station. */
980 if (station != nullptr && IsTileType(tile_cur, MP_STATION)) {
981 if (!IsRailStation(tile_cur)) {
982 return ClearTile_Station(tile_cur, DoCommandFlag::Auto); // get error message
983 } else {
984 StationID st = GetStationIndex(tile_cur);
985 if (*station == StationID::Invalid()) {
986 *station = st;
987 } else if (*station != st) {
988 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
989 }
990 }
991 } else {
992 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
993 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
994 /* Don't overbuild signals. */
995 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
996
997 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
998 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
999 TrackBits tracks = GetTrackBits(tile_cur);
1000 Track track = RemoveFirstTrack(&tracks);
1001 Track expected_track = invalid_dirs.Test(DIAGDIR_NE) ? TRACK_X : TRACK_Y;
1002
1003 /* The existing track must align with the desired station axis. */
1004 if (tracks == TRACK_BIT_NONE && track == expected_track) {
1005 /* Check for trains having a reservation for this tile. */
1006 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
1007 Train *v = GetTrainForReservation(tile_cur, track);
1008 if (v != nullptr) {
1009 affected_vehicles.push_back(v);
1010 }
1011 }
1012 ret = Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile_cur, track);
1013 if (ret.Failed()) return ret;
1014 cost.AddCost(ret.GetCost());
1015 /* With DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) CmdLandscapeClear would fail since the rail still exists */
1016 return cost;
1017 }
1018 }
1019 }
1020 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
1021 if (ret.Failed()) return ret;
1022 cost.AddCost(ret.GetCost());
1023 }
1024
1025 return cost;
1026}
1027
1042static 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)
1043{
1045
1046 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, false);
1047 if (ret.Failed()) return ret;
1048 cost.AddCost(ret.GetCost());
1049
1050 ret = IsRoadStationBridgeAboveOk(cur_tile, spec, station_type, is_drive_through ? GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + axis : FindFirstBit(invalid_dirs.base()));
1051 if (ret.Failed()) return ret;
1052
1053 /* If station is set, then we have special handling to allow building on top of already existing stations.
1054 * Station points to StationID::Invalid() if we can build on any station.
1055 * Or it points to a station if we're only allowed to build on exactly that station. */
1056 if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
1057 if (!IsAnyRoadStop(cur_tile)) {
1058 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1059 } else {
1060 if (station_type != GetStationType(cur_tile) ||
1061 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
1062 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1063 }
1064 /* Drive-through station in the wrong direction. */
1065 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
1066 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1067 }
1068 StationID st = GetStationIndex(cur_tile);
1069 if (*station == StationID::Invalid()) {
1070 *station = st;
1071 } else if (*station != st) {
1072 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1073 }
1074 }
1075 } else {
1076 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
1077 /* Road bits in the wrong direction. */
1078 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
1079 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
1080 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1081 switch (CountBits(rb)) {
1082 case 1:
1083 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1084
1085 case 2:
1086 if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1087 return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
1088
1089 default: // 3 or 4
1090 return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
1091 }
1092 }
1093
1094 if (build_over_road) {
1095 /* There is a road, check if we can build road+tram stop over it. */
1096 RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
1097 if (road_rt != INVALID_ROADTYPE) {
1098 Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
1099 if (road_owner == OWNER_TOWN) {
1100 if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
1102 ret = CheckOwnership(road_owner);
1103 if (ret.Failed()) return ret;
1104 }
1105 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
1106
1107 if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1108
1109 if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
1110 ret = CheckOwnership(road_owner);
1111 if (ret.Failed()) return ret;
1112 }
1113
1114 cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
1115 } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
1116 cost.AddCost(RoadBuildCost(rt) * 2);
1117 }
1118
1119 /* There is a tram, check if we can build road+tram stop over it. */
1120 RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
1121 if (tram_rt != INVALID_ROADTYPE) {
1122 Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
1123 if (Company::IsValidID(tram_owner) &&
1125 /* Disallow breaking end-of-line of someone else
1126 * so trams can still reverse on this tile. */
1127 HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
1128 ret = CheckOwnership(tram_owner);
1129 if (ret.Failed()) return ret;
1130 }
1131 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
1132
1133 if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1134
1135 cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
1136 } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
1137 cost.AddCost(RoadBuildCost(rt) * 2);
1138 }
1139 } else if (rt == INVALID_ROADTYPE) {
1140 return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
1141 } else {
1142 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
1143 if (ret.Failed()) return ret;
1144 cost.AddCost(ret.GetCost());
1145 cost.AddCost(RoadBuildCost(rt) * 2);
1146 }
1147 }
1148
1149 return cost;
1150}
1151
1159{
1160 TileArea cur_ta = st->train_station;
1161
1162 /* determine new size of train station region.. */
1163 int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
1164 int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
1165 new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
1166 new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
1167 new_ta.tile = TileXY(x, y);
1168
1169 /* make sure the final size is not too big. */
1171 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1172 }
1173
1174 return CommandCost();
1175}
1176
1177RailStationTileLayout::RailStationTileLayout(const StationSpec *spec, uint8_t platforms, uint8_t length) : platforms(platforms), length(length)
1178{
1179 if (spec == nullptr) return;
1180
1181 /* Look for a predefined layout for the required size. */
1182 auto found = spec->layouts.find(GetStationLayoutKey(platforms, length));
1183 if (found != std::end(spec->layouts)) this->layout = found->second;
1184}
1185
1186StationGfx RailStationTileLayout::Iterator::operator*() const
1187{
1188 /* Use predefined layout if it exists. Mask bit zero which will indicate axis. */
1189 if (!stl.layout.empty()) return this->stl.layout[this->position] & ~1;
1190
1191 if (this->stl.length == 1) {
1192 /* Special case for 1-long platforms, all bare platforms except one small building. */
1193 return this->position == ((this->stl.platforms - 1) / 2) ? 2 : 0;
1194 }
1195
1196 if ((this->position < this->stl.length && (this->stl.platforms % 2 == 1))) {
1197 /* Number of tracks is odd, make the first platform bare with a small building. */
1198 return this->position == ((this->stl.length - 1) / 2) ? 2 : 0;
1199 }
1200
1201 if (this->stl.length > 4 && ((this->position % this->stl.length) == 0 || (this->position % this->stl.length) == this->stl.length - 1)) {
1202 /* Station is longer than 4 tiles, place bare platforms at either end. */
1203 return 0;
1204 }
1205
1206 /* None of the above so must be north or south part of larger station. */
1207 return (((this->position / this->stl.length) % 2) == (this->stl.platforms % 2)) ? 4 : 6;
1208}
1209
1222template <class T, StringID error_message, class F>
1223CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1224{
1225 assert(*st == nullptr);
1226 bool check_surrounding = true;
1227
1228 if (existing_station != StationID::Invalid()) {
1229 if (adjacent && existing_station != station_to_join) {
1230 /* You can't build an adjacent station over the top of one that
1231 * already exists. */
1232 return CommandCost(error_message);
1233 } else {
1234 /* Extend the current station, and don't check whether it will
1235 * be near any other stations. */
1236 T *candidate = T::GetIfValid(existing_station);
1237 if (candidate != nullptr && filter(candidate)) *st = candidate;
1238 check_surrounding = (*st == nullptr);
1239 }
1240 } else {
1241 /* There's no station here. Don't check the tiles surrounding this
1242 * one if the company wanted to build an adjacent station. */
1243 if (adjacent) check_surrounding = false;
1244 }
1245
1246 if (check_surrounding) {
1247 /* Make sure there is no more than one other station around us that is owned by us. */
1248 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1249 if (ret.Failed()) return ret;
1250 }
1251
1252 /* Distant join */
1253 if (*st == nullptr && station_to_join != StationID::Invalid()) *st = T::GetIfValid(station_to_join);
1254
1255 return CommandCost();
1256}
1257
1267static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1268{
1269 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1270}
1271
1282CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1283{
1284 if (is_road) {
1285 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); });
1286 } else {
1287 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); });
1288 }
1289}
1290
1302
1314
1329static 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)
1330{
1332 bool length_price_ready = true;
1333 uint8_t tracknum = 0;
1334 int allowed_z = -1;
1335 for (TileIndex cur_tile : tile_area) {
1336 /* Clear the land below the station. */
1337 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1338 if (ret.Failed()) return ret;
1339
1340 /* Only add _price[PR_BUILD_STATION_RAIL_LENGTH] once for each valid plat_len. */
1341 if (tracknum == numtracks) {
1342 length_price_ready = true;
1343 tracknum = 0;
1344 } else {
1345 tracknum++;
1346 }
1347
1348 /* AddCost for new or rotated rail stations. */
1349 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1350 cost.AddCost(ret.GetCost());
1351 cost.AddCost(_price[PR_BUILD_STATION_RAIL]);
1352 cost.AddCost(RailBuildCost(rt));
1353
1354 if (length_price_ready) {
1355 cost.AddCost(_price[PR_BUILD_STATION_RAIL_LENGTH]);
1356 length_price_ready = false;
1357 }
1358 }
1359 }
1360
1361 return cost;
1362}
1363
1371{
1372 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1373 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlag::Pylons : StationSpec::TileFlags{};
1374 return statspec->tileflags[gfx];
1375}
1376
1383{
1384 const auto flags = GetStationTileFlags(GetStationGfx(tile), statspec);
1388}
1389
1404CommandCost 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)
1405{
1406 /* Does the authority allow this? */
1407 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1408 if (ret.Failed()) return ret;
1409
1410 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1411
1412 /* Check if the given station class is valid */
1413 if (static_cast<uint>(spec_class) >= StationClass::GetClassCount()) return CMD_ERROR;
1414 const StationClass *cls = StationClass::Get(spec_class);
1415 if (IsWaypointClass(*cls)) return CMD_ERROR;
1416 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1417 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1418
1419 int w_org, h_org;
1420 if (axis == AXIS_X) {
1421 w_org = plat_len;
1422 h_org = numtracks;
1423 } else {
1424 h_org = plat_len;
1425 w_org = numtracks;
1426 }
1427
1428 /* Check if the first tile and the last tile are valid */
1429 if (!IsValidTile(tile_org) || TileAddWrap(tile_org, w_org - 1, h_org - 1) == INVALID_TILE) return CMD_ERROR;
1430
1431 bool reuse = (station_to_join != NEW_STATION);
1432 if (!reuse) station_to_join = StationID::Invalid();
1433 bool distant_join = (station_to_join != StationID::Invalid());
1434
1435 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1436
1438
1439 /* these values are those that will be stored in train_tile and station_platforms */
1440 TileArea new_location(tile_org, w_org, h_org);
1441
1442 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1443 StationID est = StationID::Invalid();
1444 std::vector<Train *> affected_vehicles;
1445 /* Add construction and clearing expenses. */
1446 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1447 if (cost.Failed()) return cost;
1448
1449 Station *st = nullptr;
1450 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1451 if (ret.Failed()) return ret;
1452
1453 ret = BuildStationPart(&st, flags, reuse, new_location, STATIONNAMING_RAIL);
1454 if (ret.Failed()) return ret;
1455
1456 if (st != nullptr && st->train_station.tile != INVALID_TILE) {
1457 ret = CanExpandRailStation(st, new_location);
1458 if (ret.Failed()) return ret;
1459 }
1460
1461 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1462 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1463 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1464
1465 RailStationTileLayout stl{statspec, numtracks, plat_len};
1466 auto it = stl.begin();
1467 TileIndex tile_track = tile_org;
1468 for (uint i = 0; i != numtracks; ++i) {
1469 TileIndex tile = tile_track;
1470 for (uint j = 0; j != plat_len; ++j) {
1471 ret = IsRailStationBridgeAboveOk(tile, statspec, StationType::Rail, *it++ + axis);
1472 if (ret.Failed()) return ret;
1473 tile += tile_delta;
1474 }
1475 tile_track += track_delta;
1476 }
1477
1478 /* Check if we can allocate a custom stationspec to this station */
1479 auto specindex = AllocateSpecToStation(statspec, st, flags.Test(DoCommandFlag::Execute));
1480 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1481
1482 if (statspec != nullptr) {
1483 /* Perform NewStation checks */
1484
1485 /* Check if the station size is permitted */
1486 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1487 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1488
1489 /* Check if the station is buildable */
1491 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1493 }
1494 }
1495
1496 if (flags.Test(DoCommandFlag::Execute)) {
1497 st->train_station = new_location;
1498 st->AddFacility(StationFacility::Train, new_location.tile);
1499
1500 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
1501
1502 if (statspec != nullptr) {
1503 /* Include this station spec's animation trigger bitmask
1504 * in the station's cached copy. */
1505 st->cached_anim_triggers.Set(statspec->animation.triggers);
1506 }
1507
1508 Track track = AxisToTrack(axis);
1509
1510 auto it = stl.begin();
1511
1512 Company *c = Company::Get(st->owner);
1513 TileIndex tile_track = tile_org;
1514 for (uint i = 0; i != numtracks; ++i) {
1515 TileIndex tile = tile_track;
1516 for (uint j = 0; j != plat_len; ++j) {
1517 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1518 /* Check for trains having a reservation for this tile. */
1520 if (v != nullptr) {
1521 affected_vehicles.push_back(v);
1523 }
1524 }
1525
1526 /* Railtype can change when overbuilding. */
1527 if (IsRailStationTile(tile)) {
1528 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1530 }
1531
1532 /* Remove animation if overbuilding */
1533 DeleteAnimatedTile(tile);
1534 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1535
1536 MakeRailStation(tile, st->owner, st->index, axis, *it++, rt);
1537 /* Free the spec if we overbuild something */
1538 DeallocateSpecFromStation(st, old_specindex);
1539
1540 SetCustomStationSpecIndex(tile, *specindex);
1541 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1542 SetAnimationFrame(tile, 0);
1543
1544 if (statspec != nullptr) {
1545 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1546 uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks, j, i, false);
1547
1548 /* As the station is not yet completely finished, the station does not yet exist. */
1549 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1550 if (callback != CALLBACK_FAILED) {
1551 if (callback <= UINT8_MAX) {
1552 SetStationGfx(tile, (callback & ~1) + axis);
1553 } else {
1555 }
1556 }
1557
1558 /* Trigger station animation -- after building? */
1559 TriggerStationAnimation(st, tile, StationAnimationTrigger::Built);
1560 }
1561
1562 SetRailStationTileFlags(tile, statspec);
1563
1564 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1566
1567 tile += tile_delta;
1568 }
1569 AddTrackToSignalBuffer(tile_track, track, _current_company);
1570 YapfNotifyTrackLayoutChange(tile_track, track);
1571 tile_track += track_delta;
1572 }
1573
1574 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1575 /* Restore reservations of trains. */
1576 RestoreTrainReservation(affected_vehicles[i]);
1577 }
1578
1579 /* Check whether we need to expand the reservation of trains already on the station. */
1580 TileArea update_reservation_area;
1581 if (axis == AXIS_X) {
1582 update_reservation_area = TileArea(tile_org, 1, numtracks);
1583 } else {
1584 update_reservation_area = TileArea(tile_org, numtracks, 1);
1585 }
1586
1587 for (TileIndex tile : update_reservation_area) {
1588 /* Don't even try to make eye candy parts reserved. */
1589 if (IsStationTileBlocked(tile)) continue;
1590
1591 DiagDirection dir = AxisToDiagDir(axis);
1592 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1593 TileIndex platform_begin = tile;
1594 TileIndex platform_end = tile;
1595
1596 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1597 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1598 platform_begin = next_tile;
1599 }
1600 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1601 platform_end = next_tile;
1602 }
1603
1604 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1605 bool reservation = false;
1606 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1607 reservation = HasStationReservation(t);
1608 }
1609
1610 if (reservation) {
1611 SetRailStationPlatformReservation(platform_begin, dir, true);
1612 }
1613 }
1614
1615 st->MarkTilesDirty(false);
1616 st->AfterStationTileSetChange(true, StationType::Rail);
1617 }
1618
1619 return cost;
1620}
1621
1622static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1623{
1624restart:
1625
1626 /* too small? */
1627 if (ta.w != 0 && ta.h != 0) {
1628 /* check the left side, x = constant, y changes */
1629 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1630 /* the left side is unused? */
1631 if (++i == ta.h) {
1632 ta.tile += TileDiffXY(1, 0);
1633 ta.w--;
1634 goto restart;
1635 }
1636 }
1637
1638 /* check the right side, x = constant, y changes */
1639 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1640 /* the right side is unused? */
1641 if (++i == ta.h) {
1642 ta.w--;
1643 goto restart;
1644 }
1645 }
1646
1647 /* check the upper side, y = constant, x changes */
1648 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1649 /* the left side is unused? */
1650 if (++i == ta.w) {
1651 ta.tile += TileDiffXY(0, 1);
1652 ta.h--;
1653 goto restart;
1654 }
1655 }
1656
1657 /* check the lower side, y = constant, x changes */
1658 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1659 /* the left side is unused? */
1660 if (++i == ta.w) {
1661 ta.h--;
1662 goto restart;
1663 }
1664 }
1665 } else {
1666 ta.Clear();
1667 }
1668
1669 return ta;
1670}
1671
1672static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1673{
1674 return st->TileBelongsToRailStation(tile);
1675}
1676
1677static void MakeRailStationAreaSmaller(BaseStation *st)
1678{
1679 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1680}
1681
1682static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1683{
1684 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1685}
1686
1687static void MakeShipStationAreaSmaller(Station *st)
1688{
1689 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1690 UpdateStationDockingTiles(st);
1691}
1692
1693static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1694{
1695 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1696}
1697
1698void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1699{
1700 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1701}
1702
1713template <class T>
1714CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
1715{
1716 /* Count of the number of tiles removed */
1717 int quantity = 0;
1719 /* Accumulator for the errors seen during clearing. If no errors happen,
1720 * and the quantity is 0 there is no station. Otherwise it will be one
1721 * of the other error that got accumulated. */
1722 CommandCost error;
1723
1724 /* Do the action for every tile into the area */
1725 for (TileIndex tile : ta) {
1726 /* Make sure the specified tile is a rail station */
1727 if (!HasStationTileRail(tile)) continue;
1728
1729 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1731 error.AddCost(std::move(ret));
1732 if (error.Failed()) continue;
1733
1734 /* Check ownership of station */
1735 T *st = T::GetByTile(tile);
1736 if (st == nullptr) continue;
1737
1739 ret = CheckOwnership(st->owner);
1740 error.AddCost(std::move(ret));
1741 if (error.Failed()) continue;
1742 }
1743
1744 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1745 quantity++;
1746
1747 if (keep_rail || IsStationTileBlocked(tile)) {
1748 /* Don't refund the 'steel' of the track when we keep the
1749 * rail, or when the tile didn't have any rail at all. */
1750 total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
1751 }
1752
1753 if (flags.Test(DoCommandFlag::Execute)) {
1754 /* read variables before the station tile is removed */
1755 uint specindex = GetCustomStationSpecIndex(tile);
1756 Track track = GetRailStationTrack(tile);
1757 Owner owner = GetTileOwner(tile);
1758 RailType rt = GetRailType(tile);
1759 Train *v = nullptr;
1760
1761 if (HasStationReservation(tile)) {
1762 v = GetTrainForReservation(tile, track);
1763 if (v != nullptr) FreeTrainReservation(v);
1764 }
1765
1766 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1767 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1768
1769 DoClearSquare(tile);
1770 DeleteNewGRFInspectWindow(GSF_STATIONS, tile.base());
1771 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
1772 Company::Get(owner)->infrastructure.station--;
1774
1775 st->rect.AfterRemoveTile(st, tile);
1776 AddTrackToSignalBuffer(tile, track, owner);
1777 YapfNotifyTrackLayoutChange(tile, track);
1778
1779 DeallocateSpecFromStation(st, specindex);
1780
1781 include(affected_stations, st);
1782
1783 if (v != nullptr) RestoreTrainReservation(v);
1784 }
1785 }
1786
1787 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1788
1789 for (T *st : affected_stations) {
1790
1791 /* now we need to make the "spanned" area of the railway station smaller
1792 * if we deleted something at the edges.
1793 * we also need to adjust train_tile. */
1794 MakeRailStationAreaSmaller(st);
1795 UpdateStationSignCoord(st);
1796
1797 /* if we deleted the whole station, delete the train facility. */
1798 if (st->train_station.tile == INVALID_TILE) {
1802 MarkCatchmentTilesDirty();
1803 st->UpdateVirtCoord();
1805 }
1806 }
1807
1808 total_cost.AddCost(quantity * removal_cost);
1809 return total_cost;
1810}
1811
1822{
1823 if (end == 0) end = start;
1824 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1825
1826 TileArea ta(start, end);
1827 std::vector<Station *> affected_stations;
1828
1829 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], keep_rail);
1830 if (ret.Failed()) return ret;
1831
1832 /* Do all station specific functions here. */
1833 for (Station *st : affected_stations) {
1834
1836 st->MarkTilesDirty(false);
1837 MarkCatchmentTilesDirty();
1838 st->RecomputeCatchment();
1839 }
1840
1841 /* Now apply the rail cost to the number that we deleted */
1842 return ret;
1843}
1844
1855{
1856 if (end == 0) end = start;
1857 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1858
1859 TileArea ta(start, end);
1860 std::vector<Waypoint *> affected_stations;
1861
1862 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], keep_rail);
1863}
1864
1865
1874template <class T>
1876{
1877 /* Current company owns the station? */
1879 CommandCost ret = CheckOwnership(st->owner);
1880 if (ret.Failed()) return ret;
1881 }
1882
1883 /* determine width and height of platforms */
1884 TileArea ta = st->train_station;
1885
1886 assert(ta.w != 0 && ta.h != 0);
1887
1889 /* clear all areas of the station */
1890 for (TileIndex tile : ta) {
1891 /* only remove tiles that are actually train station tiles */
1892 if (st->TileBelongsToRailStation(tile)) {
1893 std::vector<T*> affected_stations; // dummy
1894 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1895 if (ret.Failed()) return ret;
1896 cost.AddCost(ret.GetCost());
1897 }
1898 }
1899
1900 return cost;
1901}
1902
1910{
1911 /* if there is flooding, remove platforms tile by tile */
1914 }
1915
1916 Station *st = Station::GetByTile(tile);
1917 CommandCost cost = RemoveRailStation(st, flags, _price[PR_CLEAR_STATION_RAIL]);
1918
1920
1921 return cost;
1922}
1923
1931{
1932 /* if there is flooding, remove waypoints tile by tile */
1935 }
1936
1937 return RemoveRailStation(Waypoint::GetByTile(tile), flags, _price[PR_CLEAR_WAYPOINT_RAIL]);
1938}
1939
1940
1946static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
1947{
1948 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1949
1950 if (*primary_stop == nullptr) {
1951 /* we have no roadstop of the type yet, so write a "primary stop" */
1952 return primary_stop;
1953 } else {
1954 /* there are stops already, so append to the end of the list */
1955 RoadStop *stop = *primary_stop;
1956 while (stop->next != nullptr) stop = stop->next;
1957 return &stop->next;
1958 }
1959}
1960
1961static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1962CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1963
1973static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1974{
1975 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1976}
1977
1992CommandCost 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)
1993{
1994 DiagDirections invalid_dirs{};
1995 if (is_drive_through) {
1996 invalid_dirs.Set(AxisToDiagDir(axis));
1997 invalid_dirs.Set(ReverseDiagDir(AxisToDiagDir(axis)));
1998 } else {
1999 invalid_dirs.Set(ddir);
2000 }
2001
2002 /* Check every tile in the area. */
2003 int allowed_z = -1;
2005 for (TileIndex cur_tile : tile_area) {
2006 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, roadstopspec, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
2007 if (ret.Failed()) return ret;
2008
2009 bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
2010
2011 /* Only add costs if a stop doesn't already exist in the location */
2012 if (!is_preexisting_roadstop) {
2013 cost.AddCost(ret.GetCost());
2014 cost.AddCost(unit_cost);
2015 }
2016 }
2017
2018 return cost;
2019}
2020
2037CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
2038 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
2039{
2040 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= RoadStopType::End) return CMD_ERROR;
2041 bool reuse = (station_to_join != NEW_STATION);
2042 if (!reuse) station_to_join = StationID::Invalid();
2043 bool distant_join = (station_to_join != StationID::Invalid());
2044
2045 /* Check if the given station class is valid */
2046 if (static_cast<uint>(spec_class) >= RoadStopClass::GetClassCount()) return CMD_ERROR;
2047 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
2048 if (IsWaypointClass(*cls)) return CMD_ERROR;
2049 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
2050
2051 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
2052 if (roadstopspec != nullptr) {
2053 if (stop_type == RoadStopType::Truck && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2054 if (stop_type == RoadStopType::Bus && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2055 if (!is_drive_through && roadstopspec->flags.Test(RoadStopSpecFlag::DriveThroughOnly)) return CMD_ERROR;
2056 }
2057
2058 /* Check if the requested road stop is too big */
2059 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2060 /* Check for incorrect width / length. */
2061 if (width == 0 || length == 0) return CMD_ERROR;
2062 /* Check if the first tile and the last tile are valid */
2063 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
2064
2065 TileArea roadstop_area(tile, width, length);
2066
2067 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2068
2069 /* Trams only have drive through stops */
2070 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
2071
2072 Axis axis = DiagDirToAxis(ddir);
2073
2075 if (ret.Failed()) return ret;
2076
2077 bool is_truck_stop = stop_type != RoadStopType::Bus;
2078
2079 /* Total road stop cost. */
2080 Money unit_cost;
2081 if (roadstopspec != nullptr) {
2082 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS);
2083 } else {
2084 unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
2085 }
2086 StationID est = StationID::Invalid();
2087 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, roadstopspec, axis, ddir, &est, rt, unit_cost);
2088 if (cost.Failed()) return cost;
2089
2090 Station *st = nullptr;
2091 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2092 if (ret.Failed()) return ret;
2093
2094 /* Check if this number of road stops can be allocated. */
2095 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);
2096
2097 ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD);
2098 if (ret.Failed()) return ret;
2099
2100 /* Check if we can allocate a custom stationspec to this station */
2101 auto specindex = AllocateSpecToRoadStop(roadstopspec, st, flags.Test(DoCommandFlag::Execute));
2102 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2103
2104 if (roadstopspec != nullptr) {
2105 /* Perform NewGRF checks */
2106
2107 /* Check if the road stop is buildable */
2108 if (roadstopspec->callback_mask.Test(RoadStopCallbackMask::Avail)) {
2109 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? StationType::Truck : StationType::Bus, 0);
2110 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2111 }
2112 }
2113
2114 if (flags.Test(DoCommandFlag::Execute)) {
2115 /* Check every tile in the area. */
2116 for (TileIndex cur_tile : roadstop_area) {
2117 /* Get existing road types and owners before any tile clearing */
2118 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
2119 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
2120 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
2121 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
2122
2123 if (IsTileType(cur_tile, MP_STATION) && IsStationRoadStop(cur_tile)) {
2124 RemoveRoadStop(cur_tile, flags, *specindex);
2125 }
2126
2127 if (roadstopspec != nullptr) {
2128 /* Include this road stop spec's animation trigger bitmask
2129 * in the station's cached copy. */
2130 st->cached_roadstop_anim_triggers.Set(roadstopspec->animation.triggers);
2131 }
2132
2133 RoadStop *road_stop = new RoadStop(cur_tile);
2134 /* Insert into linked list of RoadStops. */
2135 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2136 *currstop = road_stop;
2137
2138 if (is_truck_stop) {
2139 st->truck_station.Add(cur_tile);
2140 } else {
2141 st->bus_station.Add(cur_tile);
2142 }
2143
2144 /* Initialize an empty station. */
2145 st->AddFacility(is_truck_stop ? StationFacility::TruckStop : StationFacility::BusStop, cur_tile);
2146
2147 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
2148
2149 RoadStopType rs_type = is_truck_stop ? RoadStopType::Truck : RoadStopType::Bus;
2150 if (is_drive_through) {
2151 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2152 * bits first. */
2153 if (IsNormalRoadTile(cur_tile)) {
2154 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
2155 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
2156 }
2157
2158 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2159 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2160
2161 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == RoadStopType::Bus ? StationType::Bus : StationType::Truck), road_rt, tram_rt, axis);
2162 road_stop->MakeDriveThrough();
2163 } else {
2164 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2165 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2166 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2167 }
2170 Company::Get(st->owner)->infrastructure.station++;
2171
2172 SetCustomRoadStopSpecIndex(cur_tile, *specindex);
2173 if (roadstopspec != nullptr) {
2174 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2175 TriggerRoadStopAnimation(st, cur_tile, StationAnimationTrigger::Built);
2176 }
2177
2178 MarkTileDirtyByTile(cur_tile);
2179 }
2180
2181 if (st != nullptr) {
2182 st->AfterStationTileSetChange(true, is_truck_stop ? StationType::Truck: StationType::Bus);
2183 }
2184 }
2185 return cost;
2186}
2187
2195static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2196{
2197 Station *st = Station::GetByTile(tile);
2198
2200 CommandCost ret = CheckOwnership(st->owner);
2201 if (ret.Failed()) return ret;
2202 }
2203
2204 bool is_truck = IsTruckStop(tile);
2205
2206 RoadStop **primary_stop;
2207 RoadStop *cur_stop;
2208 if (is_truck) { // truck stop
2209 primary_stop = &st->truck_stops;
2210 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Truck);
2211 } else {
2212 primary_stop = &st->bus_stops;
2213 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Bus);
2214 }
2215
2216 assert(cur_stop != nullptr);
2217
2218 /* don't do the check for drive-through road stops when company bankrupts */
2220 /* remove the 'going through road stop' status from all vehicles on that tile */
2221 if (flags.Test(DoCommandFlag::Execute)) {
2222 for (Vehicle *v : VehiclesOnTile(tile)) {
2223 if (v->type != VEH_ROAD) continue;
2224 /* Okay... we are a road vehicle on a drive through road stop.
2225 * But that road stop has just been removed, so we need to make
2226 * sure we are in a valid state... however, vehicles can also
2227 * turn on road stop tiles, so only clear the 'road stop' state
2228 * bits and only when the state was 'in road stop', otherwise
2229 * we'll end up clearing the turn around bits. */
2232 }
2233 }
2234 } else {
2236 if (ret.Failed()) return ret;
2237 }
2238
2239 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2240
2241 if (flags.Test(DoCommandFlag::Execute)) {
2242 if (*primary_stop == cur_stop) {
2243 /* removed the first stop in the list */
2244 *primary_stop = cur_stop->next;
2245 /* removed the only stop? */
2246 if (*primary_stop == nullptr) {
2249 }
2250 } else {
2251 /* tell the predecessor in the list to skip this stop */
2252 RoadStop *pred = *primary_stop;
2253 while (pred->next != cur_stop) pred = pred->next;
2254 pred->next = cur_stop->next;
2255 }
2256
2257 /* Update company infrastructure counts. */
2258 for (RoadTramType rtt : _roadtramtypes) {
2259 RoadType rt = GetRoadType(tile, rtt);
2261 }
2262
2263 Company::Get(st->owner)->infrastructure.station--;
2265
2266 uint specindex = GetCustomRoadStopSpecIndex(tile);
2267
2268 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2269
2270 if (IsDriveThroughStopTile(tile)) {
2271 /* Clears the tile for us */
2272 cur_stop->ClearDriveThrough();
2273 DeleteAnimatedTile(tile);
2274 } else {
2275 DoClearSquare(tile);
2276 }
2277
2278 delete cur_stop;
2279
2280 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2281 * this station, then look for any currently heading to the tile. */
2282 StationID station_id = st->index;
2284 [](const Vehicle *v) { return v->type == VEH_ROAD; },
2285 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2286 [station_id, tile](Vehicle *v) {
2287 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2288 v->SetDestTile(v->GetOrderStationLocation(station_id));
2289 }
2290 }
2291 );
2292
2293 st->rect.AfterRemoveTile(st, tile);
2294
2295 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
2296
2297 st->RemoveRoadStopTileData(tile);
2298 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2299
2300 /* Update the tile area of the truck/bus stop */
2301 if (is_truck) {
2302 st->truck_station.Clear();
2303 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2304 } else {
2305 st->bus_station.Clear();
2306 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2307 }
2308 }
2309
2310 Price category = is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS;
2311 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2312}
2313
2321CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2322{
2323 Waypoint *wp = Waypoint::GetByTile(tile);
2324
2326 CommandCost ret = CheckOwnership(wp->owner);
2327 if (ret.Failed()) return ret;
2328 }
2329
2330 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2331 if (!flags.Test(DoCommandFlag::Bankrupt)) {
2333 if (ret.Failed()) return ret;
2334 }
2335
2336 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2337
2338 if (flags.Test(DoCommandFlag::Execute)) {
2339 /* Update company infrastructure counts. */
2340 for (RoadTramType rtt : _roadtramtypes) {
2341 RoadType rt = GetRoadType(tile, rtt);
2343 }
2344
2345 Company::Get(wp->owner)->infrastructure.station--;
2347
2348 uint specindex = GetCustomRoadStopSpecIndex(tile);
2349
2350 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2351
2352 DoClearSquare(tile);
2353
2354 wp->rect.AfterRemoveTile(wp, tile);
2355
2356 wp->RemoveRoadStopTileData(tile);
2357 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2358
2359 if (replacement_spec_index < 0) {
2360 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2361
2362 UpdateStationSignCoord(wp);
2363
2364 /* if we deleted the whole waypoint, delete the road facility. */
2368 wp->UpdateVirtCoord();
2370 }
2371 }
2372 }
2373
2374 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(PR_CLEAR_STATION_TRUCK) : _price[PR_CLEAR_STATION_TRUCK]);
2375}
2376
2385static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
2386{
2388 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2389 bool had_success = false;
2390
2391 for (TileIndex cur_tile : roadstop_area) {
2392 /* Make sure the specified tile is a road stop of the correct type */
2393 if (!IsTileType(cur_tile, MP_STATION) || !IsAnyRoadStop(cur_tile) || IsRoadWaypoint(cur_tile) != road_waypoint) continue;
2394
2395 /* Save information on to-be-restored roads before the stop is removed. */
2396 RoadBits road_bits = ROAD_NONE;
2397 RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
2398 Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
2399 if (IsDriveThroughStopTile(cur_tile)) {
2400 for (RoadTramType rtt : _roadtramtypes) {
2401 road_type[rtt] = GetRoadType(cur_tile, rtt);
2402 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2403 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2404 /* If we don't want to preserve our roads then restore only roads of others. */
2405 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2406 }
2407 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2408 }
2409
2410 CommandCost ret;
2411 if (road_waypoint) {
2412 ret = RemoveRoadWaypointStop(cur_tile, flags);
2413 } else {
2414 ret = RemoveRoadStop(cur_tile, flags);
2415 }
2416 if (ret.Failed()) {
2417 last_error = std::move(ret);
2418 continue;
2419 }
2420 cost.AddCost(ret.GetCost());
2421 had_success = true;
2422
2423 /* Restore roads. */
2424 if (flags.Test(DoCommandFlag::Execute) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
2425 MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2426 road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
2427
2428 /* Update company infrastructure counts. */
2429 int count = CountBits(road_bits);
2430 UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
2431 UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
2432 }
2433 }
2434
2435 return had_success ? cost : last_error;
2436}
2437
2448CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2449{
2450 if (stop_type >= RoadStopType::End) return CMD_ERROR;
2451 /* Check for incorrect width / height. */
2452 if (width == 0 || height == 0) return CMD_ERROR;
2453 /* Check if the first tile and the last tile are valid */
2454 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2455 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2456 if (remove_road && flags.Test(DoCommandFlag::Bankrupt)) return CMD_ERROR;
2457
2458 TileArea roadstop_area(tile, width, height);
2459
2460 return RemoveGenericRoadStop(flags, roadstop_area, false, remove_road);
2461}
2462
2471{
2472 if (end == 0) end = start;
2473 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2474
2475 TileArea roadstop_area(start, end);
2476
2477 return RemoveGenericRoadStop(flags, roadstop_area, true, false);
2478}
2479
2488uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2489{
2490 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2491 * So no need to go any further*/
2492 if (as->noise_level < 2) return as->noise_level;
2493
2494 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2495 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2496 * Basically, it says that the less tolerant a town is, the bigger the distance before
2497 * an actual decrease can be granted */
2498 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2499
2500 /* now, we want to have the distance segmented using the distance judged bareable by town
2501 * This will give us the coefficient of reduction the distance provides. */
2502 uint noise_reduction = distance / town_tolerance_distance;
2503
2504 /* If the noise reduction equals the airport noise itself, don't give it for free.
2505 * Otherwise, simply reduce the airport's level. */
2506 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2507}
2508
2519Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2520{
2521 assert(Town::GetNumItems() > 0);
2522
2523 Town *nearest = nullptr;
2524
2525 auto width = as->size_x;
2526 auto height = as->size_y;
2527 if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
2528
2529 uint perimeter_min_x = TileX(tile);
2530 uint perimeter_min_y = TileY(tile);
2531 uint perimeter_max_x = perimeter_min_x + width - 1;
2532 uint perimeter_max_y = perimeter_min_y + height - 1;
2533
2534 mindist = UINT_MAX - 1; // prevent overflow
2535
2536 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2537 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2538 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2539 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) {
2540 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2541 if (t == nullptr) continue;
2542
2543 uint dist = DistanceManhattan(t->xy, cur_tile);
2544 if (dist == mindist && t->index < nearest->index) nearest = t;
2545 if (dist < mindist) {
2546 nearest = t;
2547 mindist = dist;
2548 }
2549 }
2550 }
2551
2552 return nearest;
2553}
2554
2562static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2563{
2565}
2566
2567
2570{
2571 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2572
2573 for (const Station *st : Station::Iterate()) {
2574 if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
2575 uint dist;
2576 Town *nearest = AirportGetNearestTown(st, dist);
2577 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2578 }
2579 }
2580}
2581
2592CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2593{
2594 bool reuse = (station_to_join != NEW_STATION);
2595 if (!reuse) station_to_join = StationID::Invalid();
2596 bool distant_join = (station_to_join != StationID::Invalid());
2597
2598 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2599
2600 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2601
2603 if (ret.Failed()) return ret;
2604
2605 /* Check if a valid, buildable airport was chosen for construction */
2606 const AirportSpec *as = AirportSpec::Get(airport_type);
2607 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2608 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2609
2610 Direction rotation = as->layouts[layout].rotation;
2611 int w = as->size_x;
2612 int h = as->size_y;
2613 if (rotation == DIR_E || rotation == DIR_W) std::swap(w, h);
2614 TileArea airport_area = TileArea(tile, w, h);
2615
2617 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2618 }
2619
2620 AirportTileTableIterator tile_iter(as->layouts[layout].tiles, tile);
2621 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2622 if (cost.Failed()) return cost;
2623
2624 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2625 uint dist;
2626 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2627 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2628
2629 /* Check if local auth would allow a new airport */
2630 StringID authority_refuse_message = STR_NULL;
2631 Town *authority_refuse_town = nullptr;
2632
2634 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2635 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2636 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2637 authority_refuse_town = nearest;
2638 }
2639 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2640 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2641 uint num = 0;
2642 for (const Station *st : Station::Iterate()) {
2643 if (st->town == t && st->facilities.Test(StationFacility::Airport) && st->airport.type != AT_OILRIG) num++;
2644 }
2645 if (num >= 2) {
2646 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2647 authority_refuse_town = t;
2648 }
2649 }
2650
2651 if (authority_refuse_message != STR_NULL) {
2652 return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index);
2653 }
2654
2655 Station *st = nullptr;
2656 ret = FindJoiningStation(StationID::Invalid(), station_to_join, allow_adjacent, airport_area, &st);
2657 if (ret.Failed()) return ret;
2658
2659 /* Distant join */
2660 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2661
2662 ret = BuildStationPart(&st, flags, reuse, airport_area, GetAirport(airport_type)->flags.Test(AirportFTAClass::Flag::Airplanes) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT);
2663 if (ret.Failed()) return ret;
2664
2665 if (st != nullptr && st->airport.tile != INVALID_TILE) {
2666 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2667 }
2668
2669 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2670 cost.AddCost(_price[PR_BUILD_STATION_AIRPORT]);
2671 }
2672
2673 if (flags.Test(DoCommandFlag::Execute)) {
2674 /* Always add the noise, so there will be no need to recalculate when option toggles */
2675 nearest->noise_reached += newnoise_level;
2676
2678 st->airport.type = airport_type;
2679 st->airport.layout = layout;
2680 st->airport.blocks = {};
2681 st->airport.rotation = rotation;
2682
2683 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
2684
2685 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2686 Tile t(iter);
2687 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID);
2688 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2689 st->airport.Add(iter);
2690
2692 }
2693
2694 /* Only call the animation trigger after all tiles have been built */
2695 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2696 TriggerAirportTileAnimation(st, iter, AirportAnimationTrigger::Built);
2697 }
2698
2700
2701 Company::Get(st->owner)->infrastructure.airport++;
2702
2703 st->AfterStationTileSetChange(true, StationType::Airport);
2705
2708 }
2709 }
2710
2711 return cost;
2712}
2713
2721{
2722 Station *st = Station::GetByTile(tile);
2723
2725 CommandCost ret = CheckOwnership(st->owner);
2726 if (ret.Failed()) return ret;
2727 }
2728
2729 tile = st->airport.tile;
2730
2732
2733 for (const Aircraft *a : Aircraft::Iterate()) {
2734 if (!a->IsNormalAircraft()) continue;
2735 if (a->targetairport == st->index && a->state != FLYING) {
2736 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2737 }
2738 }
2739
2740 if (flags.Test(DoCommandFlag::Execute)) {
2741 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2742 TileIndex tile_cur = st->airport.GetHangarTile(i);
2743 OrderBackup::Reset(tile_cur, false);
2745 }
2746
2747 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2748 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2749 * need of recalculation */
2750 uint dist;
2751 Town *nearest = AirportGetNearestTown(st, dist);
2753
2756 }
2757 }
2758
2759 for (TileIndex tile_cur : st->airport) {
2760 if (!st->TileBelongsToAirport(tile_cur)) continue;
2761
2762 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2763 if (ret.Failed()) return ret;
2764
2765 cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
2766
2767 if (flags.Test(DoCommandFlag::Execute)) {
2768 DoClearSquare(tile_cur);
2769 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur.base());
2770 }
2771 }
2772
2773 if (flags.Test(DoCommandFlag::Execute)) {
2774 /* Clear the persistent storage. */
2775 delete st->airport.psa;
2776
2777 st->rect.AfterRemoveRect(st, st->airport);
2778
2779 st->airport.Clear();
2782
2784
2785 Company::Get(st->owner)->infrastructure.airport--;
2786
2787 st->AfterStationTileSetChange(false, StationType::Airport);
2788
2789 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
2790 }
2791
2792 return cost;
2793}
2794
2802{
2803 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2804 Station *st = Station::Get(station_id);
2805
2807
2808 CommandCost ret = CheckOwnership(st->owner);
2809 if (ret.Failed()) return ret;
2810
2811 if (flags.Test(DoCommandFlag::Execute)) {
2814 }
2815 return CommandCost();
2816}
2817
2824bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2825{
2826 for (const OrderList *orderlist : OrderList::Iterate()) {
2827 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2828 assert(v != nullptr);
2829 if ((v->owner == company) != include_company) continue;
2830
2831 for (const Order &order : orderlist->GetOrders()) {
2832 if (order.GetDestination() == station && (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT))) {
2833 return true;
2834 }
2835 }
2836 }
2837 return false;
2838}
2839
2840static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
2841 {-1, 0},
2842 { 0, 0},
2843 { 0, 0},
2844 { 0, -1}
2845};
2846static const uint8_t _dock_w_chk[4] = { 2, 1, 2, 1 };
2847static const uint8_t _dock_h_chk[4] = { 1, 2, 1, 2 };
2848
2857CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
2858{
2859 bool reuse = (station_to_join != NEW_STATION);
2860 if (!reuse) station_to_join = StationID::Invalid();
2861 bool distant_join = (station_to_join != StationID::Invalid());
2862
2863 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2864
2866 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2867 direction = ReverseDiagDir(direction);
2868
2869 /* Docks cannot be placed on rapids */
2870 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2871
2873 if (ret.Failed()) return ret;
2874
2875 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
2876
2877 CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
2878 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
2879 if (ret.Failed()) return ret;
2880 cost.AddCost(ret.GetCost());
2881
2882 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2883
2884 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2885 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2886 }
2887
2888 if (IsBridgeAbove(tile_cur)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
2889
2890 /* Get the water class of the water tile before it is cleared.*/
2891 WaterClass wc = GetWaterClass(tile_cur);
2892
2893 bool add_cost = !IsWaterTile(tile_cur);
2894 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
2895 if (ret.Failed()) return ret;
2896 if (add_cost) cost.AddCost(ret.GetCost());
2897
2898 tile_cur += TileOffsByDiagDir(direction);
2899 if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) {
2900 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2901 }
2902
2903 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2904 _dock_w_chk[direction], _dock_h_chk[direction]);
2905
2906 /* middle */
2907 Station *st = nullptr;
2908 ret = FindJoiningStation(StationID::Invalid(), station_to_join, adjacent, dock_area, &st);
2909 if (ret.Failed()) return ret;
2910
2911 /* Distant join */
2912 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2913
2914 ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
2915 if (ret.Failed()) return ret;
2916
2917 if (flags.Test(DoCommandFlag::Execute)) {
2918 st->ship_station.Add(tile);
2919 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2920 st->ship_station.Add(flat_tile);
2922
2923 st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
2924
2925 /* If the water part of the dock is on a canal, update infrastructure counts.
2926 * This is needed as we've cleared that tile before.
2927 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2928 * See: MakeWaterKeepingClass() */
2929 if (wc == WATER_CLASS_CANAL && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WATER_CLASS_CANAL && IsTileOwner(flat_tile, _current_company))) {
2930 Company::Get(st->owner)->infrastructure.water++;
2931 }
2932 Company::Get(st->owner)->infrastructure.station += 2;
2933
2934 MakeDock(tile, st->owner, st->index, direction, wc);
2935 UpdateStationDockingTiles(st);
2936
2937 st->AfterStationTileSetChange(true, StationType::Dock);
2938 }
2939
2940 return cost;
2941}
2942
2943void RemoveDockingTile(TileIndex t)
2944{
2945 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2946 TileIndex tile = t + TileOffsByDiagDir(d);
2947 if (!IsValidTile(tile)) continue;
2948
2949 if (IsTileType(tile, MP_STATION)) {
2950 Station *st = Station::GetByTile(tile);
2951 if (st != nullptr) UpdateStationDockingTiles(st);
2952 } else if (IsTileType(tile, MP_INDUSTRY)) {
2954 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
2955 }
2956 }
2957}
2958
2965{
2966 assert(IsValidTile(tile));
2967
2968 /* Clear and maybe re-set docking tile */
2969 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2970 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
2971 if (!IsValidTile(docking_tile)) continue;
2972
2973 if (IsPossibleDockingTile(docking_tile)) {
2974 SetDockingTile(docking_tile, false);
2975 CheckForDockingTile(docking_tile);
2976 }
2977 }
2978}
2979
2986{
2987 assert(IsDockTile(t));
2988
2989 StationGfx gfx = GetStationGfx(t);
2990 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
2991
2992 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2993 TileIndex tile = t + TileOffsByDiagDir(d);
2994 if (!IsValidTile(tile)) continue;
2995 if (!IsDockTile(tile)) continue;
2996 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
2997 }
2998
2999 return INVALID_TILE;
3000}
3001
3009{
3010 Station *st = Station::GetByTile(tile);
3011 CommandCost ret = CheckOwnership(st->owner);
3012 if (ret.Failed()) return ret;
3013
3014 if (!IsDockTile(tile)) return CMD_ERROR;
3015
3016 TileIndex tile1 = FindDockLandPart(tile);
3017 if (tile1 == INVALID_TILE) return CMD_ERROR;
3018 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
3019
3020 ret = EnsureNoVehicleOnGround(tile1);
3021 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
3022 if (ret.Failed()) return ret;
3023
3024 if (flags.Test(DoCommandFlag::Execute)) {
3025 DoClearSquare(tile1);
3026 MarkTileDirtyByTile(tile1);
3027 MakeWaterKeepingClass(tile2, st->owner);
3028
3029 st->rect.AfterRemoveTile(st, tile1);
3030 st->rect.AfterRemoveTile(st, tile2);
3031
3032 MakeShipStationAreaSmaller(st);
3033 if (st->ship_station.tile == INVALID_TILE) {
3034 st->ship_station.Clear();
3035 st->docking_station.Clear();
3038 }
3039
3040 Company::Get(st->owner)->infrastructure.station -= 2;
3041
3042 st->AfterStationTileSetChange(false, StationType::Dock);
3043
3046
3047 for (Ship *s : Ship::Iterate()) {
3048 /* Find all ships going to our dock. */
3049 if (s->current_order.GetDestination() != st->index) {
3050 continue;
3051 }
3052
3053 /* Find ships that are marked as "loading" but are no longer on a
3054 * docking tile. Force them to leave the station (as they were loading
3055 * on the removed dock). */
3056 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
3057 s->LeaveStation();
3058 }
3059
3060 /* If we no longer have a dock, mark the order as invalid and send
3061 * the ship to the next order (or, if there is none, make it
3062 * wander the world). */
3063 if (s->current_order.IsType(OT_GOTO_STATION) && !st->facilities.Test(StationFacility::Dock)) {
3064 s->SetDestTile(s->GetOrderStationLocation(st->index));
3065 }
3066 }
3067 }
3068
3069 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
3070}
3071
3079{
3080 const auto &layouts = _station_display_datas[to_underlying(st)];
3081 if (gfx >= layouts.size()) gfx &= 1;
3082 return layouts.data() + gfx;
3083}
3084
3094bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3095{
3096 bool snow_desert;
3097 switch (*ground) {
3098 case SPR_RAIL_TRACK_X:
3099 case SPR_MONO_TRACK_X:
3100 case SPR_MGLV_TRACK_X:
3101 snow_desert = false;
3102 *overlay_offset = RTO_X;
3103 break;
3104
3105 case SPR_RAIL_TRACK_Y:
3106 case SPR_MONO_TRACK_Y:
3107 case SPR_MGLV_TRACK_Y:
3108 snow_desert = false;
3109 *overlay_offset = RTO_Y;
3110 break;
3111
3112 case SPR_RAIL_TRACK_X_SNOW:
3113 case SPR_MONO_TRACK_X_SNOW:
3114 case SPR_MGLV_TRACK_X_SNOW:
3115 snow_desert = true;
3116 *overlay_offset = RTO_X;
3117 break;
3118
3119 case SPR_RAIL_TRACK_Y_SNOW:
3120 case SPR_MONO_TRACK_Y_SNOW:
3121 case SPR_MGLV_TRACK_Y_SNOW:
3122 snow_desert = true;
3123 *overlay_offset = RTO_Y;
3124 break;
3125
3126 default:
3127 return false;
3128 }
3129
3130 if (ti != nullptr) {
3131 /* Decide snow/desert from tile */
3133 case LandscapeType::Arctic:
3134 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3135 break;
3136
3137 case LandscapeType::Tropic:
3138 snow_desert = GetTropicZone(ti->tile) == TROPICZONE_DESERT;
3139 break;
3140
3141 default:
3142 break;
3143 }
3144 }
3145
3146 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3147 return true;
3148}
3149
3156static BridgePillarFlags GetStationBlockedPillars(std::span<const BridgeableTileInfo> bridgeable_info, uint8_t layout)
3157{
3158 if (layout < std::size(bridgeable_info)) return bridgeable_info[layout].disallowed_pillars;
3159 return BRIDGEPILLARFLAGS_ALL;
3160}
3161
3171{
3172 if (statspec == nullptr || !statspec->flags.Test(StationSpecFlag::CustomFoundations)) return false;
3173
3174 /* Station has custom foundations.
3175 * Check whether the foundation continues beyond the tile's upper sides. */
3176 uint edge_info = 0;
3177 auto [slope, z] = GetFoundationPixelSlope(ti->tile);
3178 if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
3179 if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
3180
3181 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, gfx, edge_info);
3182 if (image == 0) return false;
3183
3185 /* Station provides extended foundations. */
3186 static constexpr uint8_t foundation_parts[] = {
3187 UINT8_MAX, UINT8_MAX, UINT8_MAX, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3188 UINT8_MAX, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3189 UINT8_MAX, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3190 7, 8, 9, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3191 };
3192 assert(ti->tileh < std::size(foundation_parts));
3193 if (foundation_parts[ti->tileh] == UINT8_MAX) return false;
3194
3195 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3196 } else {
3197 /* Draw simple foundations, built up from 8 possible foundation sprites.
3198 * Each set bit represents one of the eight composite sprites to be drawn.
3199 * 'Invalid' entries will not drawn but are included for completeness. */
3200 static constexpr uint8_t composite_foundation_parts[] = {
3201 0b0000'0000, 0b1101'0001, 0b1110'0100, 0b1110'0000, // Invalid, Invalid, Invalid, SLOPE_SW
3202 0b1100'1010, 0b1100'1001, 0b1100'0100, 0b1100'0000, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3203 0b1101'0010, 0b1001'0001, 0b1110'0100, 0b1010'0000, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3204 0b0100'1010, 0b0000'1001, 0b0100'0100, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3205 };
3206 assert(ti->tileh < std::size(composite_foundation_parts));
3207
3208 uint8_t parts = composite_foundation_parts[ti->tileh];
3209
3210 /* If foundations continue beyond the tile's upper sides then mask out the last two pieces. */
3211 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3212 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3213
3214 /* We always have to draw at least one sprite, so if we have no parts to draw fall back to default foundation. */
3215 if (parts == 0) return false;
3216
3218 for (uint i : SetBitIterator(parts)) {
3219 AddSortableSpriteToDraw(image + i, PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3220 }
3222 }
3223
3224 OffsetGroundSprite(0, -static_cast<int>(TILE_HEIGHT));
3226
3227 return true;
3228}
3229
3230static void DrawTile_Station(TileInfo *ti)
3231{
3232 const NewGRFSpriteLayout *layout = nullptr;
3233 SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_layout and t
3234 DrawTileSpriteSpan tmp_layout;
3235 const DrawTileSprites *t = nullptr;
3236 int32_t total_offset;
3237 const RailTypeInfo *rti = nullptr;
3238 uint32_t relocation = 0;
3239 uint32_t ground_relocation = 0;
3240 BaseStation *st = nullptr;
3241 const StationSpec *statspec = nullptr;
3242 uint tile_layout = 0;
3243 auto bridgeable_info = GetStationBridgeableTileInfo(GetStationType(ti->tile));
3244
3245 if (HasStationRail(ti->tile)) {
3246 rti = GetRailTypeInfo(GetRailType(ti->tile));
3247 total_offset = rti->GetRailtypeSpriteOffset();
3248
3249 if (IsCustomStationSpecIndex(ti->tile)) {
3250 /* look for customization */
3251 st = BaseStation::GetByTile(ti->tile);
3252 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3253
3254 if (statspec != nullptr) {
3255 tile_layout = GetStationGfx(ti->tile);
3256
3258 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3259 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
3260 }
3261
3262 /* Ensure the chosen tile layout is valid for this custom station */
3263 if (!statspec->renderdata.empty()) {
3264 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3265 if (!layout->NeedsPreprocessing()) {
3266 t = layout;
3267 layout = nullptr;
3268 }
3269 }
3270 }
3271 }
3272 if (statspec != nullptr) bridgeable_info = statspec->bridgeable_info;
3273 } else {
3274 total_offset = 0;
3275 }
3276
3277 StationGfx gfx = GetStationGfx(ti->tile);
3278 if (IsAirport(ti->tile)) {
3279 gfx = GetAirportGfx(ti->tile);
3280 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3281 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3282 if (ats->grf_prop.HasSpriteGroups() && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3283 return;
3284 }
3285 /* No sprite group (or no valid one) found, meaning no graphics associated.
3286 * Use the substitute one instead */
3287 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3288 gfx = ats->grf_prop.subst_id;
3289 }
3290 switch (gfx) {
3291 case APT_RADAR_GRASS_FENCE_SW:
3292 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3293 break;
3294 case APT_GRASS_FENCE_NE_FLAG:
3295 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3296 break;
3297 case APT_RADAR_FENCE_SW:
3298 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3299 break;
3300 case APT_RADAR_FENCE_NE:
3301 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3302 break;
3303 case APT_GRASS_FENCE_NE_FLAG_2:
3304 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3305 break;
3306 }
3307 }
3308
3309 Owner owner = GetTileOwner(ti->tile);
3310
3311 PaletteID palette;
3312 if (Company::IsValidID(owner)) {
3313 palette = GetCompanyPalette(owner);
3314 } else {
3315 /* Some stations are not owner by a company, namely oil rigs */
3316 palette = PALETTE_TO_GREY;
3317 }
3318
3319 if (layout == nullptr && t == nullptr) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3320
3321 /* don't show foundation for docks */
3322 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3323 if (!DrawCustomStationFoundations(statspec, st, ti, tile_layout)) {
3325 }
3326 }
3327
3328 bool draw_ground = false;
3329
3330 if (IsBuoy(ti->tile)) {
3331 DrawWaterClassGround(ti);
3332 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
3333 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3334 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3335 if (ti->tileh == SLOPE_FLAT) {
3336 DrawWaterClassGround(ti);
3337 } else {
3338 assert(IsDock(ti->tile));
3339 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3340 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WATER_CLASS_INVALID;
3341 if (wc == WATER_CLASS_SEA) {
3342 DrawShoreTile(ti->tileh);
3343 } else {
3344 DrawClearLandTile(ti, 3);
3345 }
3346 }
3347 } else if (IsRoadWaypointTile(ti->tile)) {
3349 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3350 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3351 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3352 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3353 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3354 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3355
3356 if (ti->tileh != SLOPE_FLAT) {
3358 }
3359
3360 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3361 } else {
3362 if (layout != nullptr) {
3363 /* Sprite layout which needs preprocessing */
3364 bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround);
3365 processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3366 GetCustomStationRelocation(processor, statspec, st, ti->tile);
3367 tmp_layout = processor.GetLayout();
3368 t = &tmp_layout;
3369 total_offset = 0;
3370 } else if (statspec != nullptr) {
3371 /* Simple sprite layout */
3372 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3374 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3375 }
3376 ground_relocation += rti->fallback_railtype;
3377 }
3378
3379 draw_ground = true;
3380 }
3381
3382 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3383 SpriteID image = t->ground.sprite;
3384 PaletteID pal = t->ground.pal;
3385 RailTrackOffset overlay_offset;
3386 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3387 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
3388 DrawGroundSprite(image, PAL_NONE);
3389 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3390
3391 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3392 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
3393 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3394 }
3395 } else {
3396 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3397 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3398 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3399
3400 /* PBS debugging, draw reserved tracks darker */
3401 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3402 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
3403 }
3404 }
3405 }
3406
3408
3409 if (IsAnyRoadStop(ti->tile)) {
3410 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3411 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3412 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3413 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3414
3415 StationGfx view = GetStationGfx(ti->tile);
3416 StationType type = GetStationType(ti->tile);
3417
3418 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3419 RoadStopDrawModes stop_draw_mode{};
3420 if (stopspec != nullptr) {
3421 stop_draw_mode = stopspec->draw_mode;
3422 st = BaseStation::GetByTile(ti->tile);
3423 std::array<int32_t, 1> regs100;
3424 auto result = GetRoadStopLayout(ti, stopspec, st, type, view, regs100);
3425 if (result.has_value()) {
3426 if (stopspec->flags.Test(RoadStopSpecFlag::DrawModeRegister)) {
3427 stop_draw_mode = static_cast<RoadStopDrawModes>(regs100[0]);
3428 }
3429 if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) {
3430 draw_ground = true;
3431 }
3432 processor = std::move(*result);
3433 tmp_layout = processor.GetLayout();
3434 t = &tmp_layout;
3435 }
3436 }
3437
3438 /* Draw ground sprite */
3439 if (draw_ground) {
3440 SpriteID image = t->ground.sprite;
3441 PaletteID pal = t->ground.pal;
3442 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3443 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3444 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3445 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3446 }
3447 }
3448
3449 if (IsDriveThroughStopTile(ti->tile)) {
3450 if (type != StationType::RoadWaypoint && (stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Overlay))) {
3451 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
3452 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3453 }
3454 } else {
3455 /* Non-drivethrough road stops are only valid for roads. */
3456 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3457
3458 if ((stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Road)) && road_rti->UsesOverlay()) {
3459 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
3460 DrawGroundSprite(ground + view, PAL_NONE);
3461 }
3462 }
3463 if (stopspec != nullptr) bridgeable_info = stopspec->bridgeable_info;
3464
3465 if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
3466 /* Draw road, tram catenary */
3467 DrawRoadCatenary(ti);
3468 }
3469 }
3470
3471 if (IsRailWaypoint(ti->tile)) {
3472 /* Don't offset the waypoint graphics; they're always the same. */
3473 total_offset = 0;
3474 }
3475
3476 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
3477 DrawBridgeMiddle(ti, GetStationBlockedPillars(bridgeable_info, GetStationGfx(ti->tile)));
3478}
3479
3480void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3481{
3482 int32_t total_offset = 0;
3484 const DrawTileSprites *t = GetStationTileLayout(st, image);
3485 const RailTypeInfo *railtype_info = nullptr;
3486
3487 if (railtype != INVALID_RAILTYPE) {
3488 railtype_info = GetRailTypeInfo(railtype);
3489 total_offset = railtype_info->GetRailtypeSpriteOffset();
3490 }
3491
3492 SpriteID img = t->ground.sprite;
3493 RailTrackOffset overlay_offset;
3494 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3495 SpriteID ground = GetCustomRailSprite(railtype_info, INVALID_TILE, RTSG_GROUND);
3496 DrawSprite(img, PAL_NONE, x, y);
3497 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3498 } else {
3499 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3500 }
3501
3502 if (roadtype != INVALID_ROADTYPE) {
3503 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3504 if (image >= 4) {
3505 /* Drive-through stop */
3506 uint sprite_offset = 5 - image;
3507
3508 /* Road underlay takes precedence over tram */
3509 if (roadtype_info->UsesOverlay()) {
3510 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_GROUND);
3511 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3512
3513 SpriteID overlay = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_OVERLAY);
3514 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3515 } else if (RoadTypeIsTram(roadtype)) {
3516 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3517 }
3518 } else {
3519 /* Bay stop */
3520 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3521 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_ROADSTOP);
3522 DrawSprite(ground + image, PAL_NONE, x, y);
3523 }
3524 }
3525 }
3526
3527 /* Default waypoint has no railtype specific sprites */
3528 DrawRailTileSeqInGUI(x, y, t, (st == StationType::RailWaypoint || st == StationType::RoadWaypoint) ? 0 : total_offset, 0, pal);
3529}
3530
3531static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)
3532{
3533 return GetTileMaxPixelZ(tile);
3534}
3535
3536static Foundation GetFoundation_Station(TileIndex, Slope tileh)
3537{
3538 return FlatteningFoundation(tileh);
3539}
3540
3541static void FillTileDescRoadStop(TileIndex tile, TileDesc &td)
3542{
3543 RoadType road_rt = GetRoadTypeRoad(tile);
3544 RoadType tram_rt = GetRoadTypeTram(tile);
3545 Owner road_owner = INVALID_OWNER;
3546 Owner tram_owner = INVALID_OWNER;
3547 if (road_rt != INVALID_ROADTYPE) {
3548 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3549 td.roadtype = rti->strings.name;
3550 td.road_speed = rti->max_speed / 2;
3551 road_owner = GetRoadOwner(tile, RTT_ROAD);
3552 }
3553
3554 if (tram_rt != INVALID_ROADTYPE) {
3555 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3556 td.tramtype = rti->strings.name;
3557 td.tram_speed = rti->max_speed / 2;
3558 tram_owner = GetRoadOwner(tile, RTT_TRAM);
3559 }
3560
3561 if (IsDriveThroughStopTile(tile)) {
3562 /* Is there a mix of owners? */
3563 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
3564 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
3565 uint i = 1;
3566 if (road_owner != INVALID_OWNER) {
3567 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3568 td.owner[i] = road_owner;
3569 i++;
3570 }
3571 if (tram_owner != INVALID_OWNER) {
3572 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3573 td.owner[i] = tram_owner;
3574 }
3575 }
3576 }
3577}
3578
3579void FillTileDescRailStation(TileIndex tile, TileDesc &td)
3580{
3581 const StationSpec *spec = GetStationSpec(tile);
3582
3583 if (spec != nullptr) {
3585 td.station_name = spec->name;
3586
3587 if (spec->grf_prop.HasGrfFile()) {
3588 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3589 td.grf = gc->GetName();
3590 }
3591 }
3592
3593 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3594 td.rail_speed = rti->max_speed;
3595 td.railtype = rti->strings.name;
3596}
3597
3598void FillTileDescAirport(TileIndex tile, TileDesc &td)
3599{
3600 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3602 td.airport_name = as->name;
3603
3605 td.airport_tile_name = ats->name;
3606
3607 if (as->grf_prop.HasGrfFile()) {
3608 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3609 td.grf = gc->GetName();
3610 } else if (ats->grf_prop.HasGrfFile()) {
3611 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3612 td.grf = gc->GetName();
3613 }
3614}
3615
3616static void GetTileDesc_Station(TileIndex tile, TileDesc &td)
3617{
3618 td.owner[0] = GetTileOwner(tile);
3620
3621 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3622 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3623 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3624
3625 StringID str;
3626 switch (GetStationType(tile)) {
3627 default: NOT_REACHED();
3628 case StationType::Rail: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3629 case StationType::Airport:
3630 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3631 break;
3632 case StationType::Truck: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3633 case StationType::Bus: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3634 case StationType::Oilrig: {
3635 const Industry *i = Station::GetByTile(tile)->industry;
3636 const IndustrySpec *is = GetIndustrySpec(i->type);
3637 td.owner[0] = i->owner;
3638 str = is->name;
3639 if (is->grf_prop.HasGrfFile()) td.grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3640 break;
3641 }
3642 case StationType::Dock: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3643 case StationType::Buoy: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3644 case StationType::RailWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3645 case StationType::RoadWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3646 }
3647 td.str = str;
3648}
3649
3650
3651static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
3652{
3653 TrackBits trackbits = TRACK_BIT_NONE;
3654
3655 switch (mode) {
3656 case TRANSPORT_RAIL:
3657 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3658 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
3659 }
3660 break;
3661
3662 case TRANSPORT_WATER:
3663 /* buoy is coded as a station, it is always on open water */
3664 if (IsBuoy(tile)) {
3665 trackbits = TRACK_BIT_ALL;
3666 /* remove tracks that connect NE map edge */
3667 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
3668 /* remove tracks that connect NW map edge */
3669 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
3670 }
3671 break;
3672
3673 case TRANSPORT_ROAD:
3674 if (IsAnyRoadStop(tile)) {
3675 RoadTramType rtt = (RoadTramType)sub_mode;
3676 if (!HasTileRoadType(tile, rtt)) break;
3677
3678 if (IsBayRoadStopTile(tile)) {
3679 DiagDirection dir = GetBayRoadStopDir(tile);
3680 if (side != INVALID_DIAGDIR && dir != side) break;
3681 trackbits = DiagDirToDiagTrackBits(dir);
3682 } else {
3683 Axis axis = GetDriveThroughStopAxis(tile);
3684 if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
3685 trackbits = AxisToTrackBits(axis);
3686 }
3687 }
3688 break;
3689
3690 default:
3691 break;
3692 }
3693
3695}
3696
3697
3698static void TileLoop_Station(TileIndex tile)
3699{
3700 auto *st = BaseStation::GetByTile(tile);
3701 switch (GetStationType(tile)) {
3702 case StationType::Airport:
3703 TriggerAirportTileAnimation(Station::From(st), tile, AirportAnimationTrigger::TileLoop);
3704 break;
3705
3706 case StationType::Rail:
3707 case StationType::RailWaypoint:
3708 TriggerStationAnimation(st, tile, StationAnimationTrigger::TileLoop);
3709 break;
3710
3711 case StationType::Dock:
3712 if (!IsTileFlat(tile)) break; // only handle water part
3713 [[fallthrough]];
3714
3715 case StationType::Oilrig: //(station part)
3716 case StationType::Buoy:
3717 TileLoop_Water(tile);
3718 break;
3719
3720 case StationType::Truck:
3721 case StationType::Bus:
3722 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3723 break;
3724
3725 case StationType::RoadWaypoint: {
3727 case LandscapeType::Arctic:
3728 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3730 MarkTileDirtyByTile(tile);
3731 }
3732 break;
3733
3734 case LandscapeType::Tropic:
3737 MarkTileDirtyByTile(tile);
3738 }
3739 break;
3740
3741 default: break;
3742 }
3743
3744 HouseZone new_zone = HouseZone::TownEdge;
3745 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3746 if (t != nullptr) {
3747 new_zone = GetTownRadiusGroup(t, tile);
3748 }
3749
3750 /* Adjust road ground type depending on 'new_zone' */
3751 Roadside new_rs = new_zone != HouseZone::TownEdge ? ROADSIDE_PAVED : ROADSIDE_GRASS;
3752 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3753
3754 if (new_rs != cur_rs) {
3755 SetRoadWaypointRoadside(tile, cur_rs == ROADSIDE_BARREN ? new_rs : ROADSIDE_BARREN);
3756 MarkTileDirtyByTile(tile);
3757 }
3758
3759 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3760 break;
3761 }
3762
3763 default: break;
3764 }
3765}
3766
3767
3768static void AnimateTile_Station(TileIndex tile)
3769{
3770 if (HasStationRail(tile)) {
3771 AnimateStationTile(tile);
3772 return;
3773 }
3774
3775 if (IsAirport(tile)) {
3776 AnimateAirportTile(tile);
3777 return;
3778 }
3779
3780 if (IsAnyRoadStopTile(tile)) {
3781 AnimateRoadStopTile(tile);
3782 return;
3783 }
3784}
3785
3786
3787static bool ClickTile_Station(TileIndex tile)
3788{
3789 const BaseStation *bst = BaseStation::GetByTile(tile);
3790
3793 } else if (IsHangar(tile)) {
3794 const Station *st = Station::From(bst);
3796 } else {
3798 }
3799 return true;
3800}
3801
3802static VehicleEnterTileStates VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
3803{
3804 if (v->type == VEH_TRAIN) {
3805 StationID station_id = GetStationIndex(tile);
3806 if (!v->current_order.ShouldStopAtStation(v, station_id)) return {};
3807 if (!IsRailStation(tile) || !v->IsFrontEngine()) return {};
3808
3809 int station_ahead;
3810 int station_length;
3811 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3812
3813 /* Stop whenever that amount of station ahead + the distance from the
3814 * begin of the platform to the stop location is longer than the length
3815 * of the platform. Station ahead 'includes' the current tile where the
3816 * vehicle is on, so we need to subtract that. */
3817 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return {};
3818
3820
3821 x &= 0xF;
3822 y &= 0xF;
3823
3824 if (DiagDirToAxis(dir) != AXIS_X) std::swap(x, y);
3825 if (y == TILE_SIZE / 2) {
3826 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
3827 stop &= TILE_SIZE - 1;
3828
3829 if (x == stop) {
3830 return VehicleEnterTileState::EnteredStation; // enter station
3831 } else if (x < stop) {
3833 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3834 if (spd < v->cur_speed) v->cur_speed = spd;
3835 }
3836 }
3837 } else if (v->type == VEH_ROAD) {
3839 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
3840 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3841 /* Attempt to allocate a parking bay in a road stop */
3842 if (RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv)) return {};
3844 }
3845 }
3846 }
3847
3848 return {};
3849}
3850
3856{
3857 /* Collect cargoes accepted since the last big tick. */
3858 CargoTypes cargoes = 0;
3859 for (CargoType cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
3860 if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) SetBit(cargoes, cargo_type);
3861 }
3862
3863 /* Anything to do? */
3864 if (cargoes == 0) return;
3865
3866 /* Loop over all houses in the catchment. */
3868 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3869 if (IsTileType(tile, MP_HOUSE)) {
3871 }
3872 }
3873}
3874
3882{
3883 if (!st->IsInUse()) {
3884 if (++st->delete_ctr >= 8) delete st;
3885 return false;
3886 }
3887
3888 if (Station::IsExpected(st)) {
3890
3891 for (GoodsEntry &ge : Station::From(st)->goods) {
3892 ge.status.Reset(GoodsEntry::State::AcceptedBigtick);
3893 }
3894 }
3895
3896
3898
3899 return true;
3900}
3901
3902static inline void byte_inc_sat(uint8_t *p)
3903{
3904 uint8_t b = *p + 1;
3905 if (b != 0) *p = b;
3906}
3907
3914static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3915{
3916 /* If truncating also punish the source stations' ratings to
3917 * decrease the flow of incoming cargo. */
3918
3919 if (!ge->HasData()) return;
3920
3921 StationCargoAmountMap waiting_per_source;
3922 ge->GetData().cargo.Truncate(amount, &waiting_per_source);
3923 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3924 Station *source_station = Station::GetIfValid(i->first);
3925 if (source_station == nullptr) continue;
3926
3927 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3928 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3929 }
3930}
3931
3932static void UpdateStationRating(Station *st)
3933{
3934 bool waiting_changed = false;
3935
3936 byte_inc_sat(&st->time_since_load);
3937 byte_inc_sat(&st->time_since_unload);
3938
3939 for (const CargoSpec *cs : CargoSpec::Iterate()) {
3940 GoodsEntry *ge = &st->goods[cs->Index()];
3941 /* Slowly increase the rating back to its original level in the case we
3942 * didn't deliver cargo yet to this station. This happens when a bribe
3943 * failed while you didn't moved that cargo yet to a station. */
3944 if (!ge->HasRating() && ge->rating < INITIAL_STATION_RATING) {
3945 ge->rating++;
3946 }
3947
3948 /* Only change the rating if we are moving this cargo */
3949 if (ge->HasRating()) {
3950 byte_inc_sat(&ge->time_since_pickup);
3953 ge->last_speed = 0;
3954 TruncateCargo(cs, ge);
3955 waiting_changed = true;
3956 continue;
3957 }
3958
3959 bool skip = false;
3960 int rating = 0;
3961 uint waiting = ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0;
3962
3963 /* num_dests is at least 1 if there is any cargo as
3964 * StationID::Invalid() is also a destination.
3965 */
3966 uint num_dests = ge->HasData() ? static_cast<uint>(ge->GetData().cargo.Packets()->MapSize()) : 0;
3967
3968 /* Average amount of cargo per next hop, but prefer solitary stations
3969 * with only one or two next hops. They are allowed to have more
3970 * cargo waiting per next hop.
3971 * With manual cargo distribution waiting_avg = waiting / 2 as then
3972 * StationID::Invalid() is the only destination.
3973 */
3974 uint waiting_avg = waiting / (num_dests + 1);
3975
3977 ge->rating = rating = MAX_STATION_RATING;
3978 skip = true;
3979 } else if (cs->callback_mask.Test(CargoCallbackMask::StationRatingCalc)) {
3980 /* Perform custom station rating. If it succeeds the speed, days in transit and
3981 * waiting cargo ratings must not be executed. */
3982
3983 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
3984 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
3985
3986 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
3987 | (ClampTo<uint16_t>(ge->max_waiting_cargo) << 8)
3988 | (ClampTo<uint8_t>(last_speed) << 24);
3989 /* Convert to the 'old' vehicle types */
3990 uint32_t var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
3991 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
3992 if (callback != CALLBACK_FAILED) {
3993 skip = true;
3994 rating = GB(callback, 0, 14);
3995
3996 /* Simulate a 15 bit signed value */
3997 if (HasBit(callback, 14)) rating -= 0x4000;
3998 }
3999 }
4000
4001 if (!skip) {
4002 int b = ge->last_speed - 85;
4003 if (b >= 0) rating += b >> 2;
4004
4005 uint8_t waittime = ge->time_since_pickup;
4006 if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
4007 if (waittime <= 21) rating += 25;
4008 if (waittime <= 12) rating += 25;
4009 if (waittime <= 6) rating += 45;
4010 if (waittime <= 3) rating += 35;
4011
4012 rating -= 90;
4013 if (ge->max_waiting_cargo <= 1500) rating += 55;
4014 if (ge->max_waiting_cargo <= 1000) rating += 35;
4015 if (ge->max_waiting_cargo <= 600) rating += 10;
4016 if (ge->max_waiting_cargo <= 300) rating += 20;
4017 if (ge->max_waiting_cargo <= 100) rating += 10;
4018 }
4019
4020 if (Company::IsValidID(st->owner) && st->town->statues.Test(st->owner)) rating += 26;
4021
4022 uint8_t age = ge->last_age;
4023 if (age < 3) rating += 10;
4024 if (age < 2) rating += 10;
4025 if (age < 1) rating += 13;
4026
4027 {
4028 int or_ = ge->rating; // old rating
4029
4030 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
4031 ge->rating = rating = ClampTo<uint8_t>(or_ + Clamp(rating - or_, -2, 2));
4032
4033 /* if rating is <= 64 and more than 100 items waiting on average per destination,
4034 * remove some random amount of goods from the station */
4035 if (rating <= 64 && waiting_avg >= 100) {
4036 int dec = Random() & 0x1F;
4037 if (waiting_avg < 200) dec &= 7;
4038 waiting -= (dec + 1) * num_dests;
4039 waiting_changed = true;
4040 }
4041
4042 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
4043 if (rating <= 127 && waiting != 0) {
4044 uint32_t r = Random();
4045 if (rating <= (int)GB(r, 0, 7)) {
4046 /* Need to have int, otherwise it will just overflow etc. */
4047 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) - 1) * num_dests), 0);
4048 waiting_changed = true;
4049 }
4050 }
4051
4052 /* At some point we really must cap the cargo. Previously this
4053 * was a strict 4095, but now we'll have a less strict, but
4054 * increasingly aggressive truncation of the amount of cargo. */
4055 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
4056 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
4057 static const uint MAX_WAITING_CARGO = 1 << 15;
4058
4059 if (waiting > WAITING_CARGO_THRESHOLD) {
4060 uint difference = waiting - WAITING_CARGO_THRESHOLD;
4061 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
4062
4063 waiting = std::min(waiting, MAX_WAITING_CARGO);
4064 waiting_changed = true;
4065 }
4066
4067 /* We can't truncate cargo that's already reserved for loading.
4068 * Thus StoredCount() here. */
4069 if (waiting_changed && waiting < (ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0)) {
4070 /* Feed back the exact own waiting cargo at this station for the
4071 * next rating calculation. */
4072 ge->max_waiting_cargo = 0;
4073
4074 TruncateCargo(cs, ge, ge->GetData().cargo.AvailableCount() - waiting);
4075 } else {
4076 /* If the average number per next hop is low, be more forgiving. */
4077 ge->max_waiting_cargo = waiting_avg;
4078 }
4079 }
4080 }
4081 }
4082
4083 StationID index = st->index;
4084 if (waiting_changed) {
4085 SetWindowDirty(WC_STATION_VIEW, index); // update whole window
4086 } else {
4087 SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
4088 }
4089}
4090
4099void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
4100{
4101 GoodsEntry &ge = st->goods[cargo];
4102
4103 /* Reroute cargo in station. */
4104 if (ge.HasData()) ge.GetData().cargo.Reroute(UINT_MAX, &ge.GetData().cargo, avoid, avoid2, &ge);
4105
4106 /* Reroute cargo staged to be transferred. */
4107 for (Vehicle *v : st->loading_vehicles) {
4108 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4109 if (u->cargo_type != cargo) continue;
4110 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4111 }
4112 }
4113}
4114
4124{
4125 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
4126 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL);
4127 GoodsEntry &ge = from->goods[cargo];
4129 if (lg == nullptr) continue;
4130 std::vector<NodeID> to_remove{};
4131 for (Edge &edge : (*lg)[ge.node].edges) {
4132 Station *to = Station::Get((*lg)[edge.dest_node].station);
4133 assert(to->goods[cargo].node == edge.dest_node);
4134 assert(TimerGameEconomy::date >= edge.LastUpdate());
4136 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4137 bool updated = false;
4138
4139 if (auto_distributed) {
4140 /* Have all vehicles refresh their next hops before deciding to
4141 * remove the node. */
4142 std::vector<Vehicle *> vehicles;
4143 for (const OrderList *l : OrderList::Iterate()) {
4144 bool found_from = false;
4145 bool found_to = false;
4146 for (const Order &order : l->GetOrders()) {
4147 if (!order.IsType(OT_GOTO_STATION) && !order.IsType(OT_IMPLICIT)) continue;
4148 if (order.GetDestination() == from->index) {
4149 found_from = true;
4150 if (found_to) break;
4151 } else if (order.GetDestination() == to->index) {
4152 found_to = true;
4153 if (found_from) break;
4154 }
4155 }
4156 if (!found_to || !found_from) continue;
4157 vehicles.push_back(l->GetFirstSharedVehicle());
4158 }
4159
4160 auto iter = vehicles.begin();
4161 while (iter != vehicles.end()) {
4162 Vehicle *v = *iter;
4163 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4165 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4166 }
4167 if (edge.LastUpdate() == TimerGameEconomy::date) {
4168 updated = true;
4169 break;
4170 }
4171
4172 Vehicle *next_shared = v->NextShared();
4173 if (next_shared) {
4174 *iter = next_shared;
4175 ++iter;
4176 } else {
4177 iter = vehicles.erase(iter);
4178 }
4179
4180 if (iter == vehicles.end()) iter = vehicles.begin();
4181 }
4182 }
4183
4184 if (!updated) {
4185 /* If it's still considered dead remove it. */
4186 to_remove.emplace_back(to->goods[cargo].node);
4187 if (ge.HasData()) ge.GetData().flows.DeleteFlows(to->index);
4188 RerouteCargo(from, cargo, to->index, from->index);
4189 }
4190 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4191 edge.Restrict();
4192 if (ge.HasData()) ge.GetData().flows.RestrictFlows(to->index);
4193 RerouteCargo(from, cargo, to->index, from->index);
4194 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4195 edge.Release();
4196 }
4197 }
4198 /* Remove dead edges. */
4199 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4200
4201 assert(TimerGameEconomy::date >= lg->LastCompression());
4203 lg->Compress();
4204 }
4205 }
4206}
4207
4217void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
4218{
4219 GoodsEntry &ge1 = st->goods[cargo];
4220 Station *st2 = Station::Get(next_station_id);
4221 GoodsEntry &ge2 = st2->goods[cargo];
4222 LinkGraph *lg = nullptr;
4223 if (ge1.link_graph == LinkGraphID::Invalid()) {
4224 if (ge2.link_graph == LinkGraphID::Invalid()) {
4226 lg = new LinkGraph(cargo);
4228 ge2.link_graph = lg->index;
4229 ge2.node = lg->AddNode(st2);
4230 } else {
4231 Debug(misc, 0, "Can't allocate link graph");
4232 }
4233 } else {
4234 lg = LinkGraph::Get(ge2.link_graph);
4235 }
4236 if (lg) {
4237 ge1.link_graph = lg->index;
4238 ge1.node = lg->AddNode(st);
4239 }
4240 } else if (ge2.link_graph == LinkGraphID::Invalid()) {
4241 lg = LinkGraph::Get(ge1.link_graph);
4242 ge2.link_graph = lg->index;
4243 ge2.node = lg->AddNode(st2);
4244 } else {
4245 lg = LinkGraph::Get(ge1.link_graph);
4246 if (ge1.link_graph != ge2.link_graph) {
4248 if (lg->Size() < lg2->Size()) {
4250 lg2->Merge(lg); // Updates GoodsEntries of lg
4251 lg = lg2;
4252 } else {
4254 lg->Merge(lg2); // Updates GoodsEntries of lg2
4255 }
4256 }
4257 }
4258 if (lg != nullptr) {
4259 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, modes);
4260 }
4261}
4262
4269void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4270{
4271 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4272 if (v->refit_cap > 0) {
4273 /* The cargo count can indeed be higher than the refit_cap if
4274 * wagons have been auto-replaced and subsequently auto-
4275 * refitted to a higher capacity. The cargo gets redistributed
4276 * among the wagons in that case.
4277 * As usage is not such an important figure anyway we just
4278 * ignore the additional cargo then.*/
4279 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4280 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EdgeUpdateMode::Increase);
4281 }
4282 }
4283}
4284
4285/* called for every station each tick */
4286static void StationHandleSmallTick(BaseStation *st)
4287{
4288 if (st->facilities.Test(StationFacility::Waypoint) || !st->IsInUse()) return;
4289
4290 uint8_t b = st->delete_ctr + 1;
4291 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4292 st->delete_ctr = b;
4293
4294 if (b == 0) UpdateStationRating(Station::From(st));
4295}
4296
4297void OnTick_Station()
4298{
4299 if (_game_mode == GM_EDITOR) return;
4300
4301 for (BaseStation *st : BaseStation::Iterate()) {
4302 StationHandleSmallTick(st);
4303
4304 /* Clean up the link graph about once a week. */
4307 };
4308
4309 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4311 /* Stop processing this station if it was deleted */
4312 if (!StationHandleBigTick(st)) continue;
4313 }
4314
4315 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4317 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4318 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4319 if (Station::IsExpected(st)) TriggerAirportAnimation(Station::From(st), AirportAnimationTrigger::AcceptanceTick);
4320 }
4321 }
4322}
4323
4325static const IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto)
4326{
4327 for (Station *st : Station::Iterate()) {
4328 for (GoodsEntry &ge : st->goods) {
4331 }
4332 }
4333});
4334
4335void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4336{
4337 ForAllStationsRadius(tile, radius, [&](Station *st) {
4338 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4339 for (GoodsEntry &ge : st->goods) {
4340 if (ge.status.Any()) {
4341 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4342 }
4343 }
4344 }
4345 });
4346}
4347
4348static uint UpdateStationWaiting(Station *st, CargoType cargo, uint amount, Source source)
4349{
4350 /* We can't allocate a CargoPacket? Then don't do anything
4351 * at all; i.e. just discard the incoming cargo. */
4352 if (!CargoPacket::CanAllocateItem()) return 0;
4353
4354 GoodsEntry &ge = st->goods[cargo];
4355 amount += ge.amount_fract;
4356 ge.amount_fract = GB(amount, 0, 8);
4357
4358 amount >>= 8;
4359 /* No new "real" cargo item yet. */
4360 if (amount == 0) return 0;
4361
4362 StationID next = ge.GetVia(st->index);
4363 ge.GetOrCreateData().cargo.Append(new CargoPacket(st->index, amount, source), next);
4364 LinkGraph *lg = nullptr;
4365 if (ge.link_graph == LinkGraphID::Invalid()) {
4367 lg = new LinkGraph(cargo);
4369 ge.link_graph = lg->index;
4370 ge.node = lg->AddNode(st);
4371 } else {
4372 Debug(misc, 0, "Can't allocate link graph");
4373 }
4374 } else {
4375 lg = LinkGraph::Get(ge.link_graph);
4376 }
4377 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4378
4379 if (!ge.HasRating()) {
4382 }
4383
4385 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4386 TriggerAirportAnimation(st, AirportAnimationTrigger::NewCargo, cargo);
4387 TriggerRoadStopRandomisation(st, st->xy, StationRandomTrigger::NewCargo, cargo);
4388 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4389
4390
4392 st->MarkTilesDirty(true);
4393 return amount;
4394}
4395
4396static bool IsUniqueStationName(const std::string &name)
4397{
4398 for (const Station *st : Station::Iterate()) {
4399 if (!st->name.empty() && st->name == name) return false;
4400 }
4401
4402 return true;
4403}
4404
4412CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
4413{
4414 Station *st = Station::GetIfValid(station_id);
4415 if (st == nullptr) return CMD_ERROR;
4416
4417 CommandCost ret = CheckOwnership(st->owner);
4418 if (ret.Failed()) return ret;
4419
4420 bool reset = text.empty();
4421
4422 if (!reset) {
4424 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4425 }
4426
4427 if (flags.Test(DoCommandFlag::Execute)) {
4428 st->cached_name.clear();
4429 if (reset) {
4430 st->name.clear();
4431 } else {
4432 st->name = text;
4433 }
4434
4435 st->UpdateVirtCoord();
4437 }
4438
4439 return CommandCost();
4440}
4441
4442static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4443{
4444 for (Station *st : nearby) {
4445 if (st->TileIsInCatchment(tile)) stations.insert(st);
4446 }
4447}
4448
4454{
4455 if (this->tile != INVALID_TILE) {
4456 if (IsTileType(this->tile, MP_HOUSE)) {
4457 /* Town nearby stations need to be filtered per tile. */
4458 assert(this->w == 1 && this->h == 1);
4459 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4460 } else {
4461 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4462 this->stations.insert(st);
4463 return true;
4464 });
4465 }
4466 this->tile = INVALID_TILE;
4467 }
4468 return this->stations;
4469}
4470
4471
4472static bool CanMoveGoodsToStation(const Station *st, CargoType cargo)
4473{
4474 /* Is the station reserved exclusively for somebody else? */
4475 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4476
4477 /* Lowest possible rating, better not to give cargo anymore. */
4478 if (st->goods[cargo].rating == 0) return false;
4479
4480 /* Selectively servicing stations, and not this one. */
4481 if (_settings_game.order.selectgoods && !st->goods[cargo].HasVehicleEverTriedLoading()) return false;
4482
4484 /* Passengers are never served by just a truck stop. */
4485 if (st->facilities == StationFacility::TruckStop) return false;
4486 } else {
4487 /* Non-passengers are never served by just a bus stop. */
4488 if (st->facilities == StationFacility::BusStop) return false;
4489 }
4490 return true;
4491}
4492
4493uint MoveGoodsToStation(CargoType cargo, uint amount, Source source, const StationList &all_stations, Owner exclusivity)
4494{
4495 /* Return if nothing to do. Also the rounding below fails for 0. */
4496 if (all_stations.empty()) return 0;
4497 if (amount == 0) return 0;
4498
4499 Station *first_station = nullptr;
4500 typedef std::pair<Station *, uint> StationInfo;
4501 std::vector<StationInfo> used_stations;
4502
4503 for (Station *st : all_stations) {
4504 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4505 if (!CanMoveGoodsToStation(st, cargo)) continue;
4506
4507 /* Avoid allocating a vector if there is only one station to significantly
4508 * improve performance in this common case. */
4509 if (first_station == nullptr) {
4510 first_station = st;
4511 continue;
4512 }
4513 if (used_stations.empty()) {
4514 used_stations.reserve(2);
4515 used_stations.emplace_back(first_station, 0);
4516 }
4517 used_stations.emplace_back(st, 0);
4518 }
4519
4520 /* no stations around at all? */
4521 if (first_station == nullptr) return 0;
4522
4523 if (used_stations.empty()) {
4524 /* only one station around */
4525 amount *= first_station->goods[cargo].rating + 1;
4526 return UpdateStationWaiting(first_station, cargo, amount, source);
4527 }
4528
4529 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_best = {}; // best rating for each company, including OWNER_NONE
4530 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_sum = {}; // sum of ratings for each company
4531 uint best_rating = 0;
4532 uint best_sum = 0; // sum of best ratings for each company
4533
4534 for (auto &p : used_stations) {
4535 auto owner = p.first->owner;
4536 auto rating = p.first->goods[cargo].rating;
4537 if (rating > company_best[owner]) {
4538 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4539 company_best[owner] = rating;
4540 if (rating > best_rating) best_rating = rating;
4541 }
4542 company_sum[owner] += rating;
4543 }
4544
4545 /* From now we'll calculate with fractional cargo amounts.
4546 * First determine how much cargo we really have. */
4547 amount *= best_rating + 1;
4548
4549 uint moving = 0;
4550 for (auto &p : used_stations) {
4551 Owner owner = p.first->owner;
4552 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4553 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4554 p.second = amount * company_best[owner] * p.first->goods[cargo].rating / best_sum / company_sum[owner];
4555 moving += p.second;
4556 }
4557
4558 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4559 if (amount > moving) {
4560 std::stable_sort(used_stations.begin(), used_stations.end(), [cargo](const StationInfo &a, const StationInfo &b) {
4561 return b.first->goods[cargo].rating < a.first->goods[cargo].rating;
4562 });
4563
4564 assert(amount - moving <= used_stations.size());
4565 for (uint i = 0; i < amount - moving; i++) {
4566 used_stations[i].second++;
4567 }
4568 }
4569
4570 uint moved = 0;
4571 for (auto &p : used_stations) {
4572 moved += UpdateStationWaiting(p.first, cargo, p.second, source);
4573 }
4574
4575 return moved;
4576}
4577
4578void UpdateStationDockingTiles(Station *st)
4579{
4580 st->docking_station.Clear();
4581
4582 /* For neutral stations, start with the industry area instead of dock area */
4583 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4584
4585 if (area->tile == INVALID_TILE) return;
4586
4587 int x = TileX(area->tile);
4588 int y = TileY(area->tile);
4589
4590 /* Expand the area by a tile on each side while
4591 * making sure that we remain inside the map. */
4592 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4593 int x1 = std::max<int>(x - 1, 0);
4594
4595 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4596 int y1 = std::max<int>(y - 1, 0);
4597
4598 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4599 for (TileIndex tile : ta) {
4600 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4601 }
4602}
4603
4604void BuildOilRig(TileIndex tile)
4605{
4606 if (!Station::CanAllocateItem()) {
4607 Debug(misc, 0, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4608 return;
4609 }
4610
4611 Station *st = new Station(tile);
4612 _station_kdtree.Insert(st->index);
4613 st->town = ClosestTownFromTile(tile, UINT_MAX);
4614
4615 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
4616
4617 assert(IsTileType(tile, MP_INDUSTRY));
4618 /* Mark industry as associated both ways */
4619 st->industry = Industry::GetByTile(tile);
4620 st->industry->neutral_station = st;
4621 DeleteAnimatedTile(tile);
4622 MakeOilrig(tile, st->index, GetWaterClass(tile));
4623
4624 st->owner = OWNER_NONE;
4625 st->airport.type = AT_OILRIG;
4626 st->airport.rotation = DIR_N;
4627 st->airport.Add(tile);
4628 st->ship_station.Add(tile);
4631 UpdateStationDockingTiles(st);
4632
4633 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
4634
4635 st->UpdateVirtCoord();
4636
4637 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4638 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4639 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4641 StationList nearby = std::move(st->industry->stations_near);
4642 st->industry->stations_near.clear();
4643 for (Station *near : nearby) {
4644 near->RecomputeCatchment(true);
4645 UpdateStationAcceptance(near, true);
4646 }
4647 }
4648
4649 st->RecomputeCatchment();
4650 UpdateStationAcceptance(st, false);
4651}
4652
4653void DeleteOilRig(TileIndex tile)
4654{
4655 Station *st = Station::GetByTile(tile);
4656
4657 MakeWaterKeepingClass(tile, OWNER_NONE);
4658
4659 /* The oil rig station is not supposed to be shared with anything else */
4660 [[maybe_unused]] static constexpr StationFacilities expected_facility{StationFacility::Airport, StationFacility::Dock};
4661 assert(st->facilities == expected_facility && st->airport.type == AT_OILRIG);
4662 if (st->industry != nullptr && st->industry->neutral_station == st) {
4663 /* Don't leave dangling neutral station pointer */
4664 st->industry->neutral_station = nullptr;
4665 }
4666 delete st;
4667}
4668
4669static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4670{
4671
4672 if (IsAnyRoadStopTile(tile)) {
4673 for (RoadTramType rtt : _roadtramtypes) {
4674 /* Update all roadtypes, no matter if they are present */
4675 if (GetRoadOwner(tile, rtt) == old_owner) {
4676 RoadType rt = GetRoadType(tile, rtt);
4677 if (rt != INVALID_ROADTYPE) {
4678 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4679 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4680 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4681 }
4682 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4683 }
4684 }
4685 }
4686
4687 if (!IsTileOwner(tile, old_owner)) return;
4688
4689 if (new_owner != INVALID_OWNER) {
4690 /* Update company infrastructure counts. Only do it here
4691 * if the new owner is valid as otherwise the clear
4692 * command will do it for us. No need to dirty windows
4693 * here, we'll redraw the whole screen anyway.*/
4694 Company *old_company = Company::Get(old_owner);
4695 Company *new_company = Company::Get(new_owner);
4696
4697 /* Update counts for underlying infrastructure. */
4698 switch (GetStationType(tile)) {
4699 case StationType::Rail:
4700 case StationType::RailWaypoint:
4701 if (!IsStationTileBlocked(tile)) {
4702 old_company->infrastructure.rail[GetRailType(tile)]--;
4703 new_company->infrastructure.rail[GetRailType(tile)]++;
4704 }
4705 break;
4706
4707 case StationType::Bus:
4708 case StationType::Truck:
4709 case StationType::RoadWaypoint:
4710 /* Road stops were already handled above. */
4711 break;
4712
4713 case StationType::Buoy:
4714 case StationType::Dock:
4715 if (GetWaterClass(tile) == WATER_CLASS_CANAL) {
4716 old_company->infrastructure.water--;
4717 new_company->infrastructure.water++;
4718 }
4719 break;
4720
4721 default:
4722 break;
4723 }
4724
4725 /* Update station tile count. */
4726 if (!IsBuoy(tile) && !IsAirport(tile)) {
4727 old_company->infrastructure.station--;
4728 new_company->infrastructure.station++;
4729 }
4730
4731 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4732 SetTileOwner(tile, new_owner);
4734 } else {
4735 if (IsDriveThroughStopTile(tile)) {
4736 /* Remove the drive-through road stop */
4737 if (IsRoadWaypoint(tile)) {
4739 } else {
4741 }
4742 assert(IsTileType(tile, MP_ROAD));
4743 /* Change owner of tile and all roadtypes */
4744 ChangeTileOwner(tile, old_owner, new_owner);
4745 } else {
4747 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4748 * Update owner of buoy if it was not removed (was in orders).
4749 * Do not update when owned by OWNER_WATER (sea and rivers). */
4750 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4751 }
4752 }
4753}
4754
4764{
4765 /* Water flooding can always clear road stops. */
4766 if (_current_company == OWNER_WATER) return CommandCost();
4767
4768 CommandCost ret;
4769
4770 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4771 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
4772 if (tram_owner != OWNER_NONE) {
4773 ret = CheckOwnership(tram_owner);
4774 if (ret.Failed()) return ret;
4775 }
4776 }
4777
4778 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4779 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
4780 if (road_owner == OWNER_TOWN) {
4781 ret = CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags);
4782 if (ret.Failed()) return ret;
4783 } else if (road_owner != OWNER_NONE) {
4784 ret = CheckOwnership(road_owner);
4785 if (ret.Failed()) return ret;
4786 }
4787 }
4788
4789 return CommandCost();
4790}
4791
4799{
4800 if (flags.Test(DoCommandFlag::Auto)) {
4801 switch (GetStationType(tile)) {
4802 default: break;
4803 case StationType::Rail: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4804 case StationType::RailWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4805 case StationType::Airport: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4806 case StationType::Truck: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4807 case StationType::Bus: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4808 case StationType::RoadWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4809 case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4810 case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4811 case StationType::Oilrig:
4812 return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG);
4813 }
4814 }
4815
4816 switch (GetStationType(tile)) {
4817 case StationType::Rail: return RemoveRailStation(tile, flags);
4818 case StationType::RailWaypoint: return RemoveRailWaypoint(tile, flags);
4819 case StationType::Airport: return RemoveAirport(tile, flags);
4820 case StationType::Truck: [[fallthrough]];
4821 case StationType::Bus:
4822 if (IsDriveThroughStopTile(tile)) {
4823 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4824 if (remove_road.Failed()) return remove_road;
4825 }
4826 return RemoveRoadStop(tile, flags);
4827 case StationType::RoadWaypoint: {
4828 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4829 if (remove_road.Failed()) return remove_road;
4830 return RemoveRoadWaypointStop(tile, flags);
4831 }
4832 case StationType::Buoy: return RemoveBuoy(tile, flags);
4833 case StationType::Dock: return RemoveDock(tile, flags);
4834 default: break;
4835 }
4836
4837 return CMD_ERROR;
4838}
4839
4840static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
4841{
4843 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4844 * TTDP does not call it.
4845 */
4846 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4847 switch (GetStationType(tile)) {
4848 case StationType::RailWaypoint:
4849 case StationType::Rail: {
4850 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4851 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4852 }
4853
4854 case StationType::Airport:
4855 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4856
4857 case StationType::Truck:
4858 case StationType::Bus:
4859 case StationType::RoadWaypoint: {
4860 if (IsDriveThroughStopTile(tile)) {
4861 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4862 } else {
4863 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4864 }
4865 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4866 }
4867
4868 default: break;
4869 }
4870 }
4871 }
4872 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
4873}
4874
4881{
4882 uint32_t prev = 0;
4883 for (const auto &it : this->shares) {
4884 if (it.second == st) {
4885 return it.first - prev;
4886 } else {
4887 prev = it.first;
4888 }
4889 }
4890 return 0;
4891}
4892
4900{
4901 if (this->unrestricted == 0) return StationID::Invalid();
4902 assert(!this->shares.empty());
4903 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
4904 assert(it != this->shares.end() && it->first <= this->unrestricted);
4905 if (it->second != excluded && it->second != excluded2) return it->second;
4906
4907 /* We've hit one of the excluded stations.
4908 * Draw another share, from outside its range. */
4909
4910 uint end = it->first;
4911 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
4912 uint interval = end - begin;
4913 if (interval >= this->unrestricted) return StationID::Invalid(); // Only one station in the map.
4914 uint new_max = this->unrestricted - interval;
4915 uint rand = RandomRange(new_max);
4916 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
4917 this->shares.upper_bound(rand + interval);
4918 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
4919 if (it2->second != excluded && it2->second != excluded2) return it2->second;
4920
4921 /* We've hit the second excluded station.
4922 * Same as before, only a bit more complicated. */
4923
4924 uint end2 = it2->first;
4925 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
4926 uint interval2 = end2 - begin2;
4927 if (interval2 >= new_max) return StationID::Invalid(); // Only the two excluded stations in the map.
4928 new_max -= interval2;
4929 if (begin > begin2) {
4930 std::swap(begin, begin2);
4931 std::swap(end, end2);
4932 std::swap(interval, interval2);
4933 }
4934 rand = RandomRange(new_max);
4935 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
4936 if (rand < begin) {
4937 it3 = this->shares.upper_bound(rand);
4938 } else if (rand < begin2 - interval) {
4939 it3 = this->shares.upper_bound(rand + interval);
4940 } else {
4941 it3 = this->shares.upper_bound(rand + interval + interval2);
4942 }
4943 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
4944 return it3->second;
4945}
4946
4953{
4954 assert(!this->shares.empty());
4955 SharesMap new_shares;
4956 uint i = 0;
4957 for (const auto &it : this->shares) {
4958 new_shares[++i] = it.second;
4959 if (it.first == this->unrestricted) this->unrestricted = i;
4960 }
4961 this->shares.swap(new_shares);
4962 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
4963}
4964
4972{
4973 /* We assert only before changing as afterwards the shares can actually
4974 * be empty. In that case the whole flow stat must be deleted then. */
4975 assert(!this->shares.empty());
4976
4977 uint removed_shares = 0;
4978 uint added_shares = 0;
4979 uint last_share = 0;
4980 SharesMap new_shares;
4981 for (const auto &it : this->shares) {
4982 if (it.second == st) {
4983 if (flow < 0) {
4984 uint share = it.first - last_share;
4985 if (flow == INT_MIN || (uint)(-flow) >= share) {
4986 removed_shares += share;
4987 if (it.first <= this->unrestricted) this->unrestricted -= share;
4988 if (flow != INT_MIN) flow += share;
4989 last_share = it.first;
4990 continue; // remove the whole share
4991 }
4992 removed_shares += (uint)(-flow);
4993 } else {
4994 added_shares += (uint)(flow);
4995 }
4996 if (it.first <= this->unrestricted) this->unrestricted += flow;
4997
4998 /* If we don't continue above the whole flow has been added or
4999 * removed. */
5000 flow = 0;
5001 }
5002 new_shares[it.first + added_shares - removed_shares] = it.second;
5003 last_share = it.first;
5004 }
5005 if (flow > 0) {
5006 new_shares[last_share + (uint)flow] = st;
5007 if (this->unrestricted < last_share) {
5008 this->ReleaseShare(st);
5009 } else {
5010 this->unrestricted += flow;
5011 }
5012 }
5013 this->shares.swap(new_shares);
5014}
5015
5022{
5023 assert(!this->shares.empty());
5024 uint flow = 0;
5025 uint last_share = 0;
5026 SharesMap new_shares;
5027 for (auto &it : this->shares) {
5028 if (flow == 0) {
5029 if (it.first > this->unrestricted) return; // Not present or already restricted.
5030 if (it.second == st) {
5031 flow = it.first - last_share;
5032 this->unrestricted -= flow;
5033 } else {
5034 new_shares[it.first] = it.second;
5035 }
5036 } else {
5037 new_shares[it.first - flow] = it.second;
5038 }
5039 last_share = it.first;
5040 }
5041 if (flow == 0) return;
5042 new_shares[last_share + flow] = st;
5043 this->shares.swap(new_shares);
5044 assert(!this->shares.empty());
5045}
5046
5053{
5054 assert(!this->shares.empty());
5055 uint flow = 0;
5056 uint next_share = 0;
5057 bool found = false;
5058 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
5059 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
5060 if (found) {
5061 flow = next_share - it->first;
5062 this->unrestricted += flow;
5063 break;
5064 } else {
5065 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
5066 if (it->second == st) found = true;
5067 }
5068 next_share = it->first;
5069 }
5070 if (flow == 0) return;
5071 SharesMap new_shares;
5072 new_shares[flow] = st;
5073 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
5074 if (it->second != st) {
5075 new_shares[flow + it->first] = it->second;
5076 } else {
5077 flow = 0;
5078 }
5079 }
5080 this->shares.swap(new_shares);
5081 assert(!this->shares.empty());
5082}
5083
5090{
5091 assert(runtime > 0);
5092 SharesMap new_shares;
5093 uint share = 0;
5094 for (auto i : this->shares) {
5095 share = std::max(share + 1, i.first * 30 / runtime);
5096 new_shares[share] = i.second;
5097 if (this->unrestricted == i.first) this->unrestricted = share;
5098 }
5099 this->shares.swap(new_shares);
5100}
5101
5108void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5109{
5110 FlowStatMap::iterator origin_it = this->find(origin);
5111 if (origin_it == this->end()) {
5112 this->emplace(origin, FlowStat(via, flow));
5113 } else {
5114 origin_it->second.ChangeShare(via, flow);
5115 assert(!origin_it->second.GetShares()->empty());
5116 }
5117}
5118
5127void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5128{
5129 FlowStatMap::iterator prev_it = this->find(origin);
5130 if (prev_it == this->end()) {
5131 FlowStat fs(via, flow);
5132 fs.AppendShare(StationID::Invalid(), flow);
5133 this->emplace(origin, fs);
5134 } else {
5135 prev_it->second.ChangeShare(via, flow);
5136 prev_it->second.ChangeShare(StationID::Invalid(), flow);
5137 assert(!prev_it->second.GetShares()->empty());
5138 }
5139}
5140
5146{
5147 for (auto &i : *this) {
5148 FlowStat &fs = i.second;
5149 uint local = fs.GetShare(StationID::Invalid());
5150 if (local > INT_MAX) { // make sure it fits in an int
5151 fs.ChangeShare(self, -INT_MAX);
5152 fs.ChangeShare(StationID::Invalid(), -INT_MAX);
5153 local -= INT_MAX;
5154 }
5155 fs.ChangeShare(self, -(int)local);
5156 fs.ChangeShare(StationID::Invalid(), -(int)local);
5157
5158 /* If the local share is used up there must be a share for some
5159 * remote station. */
5160 assert(!fs.GetShares()->empty());
5161 }
5162}
5163
5171{
5172 StationIDStack ret;
5173 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5174 FlowStat &s_flows = f_it->second;
5175 s_flows.ChangeShare(via, INT_MIN);
5176 if (s_flows.GetShares()->empty()) {
5177 ret.Push(f_it->first);
5178 this->erase(f_it++);
5179 } else {
5180 ++f_it;
5181 }
5182 }
5183 return ret;
5184}
5185
5191{
5192 for (auto &it : *this) {
5193 it.second.RestrictShare(via);
5194 }
5195}
5196
5202{
5203 for (auto &it : *this) {
5204 it.second.ReleaseShare(via);
5205 }
5206}
5207
5213{
5214 uint ret = 0;
5215 for (const auto &it : *this) {
5216 ret += (--(it.second.GetShares()->end()))->first;
5217 }
5218 return ret;
5219}
5220
5227{
5228 uint ret = 0;
5229 for (const auto &it : *this) {
5230 ret += it.second.GetShare(via);
5231 }
5232 return ret;
5233}
5234
5241{
5242 FlowStatMap::const_iterator i = this->find(from);
5243 if (i == this->end()) return 0;
5244 return (--(i->second.GetShares()->end()))->first;
5245}
5246
5254{
5255 FlowStatMap::const_iterator i = this->find(from);
5256 if (i == this->end()) return 0;
5257 return i->second.GetShare(via);
5258}
5259
5260static CommandCost CheckBuildAbove_Station(TileIndex tile, DoCommandFlags, Axis, int height)
5261{
5262 StationType type = GetStationType(tile);
5263 auto bridgeable_info = GetStationBridgeableTileInfo(type);
5264
5265 switch (type) {
5266 case StationType::Rail:
5267 case StationType::RailWaypoint:
5268 if (const StationSpec *spec = GetStationSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5269 break;
5270
5271 case StationType::Bus:
5272 case StationType::Truck:
5273 case StationType::RoadWaypoint:
5274 if (const RoadStopSpec *spec = GetRoadStopSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5275 break;
5276
5277 default: break;
5278 }
5279
5280 return IsStationBridgeAboveOk(tile, bridgeable_info, type, GetStationGfx(tile), height);
5281}
5282
5283extern const TileTypeProcs _tile_type_station_procs = {
5284 DrawTile_Station, // draw_tile_proc
5285 GetSlopePixelZ_Station, // get_slope_z_proc
5286 ClearTile_Station, // clear_tile_proc
5287 nullptr, // add_accepted_cargo_proc
5288 GetTileDesc_Station, // get_tile_desc_proc
5289 GetTileTrackStatus_Station, // get_tile_track_status_proc
5290 ClickTile_Station, // click_tile_proc
5291 AnimateTile_Station, // animate_tile_proc
5292 TileLoop_Station, // tile_loop_proc
5293 ChangeTileOwner_Station, // change_tile_owner_proc
5294 nullptr, // add_produced_cargo_proc
5295 VehicleEnter_Station, // vehicle_enter_tile_proc
5296 GetFoundation_Station, // get_foundation_proc
5297 TerraformTile_Station, // terraform_tile_proc
5298 CheckBuildAbove_Station, // check_build_above_proc
5299};
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:118
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:128
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:222
struct RailTypeInfo::@20 base_sprites
Struct containing the main sprites.
StringID name
Name of this rail type.
Definition rail.h:167
uint8_t fallback_railtype
Original railtype number to use when drawing non-newgrf railtypes, or when drawing stations.
Definition rail.h:192
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:288
struct RailTypeInfo::@23 strings
Strings associated with the rail type.
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:133
StringID name
Name of this rail type.
Definition road.h:94
struct RoadTypeInfo::@26 strings
Strings associated with the rail type.
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:65
uint ApplyPixelFoundationToSlope(Foundation f, Slope &s)
Applies a foundation to a slope.
Definition landscape.h:126
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:95
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.
std::optional< uint8_t > AllocateSpecToStation(const StationSpec *statspec, BaseStation *st, bool exec)
Allocate a StationSpec to a Station.
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 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.
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:78
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:381
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:341
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:300
@ RTSG_GROUND
Main group of ground images.
Definition rail.h:43
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:42
RailTrackOffset
Offsets for sprites within an overlay/underlay set.
Definition rail.h:61
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:63
@ RTO_X
Piece of rail in X direction.
Definition rail.h:62
Command definitions for rail.
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
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:152
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:258
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:52
@ ROTSG_ROADSTOP
Required: Bay stop surface.
Definition road.h:61
@ ROTSG_GROUND
Required: Main group of ground images.
Definition road.h:53
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:268
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:186
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:258
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:634
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:1611
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1551
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1541
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
Definition sprites.h:1554
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 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.
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.
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
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:53
CompanyMask statues
which companies have a statue?
Definition town.h:69
TileIndex xy
town center tile
Definition town.h:54
uint16_t noise_reached
level of noise that all the airports are generating
Definition town.h:67
uint16_t MaxTownNoise() const
Calculate the max town noise.
Definition town.h:164
CompanyID exclusivity
which company has exclusivity
Definition town.h:74
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:75
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:350
bool IsTileOnWater(Tile t)
Tests if the tile was built on water.
Definition water_map.h:136
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:101
WaterClass GetWaterClass(Tile t)
Get the water class at a tile.
Definition water_map.h:112
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:371
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:361
bool IsWater(Tile t)
Is it a plain water tile?
Definition water_map.h:147
bool IsWaterTile(Tile t)
Is it a water tile with plain water?
Definition water_map.h:190
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:1190
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3181
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:3273
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:3168
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3155
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:3290
@ 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.