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