OpenTTD Source 20251126-master-g67ded4f980
station_cmd.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "core/flatset_type.hpp"
12#include "aircraft.h"
13#include "bridge_map.h"
14#include "vehiclelist_func.h"
15#include "viewport_func.h"
16#include "viewport_kdtree.h"
17#include "command_func.h"
18#include "town.h"
19#include "news_func.h"
20#include "train.h"
21#include "ship.h"
22#include "roadveh.h"
23#include "industry.h"
24#include "newgrf_cargo.h"
25#include "newgrf_debug.h"
26#include "newgrf_station.h"
27#include "newgrf_canal.h" /* For the buoy */
29#include "road_internal.h" /* For drawing catenary/checking road removal */
30#include "autoslope.h"
31#include "water.h"
32#include "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->HasData() || it->GetData().cargo.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 = new Station(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
979static 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)
980{
982 DiagDirections invalid_dirs = AxisToDiagDirs(axis);
983
984 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
985 bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
986
987 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, false);
988 if (ret.Failed()) return ret;
989 cost.AddCost(ret.GetCost());
990
991 if (slope_cb) {
992 /* Do slope check if requested. */
993 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
994 if (ret.Failed()) return ret;
995 }
996
997 /* if station is set, then we have special handling to allow building on top of already existing stations.
998 * so station points to StationID::Invalid() if we can build on any station.
999 * Or it points to a station if we're only allowed to build on exactly that station. */
1000 if (station != nullptr && IsTileType(tile_cur, MP_STATION)) {
1001 if (!IsRailStation(tile_cur)) {
1002 return ClearTile_Station(tile_cur, DoCommandFlag::Auto); // get error message
1003 } else {
1004 StationID st = GetStationIndex(tile_cur);
1005 if (*station == StationID::Invalid()) {
1006 *station = st;
1007 } else if (*station != st) {
1008 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1009 }
1010 }
1011 } else {
1012 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
1013 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
1014 /* Don't overbuild signals. */
1015 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
1016
1017 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
1018 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
1019 TrackBits tracks = GetTrackBits(tile_cur);
1020 Track track = RemoveFirstTrack(&tracks);
1021 Track expected_track = invalid_dirs.Test(DIAGDIR_NE) ? TRACK_X : TRACK_Y;
1022
1023 /* The existing track must align with the desired station axis. */
1024 if (tracks == TRACK_BIT_NONE && track == expected_track) {
1025 /* Check for trains having a reservation for this tile. */
1026 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
1027 Train *v = GetTrainForReservation(tile_cur, track);
1028 if (v != nullptr) {
1029 affected_vehicles.push_back(v);
1030 }
1031 }
1032 ret = Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile_cur, track);
1033 if (ret.Failed()) return ret;
1034 cost.AddCost(ret.GetCost());
1035 /* With DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) CmdLandscapeClear would fail since the rail still exists */
1036 return cost;
1037 }
1038 }
1039 }
1040 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
1041 if (ret.Failed()) return ret;
1042 cost.AddCost(ret.GetCost());
1043 }
1044
1045 return cost;
1046}
1047
1062static 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)
1063{
1065
1066 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, false);
1067 if (ret.Failed()) return ret;
1068 cost.AddCost(ret.GetCost());
1069
1070 ret = IsRoadStationBridgeAboveOk(cur_tile, spec, station_type, is_drive_through ? GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + axis : FindFirstBit(invalid_dirs.base()));
1071 if (ret.Failed()) return ret;
1072
1073 /* If station is set, then we have special handling to allow building on top of already existing stations.
1074 * Station points to StationID::Invalid() if we can build on any station.
1075 * Or it points to a station if we're only allowed to build on exactly that station. */
1076 if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
1077 if (!IsAnyRoadStop(cur_tile)) {
1078 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1079 } else {
1080 if (station_type != GetStationType(cur_tile) ||
1081 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
1082 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
1083 }
1084 /* Drive-through station in the wrong direction. */
1085 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
1086 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1087 }
1088 StationID st = GetStationIndex(cur_tile);
1089 if (*station == StationID::Invalid()) {
1090 *station = st;
1091 } else if (*station != st) {
1092 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
1093 }
1094 }
1095 } else {
1096 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
1097 /* Road bits in the wrong direction. */
1098 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
1099 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
1100 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1101 switch (CountBits(rb)) {
1102 case 1:
1103 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1104
1105 case 2:
1106 if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1107 return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
1108
1109 default: // 3 or 4
1110 return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
1111 }
1112 }
1113
1114 if (build_over_road) {
1115 /* There is a road, check if we can build road+tram stop over it. */
1116 RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
1117 if (road_rt != INVALID_ROADTYPE) {
1118 Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
1119 if (road_owner == OWNER_TOWN) {
1120 if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
1122 ret = CheckOwnership(road_owner);
1123 if (ret.Failed()) return ret;
1124 }
1125 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
1126
1127 if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1128
1129 if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
1130 ret = CheckOwnership(road_owner);
1131 if (ret.Failed()) return ret;
1132 }
1133
1134 cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
1135 } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
1136 cost.AddCost(RoadBuildCost(rt) * 2);
1137 }
1138
1139 /* There is a tram, check if we can build road+tram stop over it. */
1140 RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
1141 if (tram_rt != INVALID_ROADTYPE) {
1142 Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
1143 if (Company::IsValidID(tram_owner) &&
1145 /* Disallow breaking end-of-line of someone else
1146 * so trams can still reverse on this tile. */
1147 HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
1148 ret = CheckOwnership(tram_owner);
1149 if (ret.Failed()) return ret;
1150 }
1151 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
1152
1153 if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1154
1155 cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
1156 } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
1157 cost.AddCost(RoadBuildCost(rt) * 2);
1158 }
1159 } else if (rt == INVALID_ROADTYPE) {
1160 return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
1161 } else {
1162 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
1163 if (ret.Failed()) return ret;
1164 cost.AddCost(ret.GetCost());
1165 cost.AddCost(RoadBuildCost(rt) * 2);
1166 }
1167 }
1168
1169 return cost;
1170}
1171
1179{
1180 TileArea cur_ta = st->train_station;
1181
1182 /* determine new size of train station region.. */
1183 int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
1184 int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
1185 new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
1186 new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
1187 new_ta.tile = TileXY(x, y);
1188
1189 /* make sure the final size is not too big. */
1191 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1192 }
1193
1194 return CommandCost();
1195}
1196
1197RailStationTileLayout::RailStationTileLayout(const StationSpec *spec, uint8_t platforms, uint8_t length) : platforms(platforms), length(length)
1198{
1199 if (spec == nullptr) return;
1200
1201 /* Look for a predefined layout for the required size. */
1202 auto found = spec->layouts.find(GetStationLayoutKey(platforms, length));
1203 if (found != std::end(spec->layouts)) this->layout = found->second;
1204}
1205
1206StationGfx RailStationTileLayout::Iterator::operator*() const
1207{
1208 /* Use predefined layout if it exists. Mask bit zero which will indicate axis. */
1209 if (!stl.layout.empty()) return this->stl.layout[this->position] & ~1;
1210
1211 if (this->stl.length == 1) {
1212 /* Special case for 1-long platforms, all bare platforms except one small building. */
1213 return this->position == ((this->stl.platforms - 1) / 2) ? 2 : 0;
1214 }
1215
1216 if ((this->position < this->stl.length && (this->stl.platforms % 2 == 1))) {
1217 /* Number of tracks is odd, make the first platform bare with a small building. */
1218 return this->position == ((this->stl.length - 1) / 2) ? 2 : 0;
1219 }
1220
1221 if (this->stl.length > 4 && ((this->position % this->stl.length) == 0 || (this->position % this->stl.length) == this->stl.length - 1)) {
1222 /* Station is longer than 4 tiles, place bare platforms at either end. */
1223 return 0;
1224 }
1225
1226 /* None of the above so must be north or south part of larger station. */
1227 return (((this->position / this->stl.length) % 2) == (this->stl.platforms % 2)) ? 4 : 6;
1228}
1229
1242template <class T, StringID error_message, class F>
1243CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1244{
1245 assert(*st == nullptr);
1246 bool check_surrounding = true;
1247
1248 if (existing_station != StationID::Invalid()) {
1249 if (adjacent && existing_station != station_to_join) {
1250 /* You can't build an adjacent station over the top of one that
1251 * already exists. */
1252 return CommandCost(error_message);
1253 } else {
1254 /* Extend the current station, and don't check whether it will
1255 * be near any other stations. */
1256 T *candidate = T::GetIfValid(existing_station);
1257 if (candidate != nullptr && filter(candidate)) *st = candidate;
1258 check_surrounding = (*st == nullptr);
1259 }
1260 } else {
1261 /* There's no station here. Don't check the tiles surrounding this
1262 * one if the company wanted to build an adjacent station. */
1263 if (adjacent) check_surrounding = false;
1264 }
1265
1266 if (check_surrounding) {
1267 /* Make sure there is no more than one other station around us that is owned by us. */
1268 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1269 if (ret.Failed()) return ret;
1270 }
1271
1272 /* Distant join */
1273 if (*st == nullptr && station_to_join != StationID::Invalid()) *st = T::GetIfValid(station_to_join);
1274
1275 return CommandCost();
1276}
1277
1287static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1288{
1289 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1290}
1291
1302CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1303{
1304 if (is_road) {
1305 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); });
1306 } else {
1307 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); });
1308 }
1309}
1310
1322
1334
1349static 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)
1350{
1352 bool length_price_ready = true;
1353 uint8_t tracknum = 0;
1354 int allowed_z = -1;
1355 for (TileIndex cur_tile : tile_area) {
1356 /* Clear the land below the station. */
1357 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1358 if (ret.Failed()) return ret;
1359
1360 /* Only add _price[PR_BUILD_STATION_RAIL_LENGTH] once for each valid plat_len. */
1361 if (tracknum == numtracks) {
1362 length_price_ready = true;
1363 tracknum = 0;
1364 } else {
1365 tracknum++;
1366 }
1367
1368 /* AddCost for new or rotated rail stations. */
1369 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1370 cost.AddCost(ret.GetCost());
1371 cost.AddCost(_price[PR_BUILD_STATION_RAIL]);
1372 cost.AddCost(RailBuildCost(rt));
1373
1374 if (length_price_ready) {
1375 cost.AddCost(_price[PR_BUILD_STATION_RAIL_LENGTH]);
1376 length_price_ready = false;
1377 }
1378 }
1379 }
1380
1381 return cost;
1382}
1383
1391{
1392 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1393 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlag::Pylons : StationSpec::TileFlags{};
1394 return statspec->tileflags[gfx];
1395}
1396
1403{
1404 const auto flags = GetStationTileFlags(GetStationGfx(tile), statspec);
1408}
1409
1424CommandCost 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)
1425{
1426 /* Does the authority allow this? */
1427 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1428 if (ret.Failed()) return ret;
1429
1430 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1431
1432 /* Check if the given station class is valid */
1433 if (static_cast<uint>(spec_class) >= StationClass::GetClassCount()) return CMD_ERROR;
1434 const StationClass *cls = StationClass::Get(spec_class);
1435 if (IsWaypointClass(*cls)) return CMD_ERROR;
1436 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1437 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1438
1439 int w_org, h_org;
1440 if (axis == AXIS_X) {
1441 w_org = plat_len;
1442 h_org = numtracks;
1443 } else {
1444 h_org = plat_len;
1445 w_org = numtracks;
1446 }
1447
1448 /* Check if the first tile and the last tile are valid */
1449 if (!IsValidTile(tile_org) || TileAddWrap(tile_org, w_org - 1, h_org - 1) == INVALID_TILE) return CMD_ERROR;
1450
1451 bool reuse = (station_to_join != NEW_STATION);
1452 if (!reuse) station_to_join = StationID::Invalid();
1453 bool distant_join = (station_to_join != StationID::Invalid());
1454
1455 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1456
1458
1459 /* these values are those that will be stored in train_tile and station_platforms */
1460 TileArea new_location(tile_org, w_org, h_org);
1461
1462 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1463 StationID est = StationID::Invalid();
1464 std::vector<Train *> affected_vehicles;
1465 /* Add construction and clearing expenses. */
1466 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1467 if (cost.Failed()) return cost;
1468
1469 Station *st = nullptr;
1470 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1471 if (ret.Failed()) return ret;
1472
1473 ret = BuildStationPart(&st, flags, reuse, new_location, STATIONNAMING_RAIL);
1474 if (ret.Failed()) return ret;
1475
1476 if (st != nullptr && st->train_station.tile != INVALID_TILE) {
1477 ret = CanExpandRailStation(st, new_location);
1478 if (ret.Failed()) return ret;
1479 }
1480
1481 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1482 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1483 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1484
1485 RailStationTileLayout stl{statspec, numtracks, plat_len};
1486 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1487 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1488 /* Don't check the layout if there's no bridge above anyway. */
1489 if (!IsBridgeAbove(tile)) continue;
1490
1491 StationGfx gfx = *it + axis;
1492 if (statspec != nullptr) {
1493 uint32_t platinfo = GetPlatformInfo(AXIS_X, gfx, plat_len, numtracks, j, i, false);
1494 /* As the station is not yet completely finished, the station does not yet exist. */
1495 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1496 if (callback != CALLBACK_FAILED && callback <= UINT8_MAX) gfx = (callback & ~1) + axis;
1497 }
1498
1499 ret = IsRailStationBridgeAboveOk(tile, statspec, StationType::Rail, gfx);
1500 if (ret.Failed()) return ret;
1501 }
1502 }
1503
1504 /* Check if we can allocate a custom stationspec to this station */
1505 auto specindex = AllocateSpecToStation(statspec, st);
1506 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1507
1508 if (statspec != nullptr) {
1509 /* Perform NewStation checks */
1510
1511 /* Check if the station size is permitted */
1512 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1513 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1514
1515 /* Check if the station is buildable */
1517 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1519 }
1520 }
1521
1522 if (flags.Test(DoCommandFlag::Execute)) {
1523 st->train_station = new_location;
1524 st->AddFacility(StationFacility::Train, new_location.tile);
1525
1526 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
1527
1528 if (specindex.has_value()) AssignSpecToStation(statspec, st, *specindex);
1529 if (statspec != nullptr) {
1530 /* Include this station spec's animation trigger bitmask
1531 * in the station's cached copy. */
1532 st->cached_anim_triggers.Set(statspec->animation.triggers);
1533 }
1534
1535 Track track = AxisToTrack(axis);
1536 Company *c = Company::Get(st->owner);
1537 for (auto [i, it, tile_track] = std::make_tuple(0, stl.begin(), tile_org); i != numtracks; ++i, tile_track += track_delta) {
1538 for (auto [j, tile] = std::make_tuple(0, tile_track); j != plat_len; ++j, tile += tile_delta, ++it) {
1539 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1540 /* Check for trains having a reservation for this tile. */
1542 if (v != nullptr) {
1543 affected_vehicles.push_back(v);
1545 }
1546 }
1547
1548 /* Railtype can change when overbuilding. */
1549 if (IsRailStationTile(tile)) {
1550 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1552 }
1553
1554 /* Remove animation if overbuilding */
1555 DeleteAnimatedTile(tile);
1556 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1557
1558 MakeRailStation(tile, st->owner, st->index, axis, *it, rt);
1559 /* Free the spec if we overbuild something */
1560 DeallocateSpecFromStation(st, old_specindex);
1561
1562 SetCustomStationSpecIndex(tile, *specindex);
1563 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1564 SetAnimationFrame(tile, 0);
1565
1566 if (statspec != nullptr) {
1567 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1568 uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks, j, i, false);
1569
1570 /* As the station is not yet completely finished, the station does not yet exist. */
1571 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1572 if (callback != CALLBACK_FAILED) {
1573 if (callback <= UINT8_MAX) {
1574 SetStationGfx(tile, (callback & ~1) + axis);
1575 } else {
1577 }
1578 }
1579
1580 /* Trigger station animation -- after building? */
1581 TriggerStationAnimation(st, tile, StationAnimationTrigger::Built);
1582 }
1583
1584 SetRailStationTileFlags(tile, statspec);
1585
1586 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1588 }
1589 AddTrackToSignalBuffer(tile_track, track, _current_company);
1590 YapfNotifyTrackLayoutChange(tile_track, track);
1591 }
1592
1593 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1594 /* Restore reservations of trains. */
1595 RestoreTrainReservation(affected_vehicles[i]);
1596 }
1597
1598 /* Check whether we need to expand the reservation of trains already on the station. */
1599 TileArea update_reservation_area;
1600 if (axis == AXIS_X) {
1601 update_reservation_area = TileArea(tile_org, 1, numtracks);
1602 } else {
1603 update_reservation_area = TileArea(tile_org, numtracks, 1);
1604 }
1605
1606 for (TileIndex tile : update_reservation_area) {
1607 /* Don't even try to make eye candy parts reserved. */
1608 if (IsStationTileBlocked(tile)) continue;
1609
1610 DiagDirection dir = AxisToDiagDir(axis);
1611 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1612 TileIndex platform_begin = tile;
1613 TileIndex platform_end = tile;
1614
1615 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1616 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1617 platform_begin = next_tile;
1618 }
1619 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1620 platform_end = next_tile;
1621 }
1622
1623 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1624 bool reservation = false;
1625 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1626 reservation = HasStationReservation(t);
1627 }
1628
1629 if (reservation) {
1630 SetRailStationPlatformReservation(platform_begin, dir, true);
1631 }
1632 }
1633
1634 st->MarkTilesDirty(false);
1635 st->AfterStationTileSetChange(true, StationType::Rail);
1636 }
1637
1638 return cost;
1639}
1640
1641static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1642{
1643restart:
1644
1645 /* too small? */
1646 if (ta.w != 0 && ta.h != 0) {
1647 /* check the left side, x = constant, y changes */
1648 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1649 /* the left side is unused? */
1650 if (++i == ta.h) {
1651 ta.tile += TileDiffXY(1, 0);
1652 ta.w--;
1653 goto restart;
1654 }
1655 }
1656
1657 /* check the right side, x = constant, y changes */
1658 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1659 /* the right side is unused? */
1660 if (++i == ta.h) {
1661 ta.w--;
1662 goto restart;
1663 }
1664 }
1665
1666 /* check the upper side, y = constant, x changes */
1667 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1668 /* the left side is unused? */
1669 if (++i == ta.w) {
1670 ta.tile += TileDiffXY(0, 1);
1671 ta.h--;
1672 goto restart;
1673 }
1674 }
1675
1676 /* check the lower side, y = constant, x changes */
1677 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1678 /* the left side is unused? */
1679 if (++i == ta.w) {
1680 ta.h--;
1681 goto restart;
1682 }
1683 }
1684 } else {
1685 ta.Clear();
1686 }
1687
1688 return ta;
1689}
1690
1691static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1692{
1693 return st->TileBelongsToRailStation(tile);
1694}
1695
1696static void MakeRailStationAreaSmaller(BaseStation *st)
1697{
1698 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1699}
1700
1701static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1702{
1703 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1704}
1705
1706static void MakeShipStationAreaSmaller(Station *st)
1707{
1708 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1709 UpdateStationDockingTiles(st);
1710}
1711
1712static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1713{
1714 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1715}
1716
1717void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1718{
1719 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1720}
1721
1732template <class T>
1733CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
1734{
1735 /* Count of the number of tiles removed */
1736 int quantity = 0;
1738 /* Accumulator for the errors seen during clearing. If no errors happen,
1739 * and the quantity is 0 there is no station. Otherwise it will be one
1740 * of the other error that got accumulated. */
1741 CommandCost error;
1742
1743 /* Do the action for every tile into the area */
1744 for (TileIndex tile : ta) {
1745 /* Make sure the specified tile is a rail station */
1746 if (!HasStationTileRail(tile)) continue;
1747
1748 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1750 error.AddCost(std::move(ret));
1751 if (error.Failed()) continue;
1752
1753 /* Check ownership of station */
1754 T *st = T::GetByTile(tile);
1755 if (st == nullptr) continue;
1756
1758 ret = CheckOwnership(st->owner);
1759 error.AddCost(std::move(ret));
1760 if (error.Failed()) continue;
1761 }
1762
1763 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1764 quantity++;
1765
1766 if (keep_rail || IsStationTileBlocked(tile)) {
1767 /* Don't refund the 'steel' of the track when we keep the
1768 * rail, or when the tile didn't have any rail at all. */
1769 total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
1770 }
1771
1772 if (flags.Test(DoCommandFlag::Execute)) {
1773 /* read variables before the station tile is removed */
1774 uint specindex = GetCustomStationSpecIndex(tile);
1775 Track track = GetRailStationTrack(tile);
1776 Owner owner = GetTileOwner(tile);
1777 RailType rt = GetRailType(tile);
1778 Train *v = nullptr;
1779
1780 if (HasStationReservation(tile)) {
1781 v = GetTrainForReservation(tile, track);
1782 if (v != nullptr) FreeTrainReservation(v);
1783 }
1784
1785 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1786 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1787
1788 DoClearSquare(tile);
1789 DeleteNewGRFInspectWindow(GSF_STATIONS, tile.base());
1790 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
1791 Company::Get(owner)->infrastructure.station--;
1793
1794 st->rect.AfterRemoveTile(st, tile);
1795 AddTrackToSignalBuffer(tile, track, owner);
1796 YapfNotifyTrackLayoutChange(tile, track);
1797
1798 DeallocateSpecFromStation(st, specindex);
1799
1800 include(affected_stations, st);
1801
1802 if (v != nullptr) RestoreTrainReservation(v);
1803 }
1804 }
1805
1806 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1807
1808 for (T *st : affected_stations) {
1809
1810 /* now we need to make the "spanned" area of the railway station smaller
1811 * if we deleted something at the edges.
1812 * we also need to adjust train_tile. */
1813 MakeRailStationAreaSmaller(st);
1814 UpdateStationSignCoord(st);
1815
1816 /* if we deleted the whole station, delete the train facility. */
1817 if (st->train_station.tile == INVALID_TILE) {
1821 MarkCatchmentTilesDirty();
1822 st->UpdateVirtCoord();
1824 }
1825 }
1826
1827 total_cost.AddCost(quantity * removal_cost);
1828 return total_cost;
1829}
1830
1841{
1842 if (end == 0) end = start;
1843 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1844
1845 TileArea ta(start, end);
1846 std::vector<Station *> affected_stations;
1847
1848 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], keep_rail);
1849 if (ret.Failed()) return ret;
1850
1851 /* Do all station specific functions here. */
1852 for (Station *st : affected_stations) {
1853
1855 st->MarkTilesDirty(false);
1856 MarkCatchmentTilesDirty();
1857 st->RecomputeCatchment();
1858 }
1859
1860 /* Now apply the rail cost to the number that we deleted */
1861 return ret;
1862}
1863
1874{
1875 if (end == 0) end = start;
1876 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1877
1878 TileArea ta(start, end);
1879 std::vector<Waypoint *> affected_stations;
1880
1881 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], keep_rail);
1882}
1883
1884
1893template <class T>
1895{
1896 /* Current company owns the station? */
1898 CommandCost ret = CheckOwnership(st->owner);
1899 if (ret.Failed()) return ret;
1900 }
1901
1902 /* determine width and height of platforms */
1903 TileArea ta = st->train_station;
1904
1905 assert(ta.w != 0 && ta.h != 0);
1906
1908 /* clear all areas of the station */
1909 for (TileIndex tile : ta) {
1910 /* only remove tiles that are actually train station tiles */
1911 if (st->TileBelongsToRailStation(tile)) {
1912 std::vector<T*> affected_stations; // dummy
1913 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1914 if (ret.Failed()) return ret;
1915 cost.AddCost(ret.GetCost());
1916 }
1917 }
1918
1919 return cost;
1920}
1921
1929{
1930 /* if there is flooding, remove platforms tile by tile */
1933 }
1934
1935 Station *st = Station::GetByTile(tile);
1936 CommandCost cost = RemoveRailStation(st, flags, _price[PR_CLEAR_STATION_RAIL]);
1937
1939
1940 return cost;
1941}
1942
1950{
1951 /* if there is flooding, remove waypoints tile by tile */
1954 }
1955
1956 return RemoveRailStation(Waypoint::GetByTile(tile), flags, _price[PR_CLEAR_WAYPOINT_RAIL]);
1957}
1958
1959
1965static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
1966{
1967 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1968
1969 if (*primary_stop == nullptr) {
1970 /* we have no roadstop of the type yet, so write a "primary stop" */
1971 return primary_stop;
1972 } else {
1973 /* there are stops already, so append to the end of the list */
1974 RoadStop *stop = *primary_stop;
1975 while (stop->next != nullptr) stop = stop->next;
1976 return &stop->next;
1977 }
1978}
1979
1980static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1981CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1982
1992static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1993{
1994 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1995}
1996
2011CommandCost 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)
2012{
2013 DiagDirections invalid_dirs{};
2014 if (is_drive_through) {
2015 invalid_dirs.Set(AxisToDiagDir(axis));
2016 invalid_dirs.Set(ReverseDiagDir(AxisToDiagDir(axis)));
2017 } else {
2018 invalid_dirs.Set(ddir);
2019 }
2020
2021 /* Check every tile in the area. */
2022 int allowed_z = -1;
2024 for (TileIndex cur_tile : tile_area) {
2025 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, roadstopspec, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
2026 if (ret.Failed()) return ret;
2027
2028 bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
2029
2030 /* Only add costs if a stop doesn't already exist in the location */
2031 if (!is_preexisting_roadstop) {
2032 cost.AddCost(ret.GetCost());
2033 cost.AddCost(unit_cost);
2034 }
2035 }
2036
2037 return cost;
2038}
2039
2056CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
2057 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
2058{
2059 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= RoadStopType::End) return CMD_ERROR;
2060 bool reuse = (station_to_join != NEW_STATION);
2061 if (!reuse) station_to_join = StationID::Invalid();
2062 bool distant_join = (station_to_join != StationID::Invalid());
2063
2064 /* Check if the given station class is valid */
2065 if (static_cast<uint>(spec_class) >= RoadStopClass::GetClassCount()) return CMD_ERROR;
2066 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
2067 if (IsWaypointClass(*cls)) return CMD_ERROR;
2068 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
2069
2070 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
2071 if (roadstopspec != nullptr) {
2072 if (stop_type == RoadStopType::Truck && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2073 if (stop_type == RoadStopType::Bus && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
2074 if (!is_drive_through && roadstopspec->flags.Test(RoadStopSpecFlag::DriveThroughOnly)) return CMD_ERROR;
2075 }
2076
2077 /* Check if the requested road stop is too big */
2078 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2079 /* Check for incorrect width / length. */
2080 if (width == 0 || length == 0) return CMD_ERROR;
2081 /* Check if the first tile and the last tile are valid */
2082 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
2083
2084 TileArea roadstop_area(tile, width, length);
2085
2086 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2087
2088 /* Trams only have drive through stops */
2089 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
2090
2091 Axis axis = DiagDirToAxis(ddir);
2092
2094 if (ret.Failed()) return ret;
2095
2096 bool is_truck_stop = stop_type != RoadStopType::Bus;
2097
2098 /* Total road stop cost. */
2099 Money unit_cost;
2100 if (roadstopspec != nullptr) {
2101 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS);
2102 } else {
2103 unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
2104 }
2105 StationID est = StationID::Invalid();
2106 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, roadstopspec, axis, ddir, &est, rt, unit_cost);
2107 if (cost.Failed()) return cost;
2108
2109 Station *st = nullptr;
2110 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2111 if (ret.Failed()) return ret;
2112
2113 /* Check if this number of road stops can be allocated. */
2114 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);
2115
2116 ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD);
2117 if (ret.Failed()) return ret;
2118
2119 /* Check if we can allocate a custom stationspec to this station */
2120 auto specindex = AllocateSpecToRoadStop(roadstopspec, st);
2121 if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2122
2123 if (roadstopspec != nullptr) {
2124 /* Perform NewGRF checks */
2125
2126 /* Check if the road stop is buildable */
2127 if (roadstopspec->callback_mask.Test(RoadStopCallbackMask::Avail)) {
2128 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? StationType::Truck : StationType::Bus, 0);
2129 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2130 }
2131 }
2132
2133 if (flags.Test(DoCommandFlag::Execute)) {
2134 if (specindex.has_value()) AssignSpecToRoadStop(roadstopspec, st, *specindex);
2135 /* Check every tile in the area. */
2136 for (TileIndex cur_tile : roadstop_area) {
2137 /* Get existing road types and owners before any tile clearing */
2138 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
2139 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
2140 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
2141 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
2142
2143 if (IsTileType(cur_tile, MP_STATION) && IsStationRoadStop(cur_tile)) {
2144 RemoveRoadStop(cur_tile, flags, *specindex);
2145 }
2146
2147 if (roadstopspec != nullptr) {
2148 /* Include this road stop spec's animation trigger bitmask
2149 * in the station's cached copy. */
2150 st->cached_roadstop_anim_triggers.Set(roadstopspec->animation.triggers);
2151 }
2152
2153 RoadStop *road_stop = new RoadStop(cur_tile);
2154 /* Insert into linked list of RoadStops. */
2155 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2156 *currstop = road_stop;
2157
2158 if (is_truck_stop) {
2159 st->truck_station.Add(cur_tile);
2160 } else {
2161 st->bus_station.Add(cur_tile);
2162 }
2163
2164 /* Initialize an empty station. */
2165 st->AddFacility(is_truck_stop ? StationFacility::TruckStop : StationFacility::BusStop, cur_tile);
2166
2167 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
2168
2169 RoadStopType rs_type = is_truck_stop ? RoadStopType::Truck : RoadStopType::Bus;
2170 if (is_drive_through) {
2171 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2172 * bits first. */
2173 if (IsNormalRoadTile(cur_tile)) {
2174 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
2175 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
2176 }
2177
2178 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2179 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2180
2181 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == RoadStopType::Bus ? StationType::Bus : StationType::Truck), road_rt, tram_rt, axis);
2182 road_stop->MakeDriveThrough();
2183 } else {
2184 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2185 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2186 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2187 }
2190 Company::Get(st->owner)->infrastructure.station++;
2191
2192 SetCustomRoadStopSpecIndex(cur_tile, *specindex);
2193 if (roadstopspec != nullptr) {
2194 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2195 TriggerRoadStopAnimation(st, cur_tile, StationAnimationTrigger::Built);
2196 }
2197
2198 MarkTileDirtyByTile(cur_tile);
2199 }
2200
2201 if (st != nullptr) {
2202 st->AfterStationTileSetChange(true, is_truck_stop ? StationType::Truck: StationType::Bus);
2203 }
2204 }
2205 return cost;
2206}
2207
2215static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2216{
2217 Station *st = Station::GetByTile(tile);
2218
2220 CommandCost ret = CheckOwnership(st->owner);
2221 if (ret.Failed()) return ret;
2222 }
2223
2224 bool is_truck = IsTruckStop(tile);
2225
2226 RoadStop **primary_stop;
2227 RoadStop *cur_stop;
2228 if (is_truck) { // truck stop
2229 primary_stop = &st->truck_stops;
2230 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Truck);
2231 } else {
2232 primary_stop = &st->bus_stops;
2233 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Bus);
2234 }
2235
2236 assert(cur_stop != nullptr);
2237
2238 /* don't do the check for drive-through road stops when company bankrupts */
2240 /* remove the 'going through road stop' status from all vehicles on that tile */
2241 if (flags.Test(DoCommandFlag::Execute)) {
2242 for (Vehicle *v : VehiclesOnTile(tile)) {
2243 if (v->type != VEH_ROAD) continue;
2244 /* Okay... we are a road vehicle on a drive through road stop.
2245 * But that road stop has just been removed, so we need to make
2246 * sure we are in a valid state... however, vehicles can also
2247 * turn on road stop tiles, so only clear the 'road stop' state
2248 * bits and only when the state was 'in road stop', otherwise
2249 * we'll end up clearing the turn around bits. */
2252 }
2253 }
2254 } else {
2256 if (ret.Failed()) return ret;
2257 }
2258
2259 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2260
2261 if (flags.Test(DoCommandFlag::Execute)) {
2262 if (*primary_stop == cur_stop) {
2263 /* removed the first stop in the list */
2264 *primary_stop = cur_stop->next;
2265 /* removed the only stop? */
2266 if (*primary_stop == nullptr) {
2269 }
2270 } else {
2271 /* tell the predecessor in the list to skip this stop */
2272 RoadStop *pred = *primary_stop;
2273 while (pred->next != cur_stop) pred = pred->next;
2274 pred->next = cur_stop->next;
2275 }
2276
2277 /* Update company infrastructure counts. */
2278 for (RoadTramType rtt : _roadtramtypes) {
2279 RoadType rt = GetRoadType(tile, rtt);
2281 }
2282
2283 Company::Get(st->owner)->infrastructure.station--;
2285
2286 uint specindex = GetCustomRoadStopSpecIndex(tile);
2287
2288 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2289
2290 if (IsDriveThroughStopTile(tile)) {
2291 /* Clears the tile for us */
2292 cur_stop->ClearDriveThrough();
2293 DeleteAnimatedTile(tile);
2294 } else {
2295 DoClearSquare(tile);
2296 }
2297
2298 delete cur_stop;
2299
2300 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2301 * this station, then look for any currently heading to the tile. */
2302 StationID station_id = st->index;
2304 [](const Vehicle *v) { return v->type == VEH_ROAD; },
2305 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2306 [station_id, tile](Vehicle *v) {
2307 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2308 v->SetDestTile(v->GetOrderStationLocation(station_id));
2309 }
2310 }
2311 );
2312
2313 st->rect.AfterRemoveTile(st, tile);
2314
2315 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
2316
2317 st->RemoveRoadStopTileData(tile);
2318 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2319
2320 /* Update the tile area of the truck/bus stop */
2321 if (is_truck) {
2322 st->truck_station.Clear();
2323 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2324 } else {
2325 st->bus_station.Clear();
2326 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2327 }
2328 }
2329
2330 Price category = is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS;
2331 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2332}
2333
2341CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2342{
2343 Waypoint *wp = Waypoint::GetByTile(tile);
2344
2346 CommandCost ret = CheckOwnership(wp->owner);
2347 if (ret.Failed()) return ret;
2348 }
2349
2350 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2351 if (!flags.Test(DoCommandFlag::Bankrupt)) {
2353 if (ret.Failed()) return ret;
2354 }
2355
2356 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2357
2358 if (flags.Test(DoCommandFlag::Execute)) {
2359 /* Update company infrastructure counts. */
2360 for (RoadTramType rtt : _roadtramtypes) {
2361 RoadType rt = GetRoadType(tile, rtt);
2363 }
2364
2365 Company::Get(wp->owner)->infrastructure.station--;
2367
2368 uint specindex = GetCustomRoadStopSpecIndex(tile);
2369
2370 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2371
2372 DoClearSquare(tile);
2373
2374 wp->rect.AfterRemoveTile(wp, tile);
2375
2376 wp->RemoveRoadStopTileData(tile);
2377 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2378
2379 if (replacement_spec_index < 0) {
2380 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2381
2382 UpdateStationSignCoord(wp);
2383
2384 /* if we deleted the whole waypoint, delete the road facility. */
2388 wp->UpdateVirtCoord();
2390 }
2391 }
2392 }
2393
2394 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(PR_CLEAR_STATION_TRUCK) : _price[PR_CLEAR_STATION_TRUCK]);
2395}
2396
2405static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
2406{
2408 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2409 bool had_success = false;
2410
2411 for (TileIndex cur_tile : roadstop_area) {
2412 /* Make sure the specified tile is a road stop of the correct type */
2413 if (!IsTileType(cur_tile, MP_STATION) || !IsAnyRoadStop(cur_tile) || IsRoadWaypoint(cur_tile) != road_waypoint) continue;
2414
2415 /* Save information on to-be-restored roads before the stop is removed. */
2416 RoadBits road_bits = ROAD_NONE;
2417 RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
2418 Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
2419 if (IsDriveThroughStopTile(cur_tile)) {
2420 for (RoadTramType rtt : _roadtramtypes) {
2421 road_type[rtt] = GetRoadType(cur_tile, rtt);
2422 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2423 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2424 /* If we don't want to preserve our roads then restore only roads of others. */
2425 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2426 }
2427 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2428 }
2429
2430 CommandCost ret;
2431 if (road_waypoint) {
2432 ret = RemoveRoadWaypointStop(cur_tile, flags);
2433 } else {
2434 ret = RemoveRoadStop(cur_tile, flags);
2435 }
2436 if (ret.Failed()) {
2437 last_error = std::move(ret);
2438 continue;
2439 }
2440 cost.AddCost(ret.GetCost());
2441 had_success = true;
2442
2443 /* Restore roads. */
2444 if (flags.Test(DoCommandFlag::Execute) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
2445 MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2446 road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
2447
2448 /* Update company infrastructure counts. */
2449 int count = CountBits(road_bits);
2450 UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
2451 UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
2452 }
2453 }
2454
2455 return had_success ? cost : last_error;
2456}
2457
2468CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2469{
2470 if (stop_type >= RoadStopType::End) return CMD_ERROR;
2471 /* Check for incorrect width / height. */
2472 if (width == 0 || height == 0) return CMD_ERROR;
2473 /* Check if the first tile and the last tile are valid */
2474 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2475 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2476 if (remove_road && flags.Test(DoCommandFlag::Bankrupt)) return CMD_ERROR;
2477
2478 TileArea roadstop_area(tile, width, height);
2479
2480 return RemoveGenericRoadStop(flags, roadstop_area, false, remove_road);
2481}
2482
2491{
2492 if (end == 0) end = start;
2493 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2494
2495 TileArea roadstop_area(start, end);
2496
2497 return RemoveGenericRoadStop(flags, roadstop_area, true, false);
2498}
2499
2508uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2509{
2510 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2511 * So no need to go any further*/
2512 if (as->noise_level < 2) return as->noise_level;
2513
2514 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2515 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2516 * Basically, it says that the less tolerant a town is, the bigger the distance before
2517 * an actual decrease can be granted */
2518 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2519
2520 /* now, we want to have the distance segmented using the distance judged bareable by town
2521 * This will give us the coefficient of reduction the distance provides. */
2522 uint noise_reduction = distance / town_tolerance_distance;
2523
2524 /* If the noise reduction equals the airport noise itself, don't give it for free.
2525 * Otherwise, simply reduce the airport's level. */
2526 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2527}
2528
2539Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2540{
2541 assert(Town::GetNumItems() > 0);
2542
2543 Town *nearest = nullptr;
2544
2545 auto width = as->size_x;
2546 auto height = as->size_y;
2547 if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
2548
2549 uint perimeter_min_x = TileX(tile);
2550 uint perimeter_min_y = TileY(tile);
2551 uint perimeter_max_x = perimeter_min_x + width - 1;
2552 uint perimeter_max_y = perimeter_min_y + height - 1;
2553
2554 mindist = UINT_MAX - 1; // prevent overflow
2555
2556 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2557 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2558 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2559 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) {
2560 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2561 if (t == nullptr) continue;
2562
2563 uint dist = DistanceManhattan(t->xy, cur_tile);
2564 if (dist == mindist && t->index < nearest->index) nearest = t;
2565 if (dist < mindist) {
2566 nearest = t;
2567 mindist = dist;
2568 }
2569 }
2570 }
2571
2572 return nearest;
2573}
2574
2582static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2583{
2585}
2586
2587
2590{
2591 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2592
2593 for (const Station *st : Station::Iterate()) {
2594 if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
2595 uint dist;
2596 Town *nearest = AirportGetNearestTown(st, dist);
2597 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2598 }
2599 }
2600}
2601
2612CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2613{
2614 bool reuse = (station_to_join != NEW_STATION);
2615 if (!reuse) station_to_join = StationID::Invalid();
2616 bool distant_join = (station_to_join != StationID::Invalid());
2617
2618 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2619
2620 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2621
2623 if (ret.Failed()) return ret;
2624
2625 /* Check if a valid, buildable airport was chosen for construction */
2626 const AirportSpec *as = AirportSpec::Get(airport_type);
2627 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2628 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2629
2630 Direction rotation = as->layouts[layout].rotation;
2631 int w = as->size_x;
2632 int h = as->size_y;
2633 if (rotation == DIR_E || rotation == DIR_W) std::swap(w, h);
2634 TileArea airport_area = TileArea(tile, w, h);
2635
2637 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2638 }
2639
2640 AirportTileTableIterator tile_iter(as->layouts[layout].tiles, tile);
2641 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2642 if (cost.Failed()) return cost;
2643
2644 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2645 uint dist;
2646 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2647 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2648
2649 /* Check if local auth would allow a new airport */
2650 StringID authority_refuse_message = STR_NULL;
2651 Town *authority_refuse_town = nullptr;
2652
2654 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2655 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2656 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2657 authority_refuse_town = nearest;
2658 }
2659 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2660 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2661 uint num = 0;
2662 for (const Station *st : Station::Iterate()) {
2663 if (st->town == t && st->facilities.Test(StationFacility::Airport) && st->airport.type != AT_OILRIG) num++;
2664 }
2665 if (num >= 2) {
2666 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2667 authority_refuse_town = t;
2668 }
2669 }
2670
2671 if (authority_refuse_message != STR_NULL) {
2672 return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index);
2673 }
2674
2675 Station *st = nullptr;
2676 ret = FindJoiningStation(StationID::Invalid(), station_to_join, allow_adjacent, airport_area, &st);
2677 if (ret.Failed()) return ret;
2678
2679 /* Distant join */
2680 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2681
2682 ret = BuildStationPart(&st, flags, reuse, airport_area, GetAirport(airport_type)->flags.Test(AirportFTAClass::Flag::Airplanes) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT);
2683 if (ret.Failed()) return ret;
2684
2685 if (st != nullptr && st->airport.tile != INVALID_TILE) {
2686 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2687 }
2688
2689 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2690 cost.AddCost(_price[PR_BUILD_STATION_AIRPORT]);
2691 }
2692
2693 if (flags.Test(DoCommandFlag::Execute)) {
2694 /* Always add the noise, so there will be no need to recalculate when option toggles */
2695 nearest->noise_reached += newnoise_level;
2696
2698 st->airport.type = airport_type;
2699 st->airport.layout = layout;
2700 st->airport.blocks = {};
2701 st->airport.rotation = rotation;
2702
2703 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
2704
2705 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2706 Tile t(iter);
2707 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WaterClass::Invalid);
2708 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2709 st->airport.Add(iter);
2710
2712 }
2713
2714 /* Only call the animation trigger after all tiles have been built */
2715 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2716 TriggerAirportTileAnimation(st, iter, AirportAnimationTrigger::Built);
2717 }
2718
2720
2721 Company::Get(st->owner)->infrastructure.airport++;
2722
2723 st->AfterStationTileSetChange(true, StationType::Airport);
2725
2728 }
2729 }
2730
2731 return cost;
2732}
2733
2741{
2742 Station *st = Station::GetByTile(tile);
2743
2745 CommandCost ret = CheckOwnership(st->owner);
2746 if (ret.Failed()) return ret;
2747 }
2748
2749 tile = st->airport.tile;
2750
2752
2753 for (const Aircraft *a : Aircraft::Iterate()) {
2754 if (!a->IsNormalAircraft()) continue;
2755 if (a->targetairport == st->index && a->state != FLYING) {
2756 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2757 }
2758 }
2759
2760 if (flags.Test(DoCommandFlag::Execute)) {
2761 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2762 TileIndex tile_cur = st->airport.GetHangarTile(i);
2763 OrderBackup::Reset(tile_cur, false);
2765 }
2766
2767 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2768 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2769 * need of recalculation */
2770 uint dist;
2771 Town *nearest = AirportGetNearestTown(st, dist);
2773
2776 }
2777 }
2778
2779 for (TileIndex tile_cur : st->airport) {
2780 if (!st->TileBelongsToAirport(tile_cur)) continue;
2781
2782 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2783 if (ret.Failed()) return ret;
2784
2785 cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
2786
2787 if (flags.Test(DoCommandFlag::Execute)) {
2788 DoClearSquare(tile_cur);
2789 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur.base());
2790 }
2791 }
2792
2793 if (flags.Test(DoCommandFlag::Execute)) {
2794 /* Clear the persistent storage. */
2795 delete st->airport.psa;
2796
2797 st->rect.AfterRemoveRect(st, st->airport);
2798
2799 st->airport.Clear();
2802
2804
2805 Company::Get(st->owner)->infrastructure.airport--;
2806
2807 st->AfterStationTileSetChange(false, StationType::Airport);
2808
2809 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
2810 }
2811
2812 return cost;
2813}
2814
2822{
2823 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2824 Station *st = Station::Get(station_id);
2825
2827
2828 CommandCost ret = CheckOwnership(st->owner);
2829 if (ret.Failed()) return ret;
2830
2831 if (flags.Test(DoCommandFlag::Execute)) {
2834 }
2835 return CommandCost();
2836}
2837
2844bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2845{
2846 for (const OrderList *orderlist : OrderList::Iterate()) {
2847 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2848 assert(v != nullptr);
2849 if ((v->owner == company) != include_company) continue;
2850
2851 for (const Order &order : orderlist->GetOrders()) {
2852 if (order.GetDestination() == station && (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT))) {
2853 return true;
2854 }
2855 }
2856 }
2857 return false;
2858}
2859
2860static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
2861 {-1, 0},
2862 { 0, 0},
2863 { 0, 0},
2864 { 0, -1}
2865};
2866static const uint8_t _dock_w_chk[4] = { 2, 1, 2, 1 };
2867static const uint8_t _dock_h_chk[4] = { 1, 2, 1, 2 };
2868
2877CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
2878{
2879 bool reuse = (station_to_join != NEW_STATION);
2880 if (!reuse) station_to_join = StationID::Invalid();
2881 bool distant_join = (station_to_join != StationID::Invalid());
2882
2883 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2884
2886 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2887 direction = ReverseDiagDir(direction);
2888
2889 /* Docks cannot be placed on rapids */
2890 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2891
2893 if (ret.Failed()) return ret;
2894
2895 ret = IsDockBridgeAboveOk(tile, to_underlying(direction));
2896 if (ret.Failed()) return ret;
2897
2898 CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
2899 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
2900 if (ret.Failed()) return ret;
2901 cost.AddCost(ret.GetCost());
2902
2903 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2904
2905 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2906 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2907 }
2908
2910 if (ret.Failed()) return ret;
2911
2912 /* Get the water class of the water tile before it is cleared.*/
2913 WaterClass wc = GetWaterClass(tile_cur);
2914
2915 bool add_cost = !IsWaterTile(tile_cur);
2916 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
2917 if (ret.Failed()) return ret;
2918 if (add_cost) cost.AddCost(ret.GetCost());
2919
2920 tile_cur += TileOffsByDiagDir(direction);
2921 if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) {
2922 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2923 }
2924
2925 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2926 _dock_w_chk[direction], _dock_h_chk[direction]);
2927
2928 /* middle */
2929 Station *st = nullptr;
2930 ret = FindJoiningStation(StationID::Invalid(), station_to_join, adjacent, dock_area, &st);
2931 if (ret.Failed()) return ret;
2932
2933 /* Distant join */
2934 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2935
2936 ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
2937 if (ret.Failed()) return ret;
2938
2939 if (flags.Test(DoCommandFlag::Execute)) {
2940 st->ship_station.Add(tile);
2941 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2942 st->ship_station.Add(flat_tile);
2944
2945 st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
2946
2947 /* If the water part of the dock is on a canal, update infrastructure counts.
2948 * This is needed as we've cleared that tile before.
2949 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2950 * See: MakeWaterKeepingClass() */
2951 if (wc == WaterClass::Canal && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WaterClass::Canal && IsTileOwner(flat_tile, _current_company))) {
2952 Company::Get(st->owner)->infrastructure.water++;
2953 }
2954 Company::Get(st->owner)->infrastructure.station += 2;
2955
2956 MakeDock(tile, st->owner, st->index, direction, wc);
2957 UpdateStationDockingTiles(st);
2958
2959 st->AfterStationTileSetChange(true, StationType::Dock);
2960 }
2961
2962 return cost;
2963}
2964
2965void RemoveDockingTile(TileIndex t)
2966{
2967 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2968 TileIndex tile = t + TileOffsByDiagDir(d);
2969 if (!IsValidTile(tile)) continue;
2970
2971 if (IsTileType(tile, MP_STATION)) {
2972 Station *st = Station::GetByTile(tile);
2973 if (st != nullptr) UpdateStationDockingTiles(st);
2974 } else if (IsTileType(tile, MP_INDUSTRY)) {
2976 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
2977 }
2978 }
2979}
2980
2987{
2988 assert(IsValidTile(tile));
2989
2990 /* Clear and maybe re-set docking tile */
2991 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2992 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
2993 if (!IsValidTile(docking_tile)) continue;
2994
2995 if (IsPossibleDockingTile(docking_tile)) {
2996 SetDockingTile(docking_tile, false);
2997 CheckForDockingTile(docking_tile);
2998 }
2999 }
3000}
3001
3008{
3009 assert(IsDockTile(t));
3010
3011 StationGfx gfx = GetStationGfx(t);
3012 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
3013
3014 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
3015 TileIndex tile = t + TileOffsByDiagDir(d);
3016 if (!IsValidTile(tile)) continue;
3017 if (!IsDockTile(tile)) continue;
3018 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
3019 }
3020
3021 return INVALID_TILE;
3022}
3023
3031{
3032 Station *st = Station::GetByTile(tile);
3033 CommandCost ret = CheckOwnership(st->owner);
3034 if (ret.Failed()) return ret;
3035
3036 if (!IsDockTile(tile)) return CMD_ERROR;
3037
3038 TileIndex tile1 = FindDockLandPart(tile);
3039 if (tile1 == INVALID_TILE) return CMD_ERROR;
3040 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
3041
3042 ret = EnsureNoVehicleOnGround(tile1);
3043 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
3044 if (ret.Failed()) return ret;
3045
3046 if (flags.Test(DoCommandFlag::Execute)) {
3047 DoClearSquare(tile1);
3048 MarkTileDirtyByTile(tile1);
3049 MakeWaterKeepingClass(tile2, st->owner);
3050
3051 st->rect.AfterRemoveTile(st, tile1);
3052 st->rect.AfterRemoveTile(st, tile2);
3053
3054 MakeShipStationAreaSmaller(st);
3055 if (st->ship_station.tile == INVALID_TILE) {
3056 st->ship_station.Clear();
3057 st->docking_station.Clear();
3060 }
3061
3062 Company::Get(st->owner)->infrastructure.station -= 2;
3063
3064 st->AfterStationTileSetChange(false, StationType::Dock);
3065
3068
3069 for (Ship *s : Ship::Iterate()) {
3070 /* Find all ships going to our dock. */
3071 if (s->current_order.GetDestination() != st->index) {
3072 continue;
3073 }
3074
3075 /* Find ships that are marked as "loading" but are no longer on a
3076 * docking tile. Force them to leave the station (as they were loading
3077 * on the removed dock). */
3078 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
3079 s->LeaveStation();
3080 }
3081
3082 /* If we no longer have a dock, mark the order as invalid and send
3083 * the ship to the next order (or, if there is none, make it
3084 * wander the world). */
3085 if (s->current_order.IsType(OT_GOTO_STATION) && !st->facilities.Test(StationFacility::Dock)) {
3086 s->SetDestTile(s->GetOrderStationLocation(st->index));
3087 }
3088 }
3089 }
3090
3091 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
3092}
3093
3101{
3102 const auto &layouts = _station_display_datas[to_underlying(st)];
3103 if (gfx >= layouts.size()) gfx &= 1;
3104 return layouts.data() + gfx;
3105}
3106
3116bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3117{
3118 bool snow_desert;
3119 switch (*ground) {
3120 case SPR_RAIL_TRACK_X:
3121 case SPR_MONO_TRACK_X:
3122 case SPR_MGLV_TRACK_X:
3123 snow_desert = false;
3124 *overlay_offset = RTO_X;
3125 break;
3126
3127 case SPR_RAIL_TRACK_Y:
3128 case SPR_MONO_TRACK_Y:
3129 case SPR_MGLV_TRACK_Y:
3130 snow_desert = false;
3131 *overlay_offset = RTO_Y;
3132 break;
3133
3134 case SPR_RAIL_TRACK_X_SNOW:
3135 case SPR_MONO_TRACK_X_SNOW:
3136 case SPR_MGLV_TRACK_X_SNOW:
3137 snow_desert = true;
3138 *overlay_offset = RTO_X;
3139 break;
3140
3141 case SPR_RAIL_TRACK_Y_SNOW:
3142 case SPR_MONO_TRACK_Y_SNOW:
3143 case SPR_MGLV_TRACK_Y_SNOW:
3144 snow_desert = true;
3145 *overlay_offset = RTO_Y;
3146 break;
3147
3148 default:
3149 return false;
3150 }
3151
3152 if (ti != nullptr) {
3153 /* Decide snow/desert from tile */
3155 case LandscapeType::Arctic:
3156 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3157 break;
3158
3159 case LandscapeType::Tropic:
3160 snow_desert = GetTropicZone(ti->tile) == TROPICZONE_DESERT;
3161 break;
3162
3163 default:
3164 break;
3165 }
3166 }
3167
3168 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3169 return true;
3170}
3171
3178static BridgePillarFlags GetStationBlockedPillars(std::span<const BridgeableTileInfo> bridgeable_info, uint8_t layout)
3179{
3180 if (layout < std::size(bridgeable_info)) return bridgeable_info[layout].disallowed_pillars;
3181 return BRIDGEPILLARFLAGS_ALL;
3182}
3183
3193{
3194 if (statspec == nullptr || !statspec->flags.Test(StationSpecFlag::CustomFoundations)) return false;
3195
3196 /* Station has custom foundations.
3197 * Check whether the foundation continues beyond the tile's upper sides. */
3198 uint edge_info = 0;
3199 auto [slope, z] = GetFoundationPixelSlope(ti->tile);
3200 if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
3201 if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
3202
3203 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, gfx, edge_info);
3204 if (image == 0) return false;
3205
3207 /* Station provides extended foundations. */
3208 static constexpr uint8_t foundation_parts[] = {
3209 UINT8_MAX, UINT8_MAX, UINT8_MAX, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3210 UINT8_MAX, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3211 UINT8_MAX, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3212 7, 8, 9, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3213 };
3214 assert(ti->tileh < std::size(foundation_parts));
3215 if (foundation_parts[ti->tileh] == UINT8_MAX) return false;
3216
3217 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3218 } else {
3219 /* Draw simple foundations, built up from 8 possible foundation sprites.
3220 * Each set bit represents one of the eight composite sprites to be drawn.
3221 * 'Invalid' entries will not drawn but are included for completeness. */
3222 static constexpr uint8_t composite_foundation_parts[] = {
3223 0b0000'0000, 0b1101'0001, 0b1110'0100, 0b1110'0000, // Invalid, Invalid, Invalid, SLOPE_SW
3224 0b1100'1010, 0b1100'1001, 0b1100'0100, 0b1100'0000, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3225 0b1101'0010, 0b1001'0001, 0b1110'0100, 0b1010'0000, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3226 0b0100'1010, 0b0000'1001, 0b0100'0100, // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3227 };
3228 assert(ti->tileh < std::size(composite_foundation_parts));
3229
3230 uint8_t parts = composite_foundation_parts[ti->tileh];
3231
3232 /* If foundations continue beyond the tile's upper sides then mask out the last two pieces. */
3233 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3234 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3235
3236 /* We always have to draw at least one sprite, so if we have no parts to draw fall back to default foundation. */
3237 if (parts == 0) return false;
3238
3240 for (uint i : SetBitIterator(parts)) {
3241 AddSortableSpriteToDraw(image + i, PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1}, {}});
3242 }
3244 }
3245
3246 OffsetGroundSprite(0, -static_cast<int>(TILE_HEIGHT));
3248
3249 return true;
3250}
3251
3252static void DrawTile_Station(TileInfo *ti)
3253{
3254 const NewGRFSpriteLayout *layout = nullptr;
3255 SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_layout and t
3256 DrawTileSpriteSpan tmp_layout;
3257 const DrawTileSprites *t = nullptr;
3258 int32_t total_offset;
3259 const RailTypeInfo *rti = nullptr;
3260 uint32_t relocation = 0;
3261 uint32_t ground_relocation = 0;
3262 BaseStation *st = nullptr;
3263 const StationSpec *statspec = nullptr;
3264 uint tile_layout = 0;
3265 auto bridgeable_info = GetStationBridgeableTileInfo(GetStationType(ti->tile));
3266
3267 if (HasStationRail(ti->tile)) {
3268 rti = GetRailTypeInfo(GetRailType(ti->tile));
3269 total_offset = rti->GetRailtypeSpriteOffset();
3270
3271 if (IsCustomStationSpecIndex(ti->tile)) {
3272 /* look for customization */
3273 st = BaseStation::GetByTile(ti->tile);
3274 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3275
3276 if (statspec != nullptr) {
3277 tile_layout = GetStationGfx(ti->tile);
3278
3280 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3281 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
3282 }
3283
3284 /* Ensure the chosen tile layout is valid for this custom station */
3285 if (!statspec->renderdata.empty()) {
3286 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3287 if (!layout->NeedsPreprocessing()) {
3288 t = layout;
3289 layout = nullptr;
3290 }
3291 }
3292 }
3293 }
3294 if (statspec != nullptr) bridgeable_info = statspec->bridgeable_info;
3295 } else {
3296 total_offset = 0;
3297 }
3298
3299 StationGfx gfx = GetStationGfx(ti->tile);
3300 if (IsAirport(ti->tile)) {
3301 gfx = GetAirportGfx(ti->tile);
3302 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3303 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3304 if (ats->grf_prop.HasSpriteGroups() && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3305 return;
3306 }
3307 /* No sprite group (or no valid one) found, meaning no graphics associated.
3308 * Use the substitute one instead */
3309 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3310 gfx = ats->grf_prop.subst_id;
3311 }
3312 switch (gfx) {
3313 case APT_RADAR_GRASS_FENCE_SW:
3314 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3315 break;
3316 case APT_GRASS_FENCE_NE_FLAG:
3317 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3318 break;
3319 case APT_RADAR_FENCE_SW:
3320 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3321 break;
3322 case APT_RADAR_FENCE_NE:
3323 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3324 break;
3325 case APT_GRASS_FENCE_NE_FLAG_2:
3326 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3327 break;
3328 }
3329 }
3330
3331 Owner owner = GetTileOwner(ti->tile);
3332
3333 PaletteID palette;
3334 if (Company::IsValidID(owner)) {
3335 palette = GetCompanyPalette(owner);
3336 } else {
3337 /* Some stations are not owner by a company, namely oil rigs */
3338 palette = PALETTE_TO_GREY;
3339 }
3340
3341 if (layout == nullptr && t == nullptr) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3342
3343 /* don't show foundation for docks */
3344 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3345 if (!DrawCustomStationFoundations(statspec, st, ti, tile_layout)) {
3347 }
3348 }
3349
3350 bool draw_ground = false;
3351
3352 if (IsBuoy(ti->tile)) {
3353 DrawWaterClassGround(ti);
3354 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
3355 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3356 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3357 if (ti->tileh == SLOPE_FLAT) {
3358 DrawWaterClassGround(ti);
3359 } else {
3360 assert(IsDock(ti->tile));
3361 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3362 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WaterClass::Invalid;
3363 if (wc == WaterClass::Sea) {
3364 DrawShoreTile(ti->tileh);
3365 } else {
3366 DrawClearLandTile(ti, 3);
3367 }
3368 }
3369 } else if (IsRoadWaypointTile(ti->tile)) {
3371 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3372 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3373 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3374 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3375 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3376 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3377
3378 if (ti->tileh != SLOPE_FLAT) {
3380 }
3381
3382 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3383 } else {
3384 if (layout != nullptr) {
3385 /* Sprite layout which needs preprocessing */
3386 bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround);
3387 processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3388 GetCustomStationRelocation(processor, statspec, st, ti->tile);
3389 tmp_layout = processor.GetLayout();
3390 t = &tmp_layout;
3391 total_offset = 0;
3392 } else if (statspec != nullptr) {
3393 /* Simple sprite layout */
3394 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3396 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3397 }
3398 ground_relocation += rti->fallback_railtype;
3399 }
3400
3401 draw_ground = true;
3402 }
3403
3404 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3405 SpriteID image = t->ground.sprite;
3406 PaletteID pal = t->ground.pal;
3407 RailTrackOffset overlay_offset;
3408 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3409 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
3410 DrawGroundSprite(image, PAL_NONE);
3411 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3412
3413 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3414 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
3415 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3416 }
3417 } else {
3418 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3419 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3420 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3421
3422 /* PBS debugging, draw reserved tracks darker */
3423 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3424 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
3425 }
3426 }
3427 }
3428
3430
3431 if (IsAnyRoadStop(ti->tile)) {
3432 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3433 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3434 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3435 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3436
3437 StationGfx view = GetStationGfx(ti->tile);
3438 StationType type = GetStationType(ti->tile);
3439
3440 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3441 RoadStopDrawModes stop_draw_mode{};
3442 if (stopspec != nullptr) {
3443 stop_draw_mode = stopspec->draw_mode;
3444 st = BaseStation::GetByTile(ti->tile);
3445 std::array<int32_t, 1> regs100;
3446 auto result = GetRoadStopLayout(ti, stopspec, st, type, view, regs100);
3447 if (result.has_value()) {
3448 if (stopspec->flags.Test(RoadStopSpecFlag::DrawModeRegister)) {
3449 stop_draw_mode = static_cast<RoadStopDrawModes>(regs100[0]);
3450 }
3451 if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) {
3452 draw_ground = true;
3453 }
3454 processor = std::move(*result);
3455 tmp_layout = processor.GetLayout();
3456 t = &tmp_layout;
3457 }
3458 }
3459
3460 /* Draw ground sprite */
3461 if (draw_ground) {
3462 SpriteID image = t->ground.sprite;
3463 PaletteID pal = t->ground.pal;
3464 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3465 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3466 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3467 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3468 }
3469 }
3470
3471 if (IsDriveThroughStopTile(ti->tile)) {
3472 if (type != StationType::RoadWaypoint && (stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Overlay))) {
3473 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
3474 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3475 }
3476 } else {
3477 /* Non-drivethrough road stops are only valid for roads. */
3478 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3479
3480 if ((stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Road)) && road_rti->UsesOverlay()) {
3481 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
3482 DrawGroundSprite(ground + view, PAL_NONE);
3483 }
3484 }
3485 if (stopspec != nullptr) bridgeable_info = stopspec->bridgeable_info;
3486
3487 if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
3488 /* Draw road, tram catenary */
3489 DrawRoadCatenary(ti);
3490 }
3491 }
3492
3493 if (IsRailWaypoint(ti->tile)) {
3494 /* Don't offset the waypoint graphics; they're always the same. */
3495 total_offset = 0;
3496 }
3497
3498 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
3499 DrawBridgeMiddle(ti, GetStationBlockedPillars(bridgeable_info, GetStationGfx(ti->tile)));
3500}
3501
3502void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3503{
3504 int32_t total_offset = 0;
3506 const DrawTileSprites *t = GetStationTileLayout(st, image);
3507 const RailTypeInfo *railtype_info = nullptr;
3508
3509 if (railtype != INVALID_RAILTYPE) {
3510 railtype_info = GetRailTypeInfo(railtype);
3511 total_offset = railtype_info->GetRailtypeSpriteOffset();
3512 }
3513
3514 SpriteID img = t->ground.sprite;
3515 RailTrackOffset overlay_offset;
3516 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3517 SpriteID ground = GetCustomRailSprite(railtype_info, INVALID_TILE, RTSG_GROUND);
3518 DrawSprite(img, PAL_NONE, x, y);
3519 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3520 } else {
3521 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3522 }
3523
3524 if (roadtype != INVALID_ROADTYPE) {
3525 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3526 if (image >= 4) {
3527 /* Drive-through stop */
3528 uint sprite_offset = 5 - image;
3529
3530 /* Road underlay takes precedence over tram */
3531 if (roadtype_info->UsesOverlay()) {
3532 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_GROUND);
3533 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3534
3535 SpriteID overlay = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_OVERLAY);
3536 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3537 } else if (RoadTypeIsTram(roadtype)) {
3538 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3539 }
3540 } else {
3541 /* Bay stop */
3542 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3543 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_ROADSTOP);
3544 DrawSprite(ground + image, PAL_NONE, x, y);
3545 }
3546 }
3547 }
3548
3549 /* Default waypoint has no railtype specific sprites */
3550 DrawRailTileSeqInGUI(x, y, t, (st == StationType::RailWaypoint || st == StationType::RoadWaypoint) ? 0 : total_offset, 0, pal);
3551}
3552
3553static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)
3554{
3555 return GetTileMaxPixelZ(tile);
3556}
3557
3558static Foundation GetFoundation_Station(TileIndex, Slope tileh)
3559{
3560 return FlatteningFoundation(tileh);
3561}
3562
3563static void FillTileDescRoadStop(TileIndex tile, TileDesc &td)
3564{
3565 RoadType road_rt = GetRoadTypeRoad(tile);
3566 RoadType tram_rt = GetRoadTypeTram(tile);
3567 Owner road_owner = INVALID_OWNER;
3568 Owner tram_owner = INVALID_OWNER;
3569 if (road_rt != INVALID_ROADTYPE) {
3570 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3571 td.roadtype = rti->strings.name;
3572 td.road_speed = rti->max_speed / 2;
3573 road_owner = GetRoadOwner(tile, RTT_ROAD);
3574 }
3575
3576 if (tram_rt != INVALID_ROADTYPE) {
3577 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3578 td.tramtype = rti->strings.name;
3579 td.tram_speed = rti->max_speed / 2;
3580 tram_owner = GetRoadOwner(tile, RTT_TRAM);
3581 }
3582
3583 if (IsDriveThroughStopTile(tile)) {
3584 /* Is there a mix of owners? */
3585 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
3586 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
3587 uint i = 1;
3588 if (road_owner != INVALID_OWNER) {
3589 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3590 td.owner[i] = road_owner;
3591 i++;
3592 }
3593 if (tram_owner != INVALID_OWNER) {
3594 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3595 td.owner[i] = tram_owner;
3596 }
3597 }
3598 }
3599}
3600
3601void FillTileDescRailStation(TileIndex tile, TileDesc &td)
3602{
3603 const StationSpec *spec = GetStationSpec(tile);
3604
3605 if (spec != nullptr) {
3607 td.station_name = spec->name;
3608
3609 if (spec->grf_prop.HasGrfFile()) {
3610 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3611 td.grf = gc->GetName();
3612 }
3613 }
3614
3615 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3616 td.rail_speed = rti->max_speed;
3617 td.railtype = rti->strings.name;
3618}
3619
3620void FillTileDescAirport(TileIndex tile, TileDesc &td)
3621{
3622 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3624 td.airport_name = as->name;
3625
3627 td.airport_tile_name = ats->name;
3628
3629 if (as->grf_prop.HasGrfFile()) {
3630 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3631 td.grf = gc->GetName();
3632 } else if (ats->grf_prop.HasGrfFile()) {
3633 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3634 td.grf = gc->GetName();
3635 }
3636}
3637
3638static void GetTileDesc_Station(TileIndex tile, TileDesc &td)
3639{
3640 td.owner[0] = GetTileOwner(tile);
3642
3643 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3644 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3645 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3646
3647 StringID str;
3648 switch (GetStationType(tile)) {
3649 default: NOT_REACHED();
3650 case StationType::Rail: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3651 case StationType::Airport:
3652 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3653 break;
3654 case StationType::Truck: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3655 case StationType::Bus: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3656 case StationType::Oilrig: {
3657 const Industry *i = Station::GetByTile(tile)->industry;
3658 const IndustrySpec *is = GetIndustrySpec(i->type);
3659 td.owner[0] = i->owner;
3660 str = is->name;
3661 if (is->grf_prop.HasGrfFile()) td.grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3662 break;
3663 }
3664 case StationType::Dock: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3665 case StationType::Buoy: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3666 case StationType::RailWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3667 case StationType::RoadWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3668 }
3669 td.str = str;
3670}
3671
3672
3673static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
3674{
3675 TrackBits trackbits = TRACK_BIT_NONE;
3676
3677 switch (mode) {
3678 case TRANSPORT_RAIL:
3679 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3680 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
3681 }
3682 break;
3683
3684 case TRANSPORT_WATER:
3685 /* buoy is coded as a station, it is always on open water */
3686 if (IsBuoy(tile)) {
3687 trackbits = TRACK_BIT_ALL;
3688 /* remove tracks that connect NE map edge */
3689 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
3690 /* remove tracks that connect NW map edge */
3691 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
3692 }
3693 break;
3694
3695 case TRANSPORT_ROAD:
3696 if (IsAnyRoadStop(tile)) {
3697 RoadTramType rtt = (RoadTramType)sub_mode;
3698 if (!HasTileRoadType(tile, rtt)) break;
3699
3700 if (IsBayRoadStopTile(tile)) {
3701 DiagDirection dir = GetBayRoadStopDir(tile);
3702 if (side != INVALID_DIAGDIR && dir != side) break;
3703 trackbits = DiagDirToDiagTrackBits(dir);
3704 } else {
3705 Axis axis = GetDriveThroughStopAxis(tile);
3706 if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
3707 trackbits = AxisToTrackBits(axis);
3708 }
3709 }
3710 break;
3711
3712 default:
3713 break;
3714 }
3715
3717}
3718
3719
3720static void TileLoop_Station(TileIndex tile)
3721{
3722 auto *st = BaseStation::GetByTile(tile);
3723 switch (GetStationType(tile)) {
3724 case StationType::Airport:
3725 TriggerAirportTileAnimation(Station::From(st), tile, AirportAnimationTrigger::TileLoop);
3726 break;
3727
3728 case StationType::Rail:
3729 case StationType::RailWaypoint:
3730 TriggerStationAnimation(st, tile, StationAnimationTrigger::TileLoop);
3731 break;
3732
3733 case StationType::Dock:
3734 if (!IsTileFlat(tile)) break; // only handle water part
3735 [[fallthrough]];
3736
3737 case StationType::Oilrig: //(station part)
3738 case StationType::Buoy:
3739 TileLoop_Water(tile);
3740 break;
3741
3742 case StationType::Truck:
3743 case StationType::Bus:
3744 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3745 break;
3746
3747 case StationType::RoadWaypoint: {
3749 case LandscapeType::Arctic:
3750 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3752 MarkTileDirtyByTile(tile);
3753 }
3754 break;
3755
3756 case LandscapeType::Tropic:
3759 MarkTileDirtyByTile(tile);
3760 }
3761 break;
3762
3763 default: break;
3764 }
3765
3766 HouseZone new_zone = HouseZone::TownEdge;
3767 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3768 if (t != nullptr) {
3769 new_zone = GetTownRadiusGroup(t, tile);
3770 }
3771
3772 /* Adjust road ground type depending on 'new_zone' */
3773 Roadside new_rs = new_zone != HouseZone::TownEdge ? Roadside::Paved : Roadside::Grass;
3774 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3775
3776 if (new_rs != cur_rs) {
3777 SetRoadWaypointRoadside(tile, cur_rs == Roadside::Barren ? new_rs : Roadside::Barren);
3778 MarkTileDirtyByTile(tile);
3779 }
3780
3781 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3782 break;
3783 }
3784
3785 default: break;
3786 }
3787}
3788
3789
3790static void AnimateTile_Station(TileIndex tile)
3791{
3792 if (HasStationRail(tile)) {
3793 AnimateStationTile(tile);
3794 return;
3795 }
3796
3797 if (IsAirport(tile)) {
3798 AnimateAirportTile(tile);
3799 return;
3800 }
3801
3802 if (IsAnyRoadStopTile(tile)) {
3803 AnimateRoadStopTile(tile);
3804 return;
3805 }
3806}
3807
3808
3809static bool ClickTile_Station(TileIndex tile)
3810{
3811 const BaseStation *bst = BaseStation::GetByTile(tile);
3812
3815 } else if (IsHangar(tile)) {
3816 const Station *st = Station::From(bst);
3818 } else {
3820 }
3821 return true;
3822}
3823
3824static VehicleEnterTileStates VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
3825{
3826 if (v->type == VEH_TRAIN) {
3827 StationID station_id = GetStationIndex(tile);
3828 if (!v->current_order.ShouldStopAtStation(v, station_id)) return {};
3829 if (!IsRailStation(tile) || !v->IsFrontEngine()) return {};
3830
3831 int station_ahead;
3832 int station_length;
3833 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3834
3835 /* Stop whenever that amount of station ahead + the distance from the
3836 * begin of the platform to the stop location is longer than the length
3837 * of the platform. Station ahead 'includes' the current tile where the
3838 * vehicle is on, so we need to subtract that. */
3839 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return {};
3840
3842
3843 x &= 0xF;
3844 y &= 0xF;
3845
3846 if (DiagDirToAxis(dir) != AXIS_X) std::swap(x, y);
3847 if (y == TILE_SIZE / 2) {
3848 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
3849 stop &= TILE_SIZE - 1;
3850
3851 if (x == stop) {
3852 return VehicleEnterTileState::EnteredStation; // enter station
3853 } else if (x < stop) {
3855 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3856 if (spd < v->cur_speed) v->cur_speed = spd;
3857 }
3858 }
3859 } else if (v->type == VEH_ROAD) {
3861 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
3862 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3863 /* Attempt to allocate a parking bay in a road stop */
3864 if (RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv)) return {};
3866 }
3867 }
3868 }
3869
3870 return {};
3871}
3872
3878{
3879 /* Collect cargoes accepted since the last big tick. */
3880 CargoTypes cargoes = 0;
3881 for (CargoType cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
3882 if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) SetBit(cargoes, cargo_type);
3883 }
3884
3885 /* Anything to do? */
3886 if (cargoes == 0) return;
3887
3888 /* Loop over all houses in the catchment. */
3890 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3891 if (IsTileType(tile, MP_HOUSE)) {
3893 }
3894 }
3895}
3896
3904{
3905 if (!st->IsInUse()) {
3906 if (++st->delete_ctr >= 8) delete st;
3907 return false;
3908 }
3909
3910 if (Station::IsExpected(st)) {
3912
3913 for (GoodsEntry &ge : Station::From(st)->goods) {
3914 ge.status.Reset(GoodsEntry::State::AcceptedBigtick);
3915 }
3916 }
3917
3918
3920
3921 return true;
3922}
3923
3924static inline void byte_inc_sat(uint8_t *p)
3925{
3926 uint8_t b = *p + 1;
3927 if (b != 0) *p = b;
3928}
3929
3936static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3937{
3938 /* If truncating also punish the source stations' ratings to
3939 * decrease the flow of incoming cargo. */
3940
3941 if (!ge->HasData()) return;
3942
3943 StationCargoAmountMap waiting_per_source;
3944 ge->GetData().cargo.Truncate(amount, &waiting_per_source);
3945 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3946 Station *source_station = Station::GetIfValid(i->first);
3947 if (source_station == nullptr) continue;
3948
3949 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3950 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3951 }
3952}
3953
3959{
3960 bool waiting_changed = false;
3961
3962 byte_inc_sat(&st->time_since_load);
3963 byte_inc_sat(&st->time_since_unload);
3964
3965 for (const CargoSpec *cs : CargoSpec::Iterate()) {
3966 GoodsEntry *ge = &st->goods[cs->Index()];
3967
3968 /* The station might not currently be moving this cargo. */
3969 if (!ge->HasRating()) {
3970 /* Slowly increase the rating back to its original level in the case we
3971 * didn't deliver cargo yet to this station. This happens when a bribe
3972 * failed while you didn't moved that cargo yet to a station. */
3973 if (ge->rating < INITIAL_STATION_RATING) ge->rating++;
3974
3975 /* Nothing else to do with this cargo. */
3976 continue;
3977 }
3978
3979 byte_inc_sat(&ge->time_since_pickup);
3980
3981 /* If this cargo hasn't been picked up in a long time, get rid of it. */
3984 ge->last_speed = 0;
3985 TruncateCargo(cs, ge);
3986 waiting_changed = true;
3987 continue;
3988 }
3989
3990 bool skip = false;
3991 int rating = 0;
3992 uint waiting = ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0;
3993
3994 /* num_dests is at least 1 if there is any cargo as
3995 * StationID::Invalid() is also a destination.
3996 */
3997 uint num_dests = ge->HasData() ? static_cast<uint>(ge->GetData().cargo.Packets()->MapSize()) : 0;
3998
3999 /* Average amount of cargo per next hop, but prefer solitary stations
4000 * with only one or two next hops. They are allowed to have more
4001 * cargo waiting per next hop.
4002 * With manual cargo distribution waiting_avg = waiting / 2 as then
4003 * StationID::Invalid() is the only destination.
4004 */
4005 uint waiting_avg = waiting / (num_dests + 1);
4006
4008 ge->rating = rating = MAX_STATION_RATING;
4009 skip = true;
4010 } else if (cs->callback_mask.Test(CargoCallbackMask::StationRatingCalc)) {
4011 /* Perform custom station rating. If it succeeds the speed, days in transit and
4012 * waiting cargo ratings must not be executed. */
4013
4014 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
4015 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
4016
4017 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
4018 | (ClampTo<uint16_t>(ge->max_waiting_cargo) << 8)
4019 | (ClampTo<uint8_t>(last_speed) << 24);
4020 /* Convert to the 'old' vehicle types */
4021 uint32_t var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
4022 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
4023 if (callback != CALLBACK_FAILED) {
4024 skip = true;
4025 rating = GB(callback, 0, 14);
4026
4027 /* Simulate a 15 bit signed value */
4028 if (HasBit(callback, 14)) rating -= 0x4000;
4029 }
4030 }
4031
4032 if (!skip) {
4033 int b = ge->last_speed - 85;
4034 if (b >= 0) rating += b >> 2;
4035
4036 uint8_t waittime = ge->time_since_pickup;
4037 if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
4038 if (waittime <= 21) rating += 25;
4039 if (waittime <= 12) rating += 25;
4040 if (waittime <= 6) rating += 45;
4041 if (waittime <= 3) rating += 35;
4042
4043 rating -= 90;
4044 if (ge->max_waiting_cargo <= 1500) rating += 55;
4045 if (ge->max_waiting_cargo <= 1000) rating += 35;
4046 if (ge->max_waiting_cargo <= 600) rating += 10;
4047 if (ge->max_waiting_cargo <= 300) rating += 20;
4048 if (ge->max_waiting_cargo <= 100) rating += 10;
4049 }
4050
4051 if (Company::IsValidID(st->owner) && st->town->statues.Test(st->owner)) rating += 26;
4052
4053 uint8_t age = ge->last_age;
4054 if (age < 3) rating += 10;
4055 if (age < 2) rating += 10;
4056 if (age < 1) rating += 13;
4057
4058 {
4059 int or_ = ge->rating; // old rating
4060
4061 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
4062 ge->rating = rating = ClampTo<uint8_t>(or_ + Clamp(rating - or_, -2, 2));
4063
4064 /* if rating is <= 64 and more than 100 items waiting on average per destination,
4065 * remove some random amount of goods from the station */
4066 if (rating <= 64 && waiting_avg >= 100) {
4067 int dec = Random() & 0x1F;
4068 if (waiting_avg < 200) dec &= 7;
4069 waiting -= (dec + 1) * num_dests;
4070 waiting_changed = true;
4071 }
4072
4073 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
4074 if (rating <= 127 && waiting != 0) {
4075 uint32_t r = Random();
4076 if (rating <= (int)GB(r, 0, 7)) {
4077 /* Need to have int, otherwise it will just overflow etc. */
4078 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) + 1) * num_dests), 0);
4079 waiting_changed = true;
4080 }
4081 }
4082
4083 /* At some point we really must cap the cargo. Previously this
4084 * was a strict 4095, but now we'll have a less strict, but
4085 * increasingly aggressive truncation of the amount of cargo. */
4086 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
4087 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
4088 static const uint MAX_WAITING_CARGO = 1 << 15;
4089
4090 if (waiting > WAITING_CARGO_THRESHOLD) {
4091 uint difference = waiting - WAITING_CARGO_THRESHOLD;
4092 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
4093
4094 waiting = std::min(waiting, MAX_WAITING_CARGO);
4095 waiting_changed = true;
4096 }
4097
4098 /* We can't truncate cargo that's already reserved for loading.
4099 * Thus StoredCount() here. */
4100 if (waiting_changed && waiting < (ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0)) {
4101 /* Feed back the exact own waiting cargo at this station for the
4102 * next rating calculation. */
4103 ge->max_waiting_cargo = 0;
4104
4105 TruncateCargo(cs, ge, ge->GetData().cargo.AvailableCount() - waiting);
4106 } else {
4107 /* If the average number per next hop is low, be more forgiving. */
4108 ge->max_waiting_cargo = waiting_avg;
4109 }
4110 }
4111 }
4112
4113 StationID index = st->index;
4114 if (waiting_changed) {
4115 SetWindowDirty(WC_STATION_VIEW, index); // update whole window
4116 } else {
4117 SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
4118 }
4119}
4120
4129void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
4130{
4131 GoodsEntry &ge = st->goods[cargo];
4132
4133 /* Reroute cargo in station. */
4134 if (ge.HasData()) ge.GetData().cargo.Reroute(UINT_MAX, &ge.GetData().cargo, avoid, avoid2, &ge);
4135
4136 /* Reroute cargo staged to be transferred. */
4137 for (Vehicle *v : st->loading_vehicles) {
4138 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4139 if (u->cargo_type != cargo) continue;
4140 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4141 }
4142 }
4143}
4144
4154{
4155 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
4156 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL);
4157 GoodsEntry &ge = from->goods[cargo];
4159 if (lg == nullptr) continue;
4160 std::vector<NodeID> to_remove{};
4161 for (Edge &edge : (*lg)[ge.node].edges) {
4162 Station *to = Station::Get((*lg)[edge.dest_node].station);
4163 assert(to->goods[cargo].node == edge.dest_node);
4164 assert(TimerGameEconomy::date >= edge.LastUpdate());
4166 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4167 bool updated = false;
4168
4169 if (auto_distributed) {
4170 /* Have all vehicles refresh their next hops before deciding to
4171 * remove the node. */
4172 std::vector<Vehicle *> vehicles;
4173 for (const OrderList *l : OrderList::Iterate()) {
4174 bool found_from = false;
4175 bool found_to = false;
4176 for (const Order &order : l->GetOrders()) {
4177 if (!order.IsType(OT_GOTO_STATION) && !order.IsType(OT_IMPLICIT)) continue;
4178 if (order.GetDestination() == from->index) {
4179 found_from = true;
4180 if (found_to) break;
4181 } else if (order.GetDestination() == to->index) {
4182 found_to = true;
4183 if (found_from) break;
4184 }
4185 }
4186 if (!found_to || !found_from) continue;
4187 vehicles.push_back(l->GetFirstSharedVehicle());
4188 }
4189
4190 auto iter = vehicles.begin();
4191 while (iter != vehicles.end()) {
4192 Vehicle *v = *iter;
4193 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4195 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4196 }
4197 if (edge.LastUpdate() == TimerGameEconomy::date) {
4198 updated = true;
4199 break;
4200 }
4201
4202 Vehicle *next_shared = v->NextShared();
4203 if (next_shared) {
4204 *iter = next_shared;
4205 ++iter;
4206 } else {
4207 iter = vehicles.erase(iter);
4208 }
4209
4210 if (iter == vehicles.end()) iter = vehicles.begin();
4211 }
4212 }
4213
4214 if (!updated) {
4215 /* If it's still considered dead remove it. */
4216 to_remove.emplace_back(to->goods[cargo].node);
4217 if (ge.HasData()) ge.GetData().flows.DeleteFlows(to->index);
4218 RerouteCargo(from, cargo, to->index, from->index);
4219 }
4220 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4221 edge.Restrict();
4222 if (ge.HasData()) ge.GetData().flows.RestrictFlows(to->index);
4223 RerouteCargo(from, cargo, to->index, from->index);
4224 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4225 edge.Release();
4226 }
4227 }
4228 /* Remove dead edges. */
4229 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4230
4231 assert(TimerGameEconomy::date >= lg->LastCompression());
4233 lg->Compress();
4234 }
4235 }
4236}
4237
4247void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
4248{
4249 GoodsEntry &ge1 = st->goods[cargo];
4250 Station *st2 = Station::Get(next_station_id);
4251 GoodsEntry &ge2 = st2->goods[cargo];
4252 LinkGraph *lg = nullptr;
4253 if (ge1.link_graph == LinkGraphID::Invalid()) {
4254 if (ge2.link_graph == LinkGraphID::Invalid()) {
4256 lg = new LinkGraph(cargo);
4258 ge2.link_graph = lg->index;
4259 ge2.node = lg->AddNode(st2);
4260 } else {
4261 Debug(misc, 0, "Can't allocate link graph");
4262 }
4263 } else {
4264 lg = LinkGraph::Get(ge2.link_graph);
4265 }
4266 if (lg) {
4267 ge1.link_graph = lg->index;
4268 ge1.node = lg->AddNode(st);
4269 }
4270 } else if (ge2.link_graph == LinkGraphID::Invalid()) {
4271 lg = LinkGraph::Get(ge1.link_graph);
4272 ge2.link_graph = lg->index;
4273 ge2.node = lg->AddNode(st2);
4274 } else {
4275 lg = LinkGraph::Get(ge1.link_graph);
4276 if (ge1.link_graph != ge2.link_graph) {
4278 if (lg->Size() < lg2->Size()) {
4280 lg2->Merge(lg); // Updates GoodsEntries of lg
4281 lg = lg2;
4282 } else {
4284 lg->Merge(lg2); // Updates GoodsEntries of lg2
4285 }
4286 }
4287 }
4288 if (lg != nullptr) {
4289 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, modes);
4290 }
4291}
4292
4299void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4300{
4301 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4302 if (v->refit_cap > 0) {
4303 /* The cargo count can indeed be higher than the refit_cap if
4304 * wagons have been auto-replaced and subsequently auto-
4305 * refitted to a higher capacity. The cargo gets redistributed
4306 * among the wagons in that case.
4307 * As usage is not such an important figure anyway we just
4308 * ignore the additional cargo then.*/
4309 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4310 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EdgeUpdateMode::Increase);
4311 }
4312 }
4313}
4314
4315/* called for every station each tick */
4316static void StationHandleSmallTick(BaseStation *st)
4317{
4318 if (st->facilities.Test(StationFacility::Waypoint) || !st->IsInUse()) return;
4319
4320 uint8_t b = st->delete_ctr + 1;
4321 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4322 st->delete_ctr = b;
4323
4324 if (b == 0) UpdateStationRating(Station::From(st));
4325}
4326
4327void OnTick_Station()
4328{
4329 if (_game_mode == GM_EDITOR) return;
4330
4331 for (BaseStation *st : BaseStation::Iterate()) {
4332 StationHandleSmallTick(st);
4333
4334 /* Clean up the link graph about once a week. */
4337 };
4338
4339 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4341 /* Stop processing this station if it was deleted */
4342 if (!StationHandleBigTick(st)) continue;
4343 }
4344
4345 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4347 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4348 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4349 if (Station::IsExpected(st)) TriggerAirportAnimation(Station::From(st), AirportAnimationTrigger::AcceptanceTick);
4350 }
4351 }
4352}
4353
4355static const IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto)
4356{
4357 for (Station *st : Station::Iterate()) {
4358 for (GoodsEntry &ge : st->goods) {
4359 ge.status.Set(GoodsEntry::State::LastMonth, ge.status.Test(GoodsEntry::State::CurrentMonth));
4360 ge.status.Reset(GoodsEntry::State::CurrentMonth);
4361 }
4362 }
4363});
4364
4373void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4374{
4375 ForAllStationsRadius(tile, radius, [&](Station *st) {
4376 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4377 for (GoodsEntry &ge : st->goods) {
4378 if (ge.status.Any()) {
4379 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4380 }
4381 }
4382 }
4383 });
4384}
4385
4386static uint UpdateStationWaiting(Station *st, CargoType cargo, uint amount, Source source)
4387{
4388 /* We can't allocate a CargoPacket? Then don't do anything
4389 * at all; i.e. just discard the incoming cargo. */
4390 if (!CargoPacket::CanAllocateItem()) return 0;
4391
4392 GoodsEntry &ge = st->goods[cargo];
4393 amount += ge.amount_fract;
4394 ge.amount_fract = GB(amount, 0, 8);
4395
4396 amount >>= 8;
4397 /* No new "real" cargo item yet. */
4398 if (amount == 0) return 0;
4399
4400 StationID next = ge.GetVia(st->index);
4401 ge.GetOrCreateData().cargo.Append(new CargoPacket(st->index, amount, source), next);
4402 LinkGraph *lg = nullptr;
4403 if (ge.link_graph == LinkGraphID::Invalid()) {
4405 lg = new LinkGraph(cargo);
4407 ge.link_graph = lg->index;
4408 ge.node = lg->AddNode(st);
4409 } else {
4410 Debug(misc, 0, "Can't allocate link graph");
4411 }
4412 } else {
4413 lg = LinkGraph::Get(ge.link_graph);
4414 }
4415 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4416
4417 if (!ge.HasRating()) {
4420 }
4421
4423 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4424 TriggerAirportAnimation(st, AirportAnimationTrigger::NewCargo, cargo);
4425 TriggerRoadStopRandomisation(st, st->xy, StationRandomTrigger::NewCargo, cargo);
4426 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4427
4428
4430 st->MarkTilesDirty(true);
4431 return amount;
4432}
4433
4434static bool IsUniqueStationName(const std::string &name)
4435{
4436 for (const Station *st : Station::Iterate()) {
4437 if (!st->name.empty() && st->name == name) return false;
4438 }
4439
4440 return true;
4441}
4442
4450CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
4451{
4452 Station *st = Station::GetIfValid(station_id);
4453 if (st == nullptr) return CMD_ERROR;
4454
4455 CommandCost ret = CheckOwnership(st->owner);
4456 if (ret.Failed()) return ret;
4457
4458 bool reset = text.empty();
4459
4460 if (!reset) {
4462 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4463 }
4464
4465 if (flags.Test(DoCommandFlag::Execute)) {
4466 st->cached_name.clear();
4467 if (reset) {
4468 st->name.clear();
4469 } else {
4470 st->name = text;
4471 }
4472
4473 st->UpdateVirtCoord();
4475 }
4476
4477 return CommandCost();
4478}
4479
4487std::tuple<CommandCost, StationID> CmdMoveStationName(DoCommandFlags flags, StationID station_id, TileIndex tile)
4488{
4489 Station *st = Station::GetIfValid(station_id);
4490 if (st == nullptr) return { CMD_ERROR, StationID::Invalid() };
4491
4492 if (st->owner != OWNER_NONE) {
4493 CommandCost ret = CheckOwnership(st->owner);
4494 if (ret.Failed()) return { ret, StationID::Invalid() };
4495 }
4496
4497 const StationRect *r = &st->rect;
4498 if (!r->PtInExtendedRect(TileX(tile), TileY(tile))) {
4499 return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() };
4500 }
4501
4502 bool other_station = false;
4503 /* Check if the tile is the base tile of another station */
4504 ForAllStationsRadius(tile, 0, [&](BaseStation *s) {
4505 if (s != nullptr) {
4506 if (s != st && s->xy == tile) other_station = true;
4507 }
4508 });
4509 if (other_station) return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() };
4510
4511 if (flags.Test(DoCommandFlag::Execute)) {
4512 st->MoveSign(tile);
4513
4514 st->UpdateVirtCoord();
4515 }
4516 return { CommandCost(), station_id };
4517}
4518
4524void CcMoveStationName(Commands, const CommandCost &result, StationID station_id)
4525 {
4526 if (result.Failed()) return;
4527
4529 Station *st = Station::Get(station_id);
4530 SetViewportStationRect(st, false);
4531 }
4532
4533static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4534{
4535 for (Station *st : nearby) {
4536 if (st->TileIsInCatchment(tile)) stations.insert(st);
4537 }
4538}
4539
4545{
4546 if (this->tile != INVALID_TILE) {
4547 if (IsTileType(this->tile, MP_HOUSE)) {
4548 /* Town nearby stations need to be filtered per tile. */
4549 assert(this->w == 1 && this->h == 1);
4550 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4551 } else {
4552 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4553 this->stations.insert(st);
4554 return true;
4555 });
4556 }
4557 this->tile = INVALID_TILE;
4558 }
4559 return this->stations;
4560}
4561
4562
4563static bool CanMoveGoodsToStation(const Station *st, CargoType cargo)
4564{
4565 /* Is the station reserved exclusively for somebody else? */
4566 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4567
4568 /* Lowest possible rating, better not to give cargo anymore. */
4569 if (st->goods[cargo].rating == 0) return false;
4570
4571 /* Selectively servicing stations, and not this one. */
4572 if (_settings_game.order.selectgoods && !st->goods[cargo].HasVehicleEverTriedLoading()) return false;
4573
4575 /* Passengers are never served by just a truck stop. */
4576 if (st->facilities == StationFacility::TruckStop) return false;
4577 } else {
4578 /* Non-passengers are never served by just a bus stop. */
4579 if (st->facilities == StationFacility::BusStop) return false;
4580 }
4581 return true;
4582}
4583
4584uint MoveGoodsToStation(CargoType cargo, uint amount, Source source, const StationList &all_stations, Owner exclusivity)
4585{
4586 /* Return if nothing to do. Also the rounding below fails for 0. */
4587 if (all_stations.empty()) return 0;
4588 if (amount == 0) return 0;
4589
4590 Station *first_station = nullptr;
4591 typedef std::pair<Station *, uint> StationInfo;
4592 std::vector<StationInfo> used_stations;
4593
4594 for (Station *st : all_stations) {
4595 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4596 if (!CanMoveGoodsToStation(st, cargo)) continue;
4597
4598 /* Avoid allocating a vector if there is only one station to significantly
4599 * improve performance in this common case. */
4600 if (first_station == nullptr) {
4601 first_station = st;
4602 continue;
4603 }
4604 if (used_stations.empty()) {
4605 used_stations.reserve(2);
4606 used_stations.emplace_back(first_station, 0);
4607 }
4608 used_stations.emplace_back(st, 0);
4609 }
4610
4611 /* no stations around at all? */
4612 if (first_station == nullptr) return 0;
4613
4614 if (used_stations.empty()) {
4615 /* only one station around */
4616 amount *= first_station->goods[cargo].rating + 1;
4617 return UpdateStationWaiting(first_station, cargo, amount, source);
4618 }
4619
4620 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_best = {}; // best rating for each company, including OWNER_NONE
4621 TypedIndexContainer<std::array<uint32_t, OWNER_END.base()>, Owner> company_sum = {}; // sum of ratings for each company
4622 uint best_rating = 0;
4623 uint best_sum = 0; // sum of best ratings for each company
4624
4625 for (auto &p : used_stations) {
4626 auto owner = p.first->owner;
4627 auto rating = p.first->goods[cargo].rating;
4628 if (rating > company_best[owner]) {
4629 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4630 company_best[owner] = rating;
4631 if (rating > best_rating) best_rating = rating;
4632 }
4633 company_sum[owner] += rating;
4634 }
4635
4636 /* From now we'll calculate with fractional cargo amounts.
4637 * First determine how much cargo we really have. */
4638 amount *= best_rating + 1;
4639
4640 uint moving = 0;
4641 for (auto &p : used_stations) {
4642 Owner owner = p.first->owner;
4643 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4644 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4645 p.second = amount * company_best[owner] * p.first->goods[cargo].rating / best_sum / company_sum[owner];
4646 moving += p.second;
4647 }
4648
4649 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4650 if (amount > moving) {
4651 std::stable_sort(used_stations.begin(), used_stations.end(), [cargo](const StationInfo &a, const StationInfo &b) {
4652 return b.first->goods[cargo].rating < a.first->goods[cargo].rating;
4653 });
4654
4655 assert(amount - moving <= used_stations.size());
4656 for (uint i = 0; i < amount - moving; i++) {
4657 used_stations[i].second++;
4658 }
4659 }
4660
4661 uint moved = 0;
4662 for (auto &p : used_stations) {
4663 moved += UpdateStationWaiting(p.first, cargo, p.second, source);
4664 }
4665
4666 return moved;
4667}
4668
4669void UpdateStationDockingTiles(Station *st)
4670{
4671 st->docking_station.Clear();
4672
4673 /* For neutral stations, start with the industry area instead of dock area */
4674 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4675
4676 if (area->tile == INVALID_TILE) return;
4677
4678 int x = TileX(area->tile);
4679 int y = TileY(area->tile);
4680
4681 /* Expand the area by a tile on each side while
4682 * making sure that we remain inside the map. */
4683 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4684 int x1 = std::max<int>(x - 1, 0);
4685
4686 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4687 int y1 = std::max<int>(y - 1, 0);
4688
4689 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4690 for (TileIndex tile : ta) {
4691 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4692 }
4693}
4694
4695void BuildOilRig(TileIndex tile)
4696{
4697 if (!Station::CanAllocateItem()) {
4698 Debug(misc, 0, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4699 return;
4700 }
4701
4702 Station *st = new Station(tile);
4703 _station_kdtree.Insert(st->index);
4704 st->town = ClosestTownFromTile(tile, UINT_MAX);
4705
4706 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
4707
4708 assert(IsTileType(tile, MP_INDUSTRY));
4709 /* Mark industry as associated both ways */
4710 st->industry = Industry::GetByTile(tile);
4711 st->industry->neutral_station = st;
4712 DeleteAnimatedTile(tile);
4713 MakeOilrig(tile, st->index, GetWaterClass(tile));
4714
4715 st->owner = OWNER_NONE;
4716 st->airport.type = AT_OILRIG;
4717 st->airport.rotation = DIR_N;
4718 st->airport.Add(tile);
4719 st->ship_station.Add(tile);
4722 UpdateStationDockingTiles(st);
4723
4724 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
4725
4726 st->UpdateVirtCoord();
4727
4728 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4729 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4730 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4732 StationList nearby = std::move(st->industry->stations_near);
4733 st->industry->stations_near.clear();
4734 for (Station *near : nearby) {
4735 near->RecomputeCatchment(true);
4736 UpdateStationAcceptance(near, true);
4737 }
4738 }
4739
4740 st->RecomputeCatchment();
4741 UpdateStationAcceptance(st, false);
4742}
4743
4744void DeleteOilRig(TileIndex tile)
4745{
4746 Station *st = Station::GetByTile(tile);
4747
4748 MakeWaterKeepingClass(tile, OWNER_NONE);
4749
4750 /* The oil rig station is not supposed to be shared with anything else */
4751 [[maybe_unused]] static constexpr StationFacilities expected_facility{StationFacility::Airport, StationFacility::Dock};
4752 assert(st->facilities == expected_facility && st->airport.type == AT_OILRIG);
4753 if (st->industry != nullptr && st->industry->neutral_station == st) {
4754 /* Don't leave dangling neutral station pointer */
4755 st->industry->neutral_station = nullptr;
4756 }
4757 delete st;
4758}
4759
4760static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4761{
4762
4763 if (IsAnyRoadStopTile(tile)) {
4764 for (RoadTramType rtt : _roadtramtypes) {
4765 /* Update all roadtypes, no matter if they are present */
4766 if (GetRoadOwner(tile, rtt) == old_owner) {
4767 RoadType rt = GetRoadType(tile, rtt);
4768 if (rt != INVALID_ROADTYPE) {
4769 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4770 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4771 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4772 }
4773 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4774 }
4775 }
4776 }
4777
4778 if (!IsTileOwner(tile, old_owner)) return;
4779
4780 if (new_owner != INVALID_OWNER) {
4781 /* Update company infrastructure counts. Only do it here
4782 * if the new owner is valid as otherwise the clear
4783 * command will do it for us. No need to dirty windows
4784 * here, we'll redraw the whole screen anyway.*/
4785 Company *old_company = Company::Get(old_owner);
4786 Company *new_company = Company::Get(new_owner);
4787
4788 /* Update counts for underlying infrastructure. */
4789 switch (GetStationType(tile)) {
4790 case StationType::Rail:
4791 case StationType::RailWaypoint:
4792 if (!IsStationTileBlocked(tile)) {
4793 old_company->infrastructure.rail[GetRailType(tile)]--;
4794 new_company->infrastructure.rail[GetRailType(tile)]++;
4795 }
4796 break;
4797
4798 case StationType::Bus:
4799 case StationType::Truck:
4800 case StationType::RoadWaypoint:
4801 /* Road stops were already handled above. */
4802 break;
4803
4804 case StationType::Buoy:
4805 case StationType::Dock:
4806 if (GetWaterClass(tile) == WaterClass::Canal) {
4807 old_company->infrastructure.water--;
4808 new_company->infrastructure.water++;
4809 }
4810 break;
4811
4812 default:
4813 break;
4814 }
4815
4816 /* Update station tile count. */
4817 if (!IsBuoy(tile) && !IsAirport(tile)) {
4818 old_company->infrastructure.station--;
4819 new_company->infrastructure.station++;
4820 }
4821
4822 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4823 SetTileOwner(tile, new_owner);
4825 } else {
4826 if (IsDriveThroughStopTile(tile)) {
4827 /* Remove the drive-through road stop */
4828 if (IsRoadWaypoint(tile)) {
4830 } else {
4832 }
4833 assert(IsTileType(tile, MP_ROAD));
4834 /* Change owner of tile and all roadtypes */
4835 ChangeTileOwner(tile, old_owner, new_owner);
4836 } else {
4838 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4839 * Update owner of buoy if it was not removed (was in orders).
4840 * Do not update when owned by OWNER_WATER (sea and rivers). */
4841 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4842 }
4843 }
4844}
4845
4855{
4856 /* Water flooding can always clear road stops. */
4857 if (_current_company == OWNER_WATER) return CommandCost();
4858
4859 CommandCost ret;
4860
4861 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4862 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
4863 if (tram_owner != OWNER_NONE) {
4864 ret = CheckOwnership(tram_owner);
4865 if (ret.Failed()) return ret;
4866 }
4867 }
4868
4869 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4870 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
4871 if (road_owner == OWNER_TOWN) {
4872 ret = CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags);
4873 if (ret.Failed()) return ret;
4874 } else if (road_owner != OWNER_NONE) {
4875 ret = CheckOwnership(road_owner);
4876 if (ret.Failed()) return ret;
4877 }
4878 }
4879
4880 return CommandCost();
4881}
4882
4890{
4891 if (flags.Test(DoCommandFlag::Auto)) {
4892 switch (GetStationType(tile)) {
4893 default: break;
4894 case StationType::Rail: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4895 case StationType::RailWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4896 case StationType::Airport: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4897 case StationType::Truck: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4898 case StationType::Bus: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4899 case StationType::RoadWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4900 case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4901 case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4902 case StationType::Oilrig:
4903 return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG);
4904 }
4905 }
4906
4907 switch (GetStationType(tile)) {
4908 case StationType::Rail: return RemoveRailStation(tile, flags);
4909 case StationType::RailWaypoint: return RemoveRailWaypoint(tile, flags);
4910 case StationType::Airport: return RemoveAirport(tile, flags);
4911 case StationType::Truck: [[fallthrough]];
4912 case StationType::Bus:
4913 if (IsDriveThroughStopTile(tile)) {
4914 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4915 if (remove_road.Failed()) return remove_road;
4916 }
4917 return RemoveRoadStop(tile, flags);
4918 case StationType::RoadWaypoint: {
4919 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4920 if (remove_road.Failed()) return remove_road;
4921 return RemoveRoadWaypointStop(tile, flags);
4922 }
4923 case StationType::Buoy: return RemoveBuoy(tile, flags);
4924 case StationType::Dock: return RemoveDock(tile, flags);
4925 default: break;
4926 }
4927
4928 return CMD_ERROR;
4929}
4930
4931static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
4932{
4934 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4935 * TTDP does not call it.
4936 */
4937 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4938 switch (GetStationType(tile)) {
4939 case StationType::RailWaypoint:
4940 case StationType::Rail: {
4941 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4942 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4943 }
4944
4945 case StationType::Airport:
4946 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4947
4948 case StationType::Truck:
4949 case StationType::Bus:
4950 case StationType::RoadWaypoint: {
4951 if (IsDriveThroughStopTile(tile)) {
4952 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4953 } else {
4954 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4955 }
4956 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4957 }
4958
4959 default: break;
4960 }
4961 }
4962 }
4963 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
4964}
4965
4972{
4973 uint32_t prev = 0;
4974 for (const auto &it : this->shares) {
4975 if (it.second == st) {
4976 return it.first - prev;
4977 } else {
4978 prev = it.first;
4979 }
4980 }
4981 return 0;
4982}
4983
4991{
4992 if (this->unrestricted == 0) return StationID::Invalid();
4993 assert(!this->shares.empty());
4994 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
4995 assert(it != this->shares.end() && it->first <= this->unrestricted);
4996 if (it->second != excluded && it->second != excluded2) return it->second;
4997
4998 /* We've hit one of the excluded stations.
4999 * Draw another share, from outside its range. */
5000
5001 uint end = it->first;
5002 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
5003 uint interval = end - begin;
5004 if (interval >= this->unrestricted) return StationID::Invalid(); // Only one station in the map.
5005 uint new_max = this->unrestricted - interval;
5006 uint rand = RandomRange(new_max);
5007 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
5008 this->shares.upper_bound(rand + interval);
5009 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
5010 if (it2->second != excluded && it2->second != excluded2) return it2->second;
5011
5012 /* We've hit the second excluded station.
5013 * Same as before, only a bit more complicated. */
5014
5015 uint end2 = it2->first;
5016 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
5017 uint interval2 = end2 - begin2;
5018 if (interval2 >= new_max) return StationID::Invalid(); // Only the two excluded stations in the map.
5019 new_max -= interval2;
5020 if (begin > begin2) {
5021 std::swap(begin, begin2);
5022 std::swap(end, end2);
5023 std::swap(interval, interval2);
5024 }
5025 rand = RandomRange(new_max);
5026 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
5027 if (rand < begin) {
5028 it3 = this->shares.upper_bound(rand);
5029 } else if (rand < begin2 - interval) {
5030 it3 = this->shares.upper_bound(rand + interval);
5031 } else {
5032 it3 = this->shares.upper_bound(rand + interval + interval2);
5033 }
5034 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
5035 return it3->second;
5036}
5037
5044{
5045 assert(!this->shares.empty());
5046 SharesMap new_shares;
5047 uint i = 0;
5048 for (const auto &it : this->shares) {
5049 new_shares[++i] = it.second;
5050 if (it.first == this->unrestricted) this->unrestricted = i;
5051 }
5052 this->shares.swap(new_shares);
5053 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
5054}
5055
5063{
5064 /* We assert only before changing as afterwards the shares can actually
5065 * be empty. In that case the whole flow stat must be deleted then. */
5066 assert(!this->shares.empty());
5067
5068 uint removed_shares = 0;
5069 uint added_shares = 0;
5070 uint last_share = 0;
5071 SharesMap new_shares;
5072 for (const auto &it : this->shares) {
5073 if (it.second == st) {
5074 if (flow < 0) {
5075 uint share = it.first - last_share;
5076 if (flow == INT_MIN || (uint)(-flow) >= share) {
5077 removed_shares += share;
5078 if (it.first <= this->unrestricted) this->unrestricted -= share;
5079 if (flow != INT_MIN) flow += share;
5080 last_share = it.first;
5081 continue; // remove the whole share
5082 }
5083 removed_shares += (uint)(-flow);
5084 } else {
5085 added_shares += (uint)(flow);
5086 }
5087 if (it.first <= this->unrestricted) this->unrestricted += flow;
5088
5089 /* If we don't continue above the whole flow has been added or
5090 * removed. */
5091 flow = 0;
5092 }
5093 new_shares[it.first + added_shares - removed_shares] = it.second;
5094 last_share = it.first;
5095 }
5096 if (flow > 0) {
5097 new_shares[last_share + (uint)flow] = st;
5098 if (this->unrestricted < last_share) {
5099 this->ReleaseShare(st);
5100 } else {
5101 this->unrestricted += flow;
5102 }
5103 }
5104 this->shares.swap(new_shares);
5105}
5106
5113{
5114 assert(!this->shares.empty());
5115 uint flow = 0;
5116 uint last_share = 0;
5117 SharesMap new_shares;
5118 for (auto &it : this->shares) {
5119 if (flow == 0) {
5120 if (it.first > this->unrestricted) return; // Not present or already restricted.
5121 if (it.second == st) {
5122 flow = it.first - last_share;
5123 this->unrestricted -= flow;
5124 } else {
5125 new_shares[it.first] = it.second;
5126 }
5127 } else {
5128 new_shares[it.first - flow] = it.second;
5129 }
5130 last_share = it.first;
5131 }
5132 if (flow == 0) return;
5133 new_shares[last_share + flow] = st;
5134 this->shares.swap(new_shares);
5135 assert(!this->shares.empty());
5136}
5137
5144{
5145 assert(!this->shares.empty());
5146 uint flow = 0;
5147 uint next_share = 0;
5148 bool found = false;
5149 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
5150 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
5151 if (found) {
5152 flow = next_share - it->first;
5153 this->unrestricted += flow;
5154 break;
5155 } else {
5156 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
5157 if (it->second == st) found = true;
5158 }
5159 next_share = it->first;
5160 }
5161 if (flow == 0) return;
5162 SharesMap new_shares;
5163 new_shares[flow] = st;
5164 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
5165 if (it->second != st) {
5166 new_shares[flow + it->first] = it->second;
5167 } else {
5168 flow = 0;
5169 }
5170 }
5171 this->shares.swap(new_shares);
5172 assert(!this->shares.empty());
5173}
5174
5181{
5182 assert(runtime > 0);
5183 SharesMap new_shares;
5184 uint share = 0;
5185 for (auto i : this->shares) {
5186 share = std::max(share + 1, i.first * 30 / runtime);
5187 new_shares[share] = i.second;
5188 if (this->unrestricted == i.first) this->unrestricted = share;
5189 }
5190 this->shares.swap(new_shares);
5191}
5192
5199void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5200{
5201 FlowStatMap::iterator origin_it = this->find(origin);
5202 if (origin_it == this->end()) {
5203 this->emplace(origin, FlowStat(via, flow));
5204 } else {
5205 origin_it->second.ChangeShare(via, flow);
5206 assert(!origin_it->second.GetShares()->empty());
5207 }
5208}
5209
5218void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5219{
5220 FlowStatMap::iterator prev_it = this->find(origin);
5221 if (prev_it == this->end()) {
5222 FlowStat fs(via, flow);
5223 fs.AppendShare(StationID::Invalid(), flow);
5224 this->emplace(origin, fs);
5225 } else {
5226 prev_it->second.ChangeShare(via, flow);
5227 prev_it->second.ChangeShare(StationID::Invalid(), flow);
5228 assert(!prev_it->second.GetShares()->empty());
5229 }
5230}
5231
5237{
5238 for (auto &i : *this) {
5239 FlowStat &fs = i.second;
5240 uint local = fs.GetShare(StationID::Invalid());
5241 if (local > INT_MAX) { // make sure it fits in an int
5242 fs.ChangeShare(self, -INT_MAX);
5243 fs.ChangeShare(StationID::Invalid(), -INT_MAX);
5244 local -= INT_MAX;
5245 }
5246 fs.ChangeShare(self, -(int)local);
5247 fs.ChangeShare(StationID::Invalid(), -(int)local);
5248
5249 /* If the local share is used up there must be a share for some
5250 * remote station. */
5251 assert(!fs.GetShares()->empty());
5252 }
5253}
5254
5261std::vector<StationID> FlowStatMap::DeleteFlows(StationID via)
5262{
5263 std::vector<StationID> ret;
5264 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5265 FlowStat &s_flows = f_it->second;
5266 s_flows.ChangeShare(via, INT_MIN);
5267 if (s_flows.GetShares()->empty()) {
5268 ret.push_back(f_it->first);
5269 this->erase(f_it++);
5270 } else {
5271 ++f_it;
5272 }
5273 }
5274 return ret;
5275}
5276
5282{
5283 for (auto &it : *this) {
5284 it.second.RestrictShare(via);
5285 }
5286}
5287
5293{
5294 for (auto &it : *this) {
5295 it.second.ReleaseShare(via);
5296 }
5297}
5298
5304{
5305 uint ret = 0;
5306 for (const auto &it : *this) {
5307 ret += (--(it.second.GetShares()->end()))->first;
5308 }
5309 return ret;
5310}
5311
5318{
5319 uint ret = 0;
5320 for (const auto &it : *this) {
5321 ret += it.second.GetShare(via);
5322 }
5323 return ret;
5324}
5325
5332{
5333 FlowStatMap::const_iterator i = this->find(from);
5334 if (i == this->end()) return 0;
5335 return (--(i->second.GetShares()->end()))->first;
5336}
5337
5345{
5346 FlowStatMap::const_iterator i = this->find(from);
5347 if (i == this->end()) return 0;
5348 return i->second.GetShare(via);
5349}
5350
5351static CommandCost CheckBuildAbove_Station(TileIndex tile, DoCommandFlags, Axis, int height)
5352{
5353 StationType type = GetStationType(tile);
5354 auto bridgeable_info = GetStationBridgeableTileInfo(type);
5355
5356 switch (type) {
5357 case StationType::Rail:
5358 case StationType::RailWaypoint:
5359 if (const StationSpec *spec = GetStationSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5360 break;
5361
5362 case StationType::Bus:
5363 case StationType::Truck:
5364 case StationType::RoadWaypoint:
5365 if (const RoadStopSpec *spec = GetRoadStopSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info;
5366 break;
5367
5368 default: break;
5369 }
5370
5371 return IsStationBridgeAboveOk(tile, bridgeable_info, type, GetStationGfx(tile), height);
5372}
5373
5374extern const TileTypeProcs _tile_type_station_procs = {
5375 DrawTile_Station, // draw_tile_proc
5376 GetSlopePixelZ_Station, // get_slope_z_proc
5377 ClearTile_Station, // clear_tile_proc
5378 nullptr, // add_accepted_cargo_proc
5379 GetTileDesc_Station, // get_tile_desc_proc
5380 GetTileTrackStatus_Station, // get_tile_track_status_proc
5381 ClickTile_Station, // click_tile_proc
5382 AnimateTile_Station, // animate_tile_proc
5383 TileLoop_Station, // tile_loop_proc
5384 ChangeTileOwner_Station, // change_tile_owner_proc
5385 nullptr, // add_produced_cargo_proc
5386 VehicleEnter_Station, // vehicle_enter_tile_proc
5387 GetFoundation_Station, // get_foundation_proc
5388 TerraformTile_Station, // terraform_tile_proc
5389 CheckBuildAbove_Station, // check_build_above_proc
5390};
Base for aircraft.
void UpdateAirplanesOnNewStation(const Station *st)
Updates the status of the Aircraft heading or in the station.
const AirportFTAClass * GetAirport(const uint8_t airport_type)
Get the finite state machine of an airport type.
Definition airport.cpp:186
static const uint INVALID_AIRPORTTILE
id for an invalid airport tile
Definition airport.h:25
static const uint NEW_AIRPORTTILE_OFFSET
offset of first newgrf airport tile
Definition airport.h:24
@ AirportClosed
Dummy block for indicating a closed airport.
@ FLYING
Vehicle is flying in the air.
Definition airport.h:77
@ NUM_AIRPORTS
Maximal number of airports in total.
Definition airport.h:41
@ AT_OILRIG
Oilrig airport.
Definition airport.h:38
Enum of the default airport tiles.
void AddAnimatedTile(TileIndex tile, bool mark_dirty)
Add the given tile to the animated tile table (if it does not exist yet).
void DeleteAnimatedTile(TileIndex tile, bool immediate)
Stops animation on the given tile.
Tile animation!
Functions related to autoslope.
bool AutoslopeCheckForAxis(TileIndex tile, int z_new, Slope tileh_new, Axis axis)
Autoslope check for tiles with something built along an axis.
Definition autoslope.h:51
bool AutoslopeCheckForEntranceEdge(TileIndex tile, int z_new, Slope tileh_new, DiagDirection entrance)
Autoslope check for tiles with an entrance on an edge.
Definition autoslope.h:31
bool AutoslopeEnabled()
Tests if autoslope is enabled for _current_company.
Definition autoslope.h:65
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr bool HasExactlyOneBit(T value)
Test whether value has exactly 1 bit set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
Draw the middle bits of a bridge.
TileIndex GetSouthernBridgeEnd(TileIndex t)
Finds the southern end of a bridge starting at a middle tile.
int GetBridgeHeight(TileIndex t)
Get the height ('z') of a bridge.
Map accessor functions for bridges.
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:23
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
@ Mail
Mail.
@ Liquid
Liquids (Oil, Water, Rubber)
@ Passengers
Passengers.
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
Cheats _cheats
All the cheats.
Definition cheat.cpp:16
Types related to cheating.
Iterator to iterate over all tiles belonging to an airport.
Iterator to iterate over all tiles belonging to an airport spec.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Iterator to iterate over all tiles belonging to a bitmaptilearea.
const Tcont * Packets() const
Returns a pointer to the cargo packet list (so you can iterate over it etc).
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
Enum-as-bit-set wrapper.
Flat set implementation that uses a sorted vector for storage.
std::pair< const_iterator, bool > insert(const Tkey &key)
Insert a key into the set, if it does not already exist.
uint GetFlow() const
Get the sum of all flows from this FlowStatMap.
void PassOnFlow(StationID origin, StationID via, uint amount)
Pass on some flow, remembering it as invalid, for later subtraction from locally consumed flow.
std::vector< StationID > DeleteFlows(StationID via)
Delete all flows at a station for specific cargo and destination.
void AddFlow(StationID origin, StationID via, uint amount)
Add some flow from "origin", going via "via".
uint GetFlowFrom(StationID from) const
Get the sum of flows from a specific station from this FlowStatMap.
void FinalizeLocalConsumption(StationID self)
Subtract invalid flows from locally consumed flow.
void ReleaseFlows(StationID via)
Release all flows at a station for specific cargo and destination.
uint GetFlowFromVia(StationID from, StationID via) const
Get the flow from a specific station via a specific other station.
uint GetFlowVia(StationID via) const
Get the sum of flows via a specific station from this FlowStatMap.
void RestrictFlows(StationID via)
Restrict all flows at a station for specific cargo and destination.
Flow statistics telling how much flow should be sent along a link.
static const SharesMap empty_sharesmap
Static instance of FlowStat::SharesMap.
void ScaleToMonthly(uint runtime)
Scale all shares from link graph's runtime to monthly values.
void RestrictShare(StationID st)
Restrict a flow by moving it to the end of the map and decreasing the amount of unrestricted flow.
uint GetShare(StationID st) const
Get flow for a station.
StationID GetVia() const
Get a station a package can be routed to.
const SharesMap * GetShares() const
Get the actual shares as a const pointer so that they can be iterated over.
void ReleaseShare(StationID st)
Release ("unrestrict") a flow by moving it to the begin of the map and increasing the amount of unres...
void ChangeShare(StationID st, int flow)
Change share for specified station.
void AppendShare(StationID st, uint flow, bool restricted=false)
Add some flow to the end of the shares map.
void Invalidate()
Reduce all flows to minimum capacity so that they don't get in the way of link usage statistics too m...
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
void Insert(const T &element)
Insert a single element in the tree.
Definition kdtree.hpp:396
void Remove(const T &element)
Remove a single element from the tree, if it exists.
Definition kdtree.hpp:415
static LinkGraphSchedule instance
Static instance of LinkGraphSchedule.
void Queue(LinkGraph *lg)
Queue a link graph for execution.
void Dequeue(LinkGraph *lg)
Remove a link graph from the execution queue.
A connected component of a link graph.
Definition linkgraph.h:37
void Merge(LinkGraph *other)
Merge a link graph with another one.
Definition linkgraph.cpp:90
TimerGameEconomy::Date LastCompression() const
Get date of last compression.
Definition linkgraph.h:236
NodeID AddNode(const Station *st)
Add a node to the component and create empty edges associated with it.
NodeID Size() const
Get the current size of the component.
Definition linkgraph.h:230
static const uint MIN_TIMEOUT_DISTANCE
Minimum effective distance for timeout calculation.
Definition linkgraph.h:170
static constexpr TimerGameEconomy::Date STALE_LINK_DEPOT_TIMEOUT
Number of days before deleting links served only by vehicles stopped in depot.
Definition linkgraph.h:173
static constexpr TimerGameEconomy::Date COMPRESSION_INTERVAL
Minimum number of days between subsequent compressions of a LG.
Definition linkgraph.h:176
static void Run(Vehicle *v, bool allow_merge=true, bool is_full_loading=false)
Refresh all links the given vehicle will visit.
Definition refresh.cpp:26
size_t MapSize() const
Count the number of ranges with equal keys in this MultiMap.
Definition multimap.hpp:291
Struct containing information relating to NewGRF classes for stations and airports.
StringID name
Name of this class.
static NewGRFClass * Get(Tindex class_index)
Get a particular class.
uint GetSpecCount() const
Get the number of allocated specs within the class.
static uint GetClassCount()
Get the number of allocated classes.
const Tspec * GetSpec(uint index) const
Get a spec from the class at a given index.
uint position
Position within iterator.
const RailStationTileLayout & stl
Station tile layout being iterated.
uint length
Length of platforms.
uint platforms
Number of platforms.
std::span< const StationGfx > layout
Predefined tile layout.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:116
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:126
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:221
StringID name
Name of this rail type.
Definition rail.h:166
uint8_t fallback_railtype
Original railtype number to use when drawing non-newgrf railtypes, or when drawing stations.
Definition rail.h:191
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:287
struct RailTypeInfo::@22 strings
Strings associated with the rail type.
struct RailTypeInfo::@19 base_sprites
Struct containing the main sprites.
struct RoadTypeInfo::@25 strings
Strings associated with the rail type.
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:117
StringID name
Name of this rail type.
Definition road.h:78
Generate TileIndices around a center tile or tile area, with increasing distance.
Add dynamic register values to a sprite layout.
DrawTileSpriteSpan GetLayout() const
Returns the result spritelayout after preprocessing.
uint Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
Routes packets with station "avoid" as next hop to a different place.
uint AvailableCount() const
Returns sum of cargo still available for loading at the station.
void Append(CargoPacket *cp, StationID next)
Appends the given cargo packet to the range of packets with the same next station.
uint Truncate(uint max_move=UINT_MAX, StationCargoAmountMap *cargo_per_source=nullptr)
Truncates where each destination loses roughly the same percentage of its cargo.
const StationList & GetStations()
Run a tile loop to find stations around a tile, on demand.
static constexpr TimerGameTick::Ticks STATION_LINKGRAPH_TICKS
Cycle duration for cleaning dead links.
static constexpr TimerGameTick::Ticks STATION_ACCEPTANCE_TICKS
Cycle duration for updating station acceptance.
static constexpr TimerGameTick::Ticks STATION_RATING_TICKS
Cycle duration for updating station rating.
Base class for tile iterators.
Wrapper class to abstract away the way the tiles are stored.
Definition map_func.h:25
static Date date
Current date in days (day counter).
static constexpr TimerGame< struct Economy >::Date INVALID_DATE
Representation of an invalid date.
static Date date
Current date in days (day counter).
static TickCounter counter
Monotonic counter, in ticks, since start of game.
A sort-of mixin that implements 'at(pos)' and 'operator[](pos)' only for a specific type.
uint StoredCount() const
Returns sum of cargo on board the vehicle (ie not only reserved).
Iterate over all vehicles on a tile.
Functions related to clear (MP_CLEAR) land.
CommandCost CommandCostWithParam(StringID str, uint64_t value)
Return an error status, with string and parameter.
Definition command.cpp:417
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ 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:1032
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
uint8_t GetSnowLine()
Get the current snow line, either variable or static.
void MarkTilesDirty(bool cargo_change) const
Marks the tiles of the station as dirty.
Definition station.cpp:247
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
HouseZone
Definition house.h:55
Base of all industries.
bool IsTileForestIndustry(TileIndex tile)
Check whether the tile is a forest.
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
IndustryType GetIndustryType(Tile tile)
Retrieve the type for this industry.
IndustryID GetIndustryIndex(Tile t)
Get the industry ID of the given tile.
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like IT_...
@ Extractive
Like mines.
void ChangeTileOwner(TileIndex tile, Owner old_owner, Owner new_owner)
Change the owner of a tile.
void DrawFoundation(TileInfo *ti, Foundation f)
Draw foundation f at tile ti.
std::tuple< Slope, int > GetFoundationPixelSlope(TileIndex tile)
Get slope of a tile on top of a (possible) foundation If a tile does not have a foundation,...
Definition landscape.h:67
uint ApplyPixelFoundationToSlope(Foundation f, Slope &s)
Applies a foundation to a slope.
Definition landscape.h:128
Point RemapCoords2(int x, int y)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:97
Command definitions related to landscape (slopes etc.).
@ Random
Randomise borders.
Some typedefs for the main game.
@ Increase
Increase capacity.
@ DT_MANUAL
Manual distribution. No link graph calculations are run.
uint DistanceFromEdge(TileIndex tile)
Param the minimum distance to an edge.
Definition map.cpp:218
uint DistanceMax(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles.
Definition map.cpp:190
TileIndex TileAddWrap(TileIndex tile, int addx, int addy)
This function checks if we add addx/addy to tile, if we do wrap around the edges.
Definition map.cpp:109
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:158
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:385
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:452
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:401
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:567
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:437
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:427
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:582
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
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.
@ Invalid
GRF is unusable with this version of OpenTTD.
Functions/types related to NewGRF debugging.
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Delete inspect window for a given feature and index.
void TriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, CargoTypes trigger_cargoes)
Run watched cargo accepted callback for a house.
Functions related to NewGRF houses.
SpriteID GetCustomRailSprite(const RailTypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of rail types.
NewGRF definitions and structures for road stops.
@ Overlay
Drive-through stops: Draw the road overlay, e.g. pavement.
@ WaypGround
Waypoints: Draw the sprite layout ground tile (on top of the road)
@ Road
Bay stops: Draw the road itself.
bool IsWaypointClass(const RoadStopClass &cls)
Test if a RoadStopClass is the waypoint class.
RoadStopClassID
@ ROADSTOPTYPE_FREIGHT
This RoadStop is for freight (truck) stops.
@ ROADSTOPTYPE_ALL
This RoadStop is for both types of station road stops.
@ ROADSTOPTYPE_PASSENGER
This RoadStop is for passenger (bus) stops.
@ NoCatenary
Do not show catenary.
@ DriveThroughOnly
Stop is drive-through only.
@ DrawModeRegister
Read draw mode from register 0x100.
SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of road types.
uint32_t GetPlatformInfo(Axis axis, uint8_t tile, int platforms, int length, int x, int y, bool centred)
Evaluate a tile's position within a station, and return the result in a bit-stuffed format.
SpriteID GetCustomStationRelocation(const StationSpec *statspec, BaseStation *st, TileIndex tile, uint32_t var10)
Resolve sprites for drawing a station tile.
SpriteID GetCustomStationFoundationRelocation(const StationSpec *statspec, BaseStation *st, TileIndex tile, uint layout, uint edge_info)
Resolve the sprites for custom station foundations.
void AssignSpecToStation(const StationSpec *spec, BaseStation *st, uint8_t specindex)
Assign a previously allocated StationSpec specindex to a Station.
void DeallocateSpecFromStation(BaseStation *st, uint8_t specindex)
Deallocate a StationSpec from a Station.
void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger station randomisation.
CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, uint8_t plat_len, uint8_t numtracks)
Check the slope of a tile of a new station.
std::optional< uint8_t > AllocateSpecToStation(const StationSpec *spec, BaseStation *st)
Allocate a StationSpec to a Station.
Header file for NewGRF stations.
@ CustomFoundations
Draw custom foundations.
@ SeparateGround
Use different sprite set for ground sprites.
@ ExtendedFoundations
Extended foundation block instead of simple.
uint16_t GetStationLayoutKey(uint8_t platforms, uint8_t length)
Get the station layout key for a given station layout size.
StationClassID
Functions related to news.
void AddNewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1={}, NewsReference ref2={}, std::unique_ptr< NewsAllocatedData > &&data=nullptr, AdviceType advice_type=AdviceType::Invalid)
Add a new newsitem to be shown.
Definition news_gui.cpp:902
@ Acceptance
A type of cargo is (no longer) accepted.
@ Small
Small news item. (Information window with text and viewport)
@ InColour
News item is shown in colour (otherwise it is shown in black & white).
Functions related to order backups.
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:331
void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
Set the reservation for a complete station platform.
Definition pbs.cpp:57
PBS support routines.
bool ValParamRailType(const RailType rail)
Validate functions for rail building.
Definition rail.cpp:90
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:429
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition rail.h:377
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:301
@ RTSG_GROUND
Main group of ground images.
Definition rail.h:44
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:43
RailTrackOffset
Offsets for sprites within an overlay/underlay set.
Definition rail.h:62
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:64
@ RTO_X
Piece of rail in X direction.
Definition rail.h:63
Command definitions for rail.
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
@ 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
static debug_inline bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
Pseudo random number generator.
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
Definition of link refreshing utility.
bool ValParamRoadType(RoadType roadtype)
Validate functions for rail building.
Definition road.cpp:164
bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
Checks if an engine of the given RoadType got power on a tile with a given RoadType.
Definition road.h:231
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:216
@ ROTSG_OVERLAY
Optional: Images for overlaying track.
Definition road.h:39
@ ROTSG_ROADSTOP
Required: Bay stop surface.
Definition road.h:48
@ ROTSG_GROUND
Required: Main group of ground images.
Definition road.h:40
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:241
void DrawRoadGroundSprites(const TileInfo *ti, RoadBits road, RoadBits tram, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, Roadside roadside, bool snow_or_desert)
Draw road ground sprites.
void DrawRoadCatenary(const TileInfo *ti)
Draws the catenary for the given tile.
void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count)
Update road infrastructure counts for a company.
Definition road_cmd.cpp:180
CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadTramType rtt, DoCommandFlags flags, bool town_check)
Is it allowed to remove the given road bits from the given tile?
Definition road_cmd.cpp:252
void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, uint road_offset, uint tram_offset, bool draw_underlay)
Draw road underlay and overlay sprites.
Functions related to roads.
RoadBits AxisToRoadBits(Axis a)
Create the road-part which belongs to the given Axis.
Definition road_func.h:111
Functions used internally by the roads.
bool MayHaveRoad(Tile t)
Test whether a tile can have road/tram types.
Definition road_map.cpp:21
RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance)
Returns the RoadBits on an arbitrary tile Special behaviour:
Definition road_map.cpp:54
void SetRoadOwner(Tile t, RoadTramType rtt, Owner o)
Set the owner of a specific road type.
Definition road_map.h:235
static debug_inline bool IsNormalRoadTile(Tile t)
Return whether a tile is a normal road tile.
Definition road_map.h:58
RoadBits GetRoadBits(Tile t, RoadTramType rtt)
Get the present road bits for a specific road type.
Definition road_map.h:112
DisallowedRoadDirections GetDisallowedRoadDirections(Tile t)
Gets the disallowed directions.
Definition road_map.h:285
bool HasTileRoadType(Tile t, RoadTramType rtt)
Check if a tile has a road or a tram road type.
Definition road_map.h:195
void MakeRoadNormal(Tile t, RoadBits bits, RoadType road_rt, RoadType tram_rt, TownID town, Owner road, Owner tram)
Make a normal road tile.
Definition road_map.h:620
RoadBits GetAllRoadBits(Tile tile)
Get all set RoadBits on the given tile.
Definition road_map.h:125
Owner GetRoadOwner(Tile t, RoadTramType rtt)
Get the owner of a specific road type.
Definition road_map.h:218
Roadside
The possible road side decorations.
Definition road_map.h:457
@ 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:48
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:49
Foundation
Enumeration for Foundations.
Definition slope_type.h:93
@ FOUNDATION_LEVELED
The tile is leveled up to a flat slope.
Definition slope_type.h:95
void DrawRailTileSeqInGUI(int x, int y, const DrawTileSprites *dts, int32_t total_offset, uint32_t newgrf_offset, PaletteID default_palette)
Draw tile sprite sequence in GUI with railroad specifics.
Definition sprite.h:105
void DrawRailTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, int32_t total_offset, uint32_t newgrf_offset, PaletteID default_palette)
Draw tile sprite sequence on tile with railroad specifics.
Definition sprite.h:95
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:174
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1559
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1549
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
Definition sprites.h:1562
Base classes/functions for stations.
void ForAllStationsAroundTiles(const TileArea &ta, Func func)
Call a function on all stations that have any part of the requested area within their catchment.
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index=-1)
Remove a bus station/truck stop.
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.
static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCommandFlags flags)
Checks if an airport can be built at the given location and clear the area.
CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index=-1)
Remove a road waypoint.
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
Look for a station owned by the given company around the given tile area.
CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
Remove bus or truck stops.
bool HasStationInUse(StationID station, bool include_company, CompanyID company)
Tests whether the company's vehicles have this station in orders.
static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
Counts the numbers of tiles matching a specific type in the area around.
CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
Build a bus or truck stop.
static StationSpec::TileFlags GetStationTileFlags(StationGfx gfx, const StationSpec *statspec)
Get station tile flags for the given StationGfx.
static CommandCost IsDockBridgeAboveOk(TileIndex tile, StationGfx layout)
Test if a dock can be built below a bridge.
static bool DrawCustomStationFoundations(const StationSpec *statspec, BaseStation *st, TileInfo *ti, StationGfx gfx)
Draw custom station foundations for a NewGRF station if provided.
const DrawTileSprites * GetStationTileLayout(StationType st, uint8_t gfx)
Get station tile layout for a station type and its station gfx.
CommandCost CmdRemoveFromRoadWaypoint(DoCommandFlags flags, TileIndex start, TileIndex end)
Remove road waypoints.
void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
Reroute cargo of type c at station st or in any vehicles unloading there.
CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags)
Clear a single tile of a station.
static void UpdateStationRating(Station *st)
Periodic update of a station's rating.
void UpdateAllStationVirtCoords()
Update the virtual coords needed to draw the station sign for all stations.
static bool CMSAWater(TileIndex tile)
Check whether the tile is water.
CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
Place an Airport.
void TriggerWatchedCargoCallbacks(Station *st)
Run the watched cargo callback for all houses in the catchment area.
static CommandCost CanRemoveRoadWithStop(TileIndex tile, DoCommandFlags flags)
Check if a drive-through road stop tile can be cleared.
static RoadStop ** FindRoadStopSpot(bool truck_station, Station *st)
CargoTypes GetAcceptanceMask(const Station *st)
Get a mask of the cargo types that the station accepts.
static void RestoreTrainReservation(Train *v)
Restore platform reservation during station building/removing.
void UpdateAirportsNoise()
Recalculate the noise generated by the airports of each town.
static std::span< const BridgeableTileInfo > GetStationBridgeableTileInfo(StationType type)
Get bridgeable tile information for a station type.
static bool CMSATree(TileIndex tile)
Check whether the tile is a tree.
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
Forcibly modify station ratings near a given tile.
CommandCost RemoveRailStation(T *st, DoCommandFlags flags, Money removal_cost)
Remove a rail station/waypoint.
void UpdateStationAcceptance(Station *st, bool show_msg)
Update the acceptance for a station.
static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
Find a nearby station that joins this road stop.
CommandCost CmdOpenCloseAirport(DoCommandFlags flags, StationID station_id)
Open/close an airport to incoming aircraft.
static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_tile, int &allowed_z, DoCommandFlags flags, Axis axis, StationID *station, RailType rt, std::vector< Train * > &affected_vehicles, StationClassID spec_class, uint16_t spec_index, uint8_t plat_len, uint8_t numtracks)
Checks if a rail station can be built at the given tile.
static TileIndex FindDockLandPart(TileIndex t)
Find the part of a dock that is land-based.
static void FreeTrainReservation(Train *v)
Clear platform reservation during station building/removing.
void SetRailStationTileFlags(TileIndex tile, const StationSpec *statspec)
Set rail station tile flags for the given tile.
static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
Get the acceptance of cargoes around the station in.
static CommandCost RemoveDock(TileIndex tile, DoCommandFlags flags)
Remove a dock.
bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
Check whether a sprite is a track sprite, which can be replaced by a non-track ground sprite and a ra...
CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
Get the cargo types being produced around the tile (in a rectangle).
CommandCost CmdRemoveFromRailStation(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
Remove a single tile from a rail station.
static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
Find a nearby station that joins this station.
void DeleteStaleLinks(Station *from)
Check all next hops of cargo packets in this station for existence of a a valid link they may use to ...
static BridgePillarFlags GetStationBlockedPillars(std::span< const BridgeableTileInfo > bridgeable_info, uint8_t layout)
Get blocked pillar information for a station tile.
CommandCost CheckBuildableTile(TileIndex tile, DiagDirections invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge=true)
Checks if the given tile is buildable, flat and has a certain height.
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
Find a nearby station that joins this station.
Town * AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
Finds the town nearest to given airport.
static bool StationHandleBigTick(BaseStation *st)
This function is called for each station once every 250 ticks.
static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
Add news item for when a station changes which cargoes it accepts.
CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailType rt, Axis axis, uint8_t numtracks, uint8_t plat_len, StationClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
Build rail station.
static void DeleteStationIfEmpty(BaseStation *st)
This is called right after a station was deleted.
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
Calculates cost of new road stops within the area.
static CommandCost IsStationBridgeAboveOk(TileIndex tile, std::span< const BridgeableTileInfo > bridgeable_info, StationType type, StationGfx layout, int bridge_height, StringID disallowed_msg=INVALID_STRING_ID)
Test if a bridge can be built above a station.
Command definitions related to stations.
Functions related to stations.
void ShowStationViewWindow(StationID station)
Opens StationViewWindow for given station.
Declarations for accessing the k-d tree of stations.
void ForAllStationsRadius(TileIndex center, uint radius, Func func)
Call a function on all stations whose sign is within a radius of a center tile.
Sprites to use and how to display them for station tiles.
Functions related to station layouts.
void MakeAirport(Tile t, Owner o, StationID sid, uint8_t section, WaterClass wc)
Make the given tile an airport tile.
StationType GetStationType(Tile t)
Get the station type of this tile.
Definition station_map.h:44
StationGfx GetStationGfx(Tile t)
Get the station graphics of this tile.
Definition station_map.h:68
void SetStationGfx(Tile t, StationGfx gfx)
Set the station graphics of this tile.
Definition station_map.h:80
void MakeRoadStop(Tile t, Owner o, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, DiagDirection d)
Make the given tile a roadstop tile.
void SetCustomStationSpecIndex(Tile t, uint8_t specindex)
Set the custom station spec for this tile.
void SetStationTileHaveWires(Tile t, bool b)
Set the catenary wires state of the rail station.
bool IsAirport(Tile t)
Is this station tile an airport?
bool IsBayRoadStopTile(Tile t)
Is tile t a bay (non-drive through) road stop station?
uint GetCustomRoadStopSpecIndex(Tile t)
Get the custom road stop spec for this tile.
static const int GFX_DOCK_BASE_WATER_PART
The offset for the water parts.
Definition station_map.h:35
bool IsBuoy(Tile t)
Is tile t a buoy tile?
bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile)
Check if a tile is a valid continuation to a railstation tile.
bool IsRoadWaypoint(Tile t)
Is the station at t a road waypoint?
void MakeDriveThroughRoadStop(Tile t, Owner station, Owner road, Owner tram, StationID sid, StationType rst, RoadType road_rt, RoadType tram_rt, Axis a)
Make the given tile a drivethrough roadstop tile.
bool IsDriveThroughStopTile(Tile t)
Is tile t a drive through road stop station or waypoint?
static Roadside GetRoadWaypointRoadside(Tile tile)
Get the decorations of a road waypoint.
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
Track GetRailStationTrack(Tile t)
Get the rail track of a rail station tile.
static void ToggleRoadWaypointOnSnowOrDesert(Tile t)
Toggle the snow/desert state of a road waypoint tile.
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
bool HasStationTileRail(Tile t)
Has this station tile a rail? In other words, is this station tile a rail station or rail waypoint?
StationGfx GetAirportGfx(Tile t)
Get the station graphics of this airport tile.
uint GetCustomStationSpecIndex(Tile t)
Get the custom station spec for this tile.
bool IsRailWaypoint(Tile t)
Is this station tile a rail waypoint?
bool IsRailStation(Tile t)
Is this station tile a rail station?
Definition station_map.h:92
bool IsDockTile(Tile t)
Is tile t a dock tile?
static void SetRoadWaypointRoadside(Tile tile, Roadside s)
Set the decorations of a road waypoint.
void SetStationTileRandomBits(Tile t, uint8_t random_bits)
Set the random bits for a station tile.
bool IsAnyRoadStop(Tile t)
Is the station at t a road station?
void MakeOilrig(Tile t, StationID sid, WaterClass wc)
Make the given tile an oilrig tile.
DiagDirection GetDockDirection(Tile t)
Get the direction of a dock.
Axis GetRailStationAxis(Tile t)
Get the rail direction of a rail station.
static bool IsRoadWaypointOnSnowOrDesert(Tile t)
Check if a road waypoint tile has snow/desert.
bool IsRoadWaypointTile(Tile t)
Is this tile a station tile and a road waypoint?
bool IsStationTileBlocked(Tile t)
Is tile t a blocked tile?
bool IsTruckStop(Tile t)
Is the station at t a truck stop?
bool IsStationRoadStop(Tile t)
Is the station at t a road station?
bool IsCustomStationSpecIndex(Tile t)
Is there a custom rail station spec on this tile?
bool HasStationRail(Tile t)
Has this station tile a rail? In other words, is this station tile a rail station or rail waypoint?
static const int GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET
The offset for the drive through parts.
Definition station_map.h:36
Axis GetDriveThroughStopAxis(Tile t)
Gets the axis of the drive through stop.
void SetStationTileHavePylons(Tile t, bool b)
Set the catenary pylon state of the rail station.
bool IsOilRig(Tile t)
Is tile t part of an oilrig?
bool IsBuoyTile(Tile t)
Is tile t a buoy tile?
void MakeRailStation(Tile t, Owner o, StationID sid, Axis a, uint8_t section, RailType rt)
Make the given tile a rail station tile.
bool HasStationReservation(Tile t)
Get the reservation state of the rail station.
void MakeDock(Tile t, Owner o, StationID sid, DiagDirection d, WaterClass wc)
Make the given tile a dock tile.
DiagDirection GetBayRoadStopDir(Tile t)
Gets the direction the bay road stop entrance points towards.
bool IsDock(Tile t)
Is tile t a dock tile?
bool IsAnyRoadStopTile(Tile t)
Is tile t a road stop station?
void SetStationTileBlocked(Tile t, bool b)
Set the blocked state of the rail station.
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition station_map.h:56
void SetCustomRoadStopSpecIndex(Tile t, uint8_t specindex)
Set the custom road stop spec for this tile.
RoadStopType
Types of RoadStops.
@ Bus
A standard stop for buses.
@ Truck
A standard stop for trucks.
@ End
End of valid types.
@ Dock
Station with a dock.
@ Waypoint
Station is a waypoint.
@ TruckStop
Station with truck stops.
@ Train
Station with train station.
@ Airport
Station with an airport.
@ BusStop
Station with bus stops.
StationType
Station types.
@ NewCargo
Trigger station on new cargo arrival.
std::set< Station *, StationCompare > StationList
List of stations.
@ Built
Trigger tile when built.
@ TileLoop
Trigger in the periodic tile loop.
@ NewCargo
Trigger station on new cargo arrival.
@ AcceptanceTick
Trigger station every 250 ticks.
static const uint MAX_LENGTH_STATION_NAME_CHARS
The maximum length of a station name in characters including '\0'.
@ Built
Triggered when the airport is built (for all tiles at the same time).
@ TileLoop
Triggered in the periodic tile loop.
@ NewCargo
Triggered when new cargo arrives at the station (for all tiles at the same time).
@ AcceptanceTick
Triggered every 250 ticks (for all tiles at the same time).
Types related to the station widgets.
@ WID_SV_CLOSE_AIRPORT
'Close airport' button.
@ WID_SV_ROADVEHS
List of scheduled road vehs button.
@ WID_SV_ACCEPT_RATING_LIST
List of accepted cargoes / rating of cargoes.
@ WID_SV_SHIPS
List of scheduled ships button.
@ WID_SV_TRAINS
List of scheduled trains button.
Definition of base types and functions in a cross-platform compatible way.
size_t Utf8StringLength(std::string_view str)
Get the length of an UTF-8 encoded string in number of characters and thus not the number of bytes th...
Definition string.cpp:350
Functions related to low-level strings.
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:336
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
Functions related to OTTD's strings.
auto MakeParameters(Args &&... args)
Helper to create the StringParameters with its own buffer with the given parameter values.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:72
@ Airplanes
Can planes land on this airport type?
Defines the data structure for an airport.
StringID name
name of this airport
SubstituteGRFFileProps grf_prop
Properties related to the grf file.
std::vector< AirportTileLayout > layouts
List of layouts composing the airport.
bool IsWithinMapBounds(uint8_t table, TileIndex index) const
Check if the airport would be within the map bounds at the given tile.
uint8_t size_y
size of airport in y direction
uint8_t size_x
size of airport in x direction
static const AirportSpec * Get(uint8_t type)
Retrieve airport spec for the given airport.
std::span< const HangarTileTable > depots
Position of the depots on the airports.
bool IsAvailable() const
Check whether this airport is available to build.
uint8_t noise_level
noise that this airport generates
Defines the data structure of each individual tile of an airport.
static const AirportTileSpec * Get(StationGfx gfx)
Retrieve airport tile spec for the given airport tile.
SubstituteGRFFileProps grf_prop
properties related the the grf file
StringID name
Tile Subname string, land information on this tile will give you "AirportName (TileSubname)".
AnimationInfo< AirportAnimationTriggers > animation
Information about the animation.
static const AirportTileSpec * GetByTile(TileIndex tile)
Retrieve airport tile spec for the given airport tile.
AirportBlocks blocks
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
TileIndex GetRotatedTileFromOffset(TileIndexDiffC tidc) const
Add the tileoffset to the base tile of this airport but rotate it first.
uint GetHangarNum(TileIndex tile) const
Get the hangar number of the hangar at a specific tile.
Direction rotation
How this airport is rotated.
uint8_t type
Type of this airport,.
PersistentStorage * psa
Persistent storage for NewGRF airports.
uint GetNumHangars() const
Get the number of hangars on this airport.
TileIndex GetHangarTile(uint hangar_num) const
Get the first tile of the given hangar.
const AirportSpec * GetSpec() const
Get the AirportSpec that from the airport type of this airport.
uint8_t layout
Airport layout number.
Base class for all station-ish types.
StringID string_id
Default name (town area) of station.
TileIndex xy
Base tile of the station.
std::vector< SpecMapping< StationSpec > > speclist
List of rail station specs of this station.
StationFacilities facilities
The facilities that this station has.
std::string cached_name
NOSAVE: Cache of the resolved name of the station, if not using a custom name.
TileArea train_station
Tile area the train 'station' part covers.
StationAnimationTriggers cached_anim_triggers
NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
Owner owner
The owner of this station.
virtual void UpdateVirtCoord()=0
Update the coordinated of the sign (as shown in the viewport).
uint8_t delete_ctr
Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is ...
StationAnimationTriggers cached_roadstop_anim_triggers
NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing sh...
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
bool IsInUse() const
Check whether the base station currently is in use; in use means that it is not scheduled for deletio...
Town * town
The town this station is associated with.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
virtual bool TileBelongsToRailStation(TileIndex tile) const =0
Check whether a specific tile belongs to this station.
TrackedViewportSign sign
NOSAVE: Dimensions of sign.
TimerGameCalendar::Date build_date
Date of construction.
std::string name
Custom name.
VehicleType type
Type of vehicle.
Class for storing amounts of cargo.
Definition cargo_type.h:113
Container for cargo from the same location and time.
Definition cargopacket.h:41
Specification of a cargo type.
Definition cargotype.h:74
CargoClasses classes
Classes of this cargo type.
Definition cargotype.h:81
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:108
static IterateWrapper Iterate(size_t from=0)
Returns an iterable ensemble of all valid CargoSpec.
Definition cargotype.h:192
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat station_rating
Fix station ratings at 100%.
Definition cheat_type.h:35
GUISettings gui
settings related to the GUI
uint32_t station
Count of company owned station tiles.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
uint32_t water
Count of company owned track bits for canals.
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
bool build_on_slopes
allow building on slopes
bool road_stop_on_town_road
allow building of drive-through road stops on town owned roads
bool road_stop_on_competitor_road
allow building of drive-through road stops on roads owned by competitors
T y
Y coordinate.
T x
X coordinate.
T z
Z coordinate.
uint8_t town_council_tolerance
minimum required town ratings to be allowed to demolish stuff
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:67
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:52
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:53
bool station_noise_level
build new airports when the town noise level is still within accepted limits
Information about GRF, used in the game and (part of it) in savegames.
std::string GetName() const
Get the name of this grf.
const struct GRFFile * grffile
grf file that introduced this entity
uint32_t grfid
grfid that introduced this entity.
bool HasGrfFile() const
Test if this entity was introduced by NewGRF.
bool show_track_reservation
highlight reserved tracks.
LandscapeType landscape
the landscape we're currently in
EconomySettings economy
settings to change the economy
ConstructionSettings construction
construction of things in-game
DifficultySettings difficulty
settings related to the difficulty
GameCreationSettings game_creation
settings used during the creation of a game (map)
StationSettings station
settings related to station management
LinkGraphSettings linkgraph
settings for link graph calculations
OrderSettings order
settings related to orders
FlowStatMap flows
Planned flows through this station.
StationCargoList cargo
The cargo packets of cargo waiting in this station.
Stores station stats for a single cargo.
uint max_waiting_cargo
Max cargo from this station waiting at any station.
bool HasRating() const
Does this cargo have a rating at this station?
uint8_t last_speed
Maximum speed (up to 255) of the last vehicle that tried to load this cargo.
uint8_t last_age
Age in years (up to 255) of the last vehicle that tried to load this cargo.
uint8_t time_since_pickup
Number of rating-intervals (up to 255) since the last vehicle tried to load this cargo.
States status
Status of this cargo, see State.
debug_inline const GoodsEntryData & GetData() const
Get optional cargo packet/flow data.
NodeID node
ID of node in link graph referring to this goods entry.
@ LastMonth
Set when cargo was delivered for final delivery last month.
@ Acceptance
Set when the station accepts the cargo currently for final deliveries.
@ Rating
This indicates whether a cargo has a rating at the station.
@ AcceptedBigtick
Set when cargo was delivered for final delivery during the current STATION_ACCEPTANCE_TICKS interval.
@ CurrentMonth
Set when cargo was delivered for final delivery this month.
uint8_t amount_fract
Fractional part of the amount in the cargo list.
LinkGraphID link_graph
Link graph this station belongs to.
bool HasVehicleEverTriedLoading() const
Reports whether a vehicle has ever tried to load the cargo at this station.
uint8_t rating
Station rating for this cargo.
GoodsEntryData & GetOrCreateData()
Get optional cargo packet/flow data.
debug_inline bool HasData() const
Test if this goods entry has optional cargo packet/flow data.
StationID GetVia(StationID source) const
Get the best next hop for a cargo packet from station source.
Defines the data structure for constructing industry.
SubstituteGRFFileProps grf_prop
properties related to the grf file
StringID name
Displayed name of the industry.
StringID station_name
Default name for nearby station.
bool enabled
entity still available (by default true).newgrf can disable it, though
IndustryLifeTypes life_type
This is also known as Industry production flag, in newgrf specs.
Defines the internal data of a functional industry.
Definition industry.h:62
IndustryType type
type of industry.
Definition industry.h:115
ProducedCargoes produced
produced cargo slots
Definition industry.h:110
Owner owner
owner of the industry. Which SHOULD always be (imho) OWNER_NONE
Definition industry.h:116
static Industry * GetByTile(TileIndex tile)
Get the industry of the given tile.
Definition industry.h:251
TileArea location
Location of the industry.
Definition industry.h:106
Station * neutral_station
Associated neutral station.
Definition industry.h:108
StationList stations_near
NOSAVE: List of nearby stations.
Definition industry.h:123
An edge in the link graph.
Definition linkgraph.h:42
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:281
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:272
static debug_inline uint Size()
Get the size of the map.
Definition map_func.h:290
Tindex class_index
Class index of this spec, invalid until class is allocated.
NewGRF supplied spritelayout.
static void Reset(TileIndex tile=INVALID_TILE, bool from_gui=true)
Reset the OrderBackups from GUI/game logic.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:264
bool selectgoods
only send the goods to station if a train has been there
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:66
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
Represents the covered area of e.g.
uint16_t w
The width of the area.
void Add(TileIndex to_add)
Add a single tile to a tile area; enlarge if needed.
Definition tilearea.cpp:43
void Clear()
Clears the 'tile area', i.e.
TileIndex tile
The base tile of the area.
uint16_t h
The height of the area.
OrthogonalTileArea & Expand(int rad)
Expand a tile area by rad tiles in each direction, keeping within map bounds.
Definition tilearea.cpp:123
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed)
Definition gfx_type.h:24
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
static size_t GetNumItems()
Returns number of valid items in the pool.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(auto index)
Returns Titem with given index.
Road stop specification.
Money GetBuildCost(Price category) const
Get the cost for building a road stop of this type.
std::array< BridgeableTileInfo, 6 > bridgeable_info
Per tile layout bridge information.
CargoGRFFileProps grf_prop
Link to NewGRF.
Money GetClearCost(Price category) const
Get the cost for clearing a road stop of this type.
A Stop for a Road Vehicle.
RoadStop * next
Next stop of the given type at this station.
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp: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 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:174
CompanyID exclusivity
which company has exclusivity
Definition town.h:84
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:85
void UpdatePosition(int center, int top, std::string_view str, std::string_view str_small={})
Update the position of the viewport sign.
bool kdtree_valid
Are the sign data valid for use with the _viewport_sign_kdtree?
'Train' is either a loco or a wagon.
Definition train.h:91
Trackdir GetVehicleTrackdir() const override
Get the tracks of the train vehicle.
Vehicle data structure.
Direction direction
facing
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
VehicleCargoList cargo
The cargo this vehicle is carrying.
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
VehStates vehstatus
Status.
CargoType cargo_type
type of cargo this vehicle is carrying
debug_inline bool IsFrontEngine() const
Check if the vehicle is a front engine.
virtual TileIndex GetOrderStationLocation(StationID station)
Determine the location for the station where the vehicle goes to next.
Order current_order
The current order (+ status, like: loading)
Vehicle * Next() const
Get the next vehicle of this vehicle.
uint16_t refit_cap
Capacity left over from before last refit.
Vehicle * NextShared() const
Get the next vehicle of the shared vehicle chain.
uint16_t cur_speed
current speed
TileIndex tile
Current tile index.
TileIndex dest_tile
Heading for this tile.
Owner owner
Which company owns the vehicle?
Representation of a waypoint.
TileArea road_waypoint_area
Tile area the road waypoint part covers.
uint16_t waypoint_flags
Waypoint flags, see WaypointFlags.
void UpdateVirtCoord() override
Update the virtual coords needed to draw the waypoint sign.
@ EnteredStation
The vehicle entered a station.
@ CannotEnter
The vehicle cannot enter the tile.
bool IsTileFlat(TileIndex tile, int *h)
Check if a given tile is flat.
Definition tile_map.cpp:95
std::tuple< Slope, int > GetTileSlopeZ(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.cpp:55
int GetTileMaxZ(TileIndex t)
Get top height of the tile inside the map.
Definition tile_map.cpp:136
int GetTileZ(TileIndex tile)
Get bottom height of the tile.
Definition tile_map.cpp:116
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
void SetTileOwner(Tile tile, Owner owner)
Sets the owner of a tile.
Definition tile_map.h:198
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
uint8_t GetAnimationFrame(Tile t)
Get the current animation frame.
Definition tile_map.h:250
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
TropicZone GetTropicZone(Tile tile)
Get the tropic zone.
Definition tile_map.h:238
void SetAnimationFrame(Tile t, uint8_t frame)
Set a new animation frame.
Definition tile_map.h:262
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:78
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:87
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static constexpr uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
@ MP_TREES
Tile got trees.
Definition tile_type.h:52
@ MP_ROAD
A tile with road (or tram tracks)
Definition tile_type.h:50
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
@ MP_HOUSE
A house by a town.
Definition tile_type.h:51
@ MP_WATER
Water tile.
Definition tile_type.h:54
@ MP_INDUSTRY
Part of an industry.
Definition tile_type.h:56
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
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:527
@ TrainSlowing
Train is slowing down.
Functions related to vehicles.
@ VEH_INVALID
Non-existing type of vehicle.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
Functions and type for generating vehicle lists.
void FindVehiclesWithOrder(VehiclePredicate veh_pred, OrderPredicate ord_pred, VehicleFunc veh_func)
Find vehicles matching an order.
void OffsetGroundSprite(int x, int y)
Called when a foundation has been drawn for the current tile.
Definition viewport.cpp:591
void 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.
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:1193
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3202
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:3294
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:3189
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3176
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:3311
@ 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.