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