OpenTTD Source 20250529-master-g10c159a79f
station_cmd.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "core/flatset_type.hpp"
12#include "aircraft.h"
13#include "bridge_map.h"
14#include "vehiclelist_func.h"
15#include "viewport_func.h"
16#include "viewport_kdtree.h"
17#include "command_func.h"
18#include "town.h"
19#include "news_func.h"
20#include "train.h"
21#include "ship.h"
22#include "roadveh.h"
23#include "industry.h"
24#include "newgrf_cargo.h"
25#include "newgrf_debug.h"
26#include "newgrf_station.h"
27#include "newgrf_canal.h" /* For the buoy */
29#include "road_internal.h" /* For drawing catenary/checking road removal */
30#include "autoslope.h"
31#include "water.h"
32#include "strings_func.h"
33#include "clear_func.h"
35#include "vehicle_func.h"
36#include "string_func.h"
37#include "animated_tile_func.h"
38#include "elrail_func.h"
39#include "station_base.h"
40#include "station_func.h"
41#include "station_kdtree.h"
42#include "roadstop_base.h"
43#include "newgrf_railtype.h"
44#include "newgrf_roadtype.h"
45#include "waypoint_base.h"
46#include "waypoint_func.h"
47#include "pbs.h"
48#include "debug.h"
49#include "core/random_func.hpp"
51#include "company_base.h"
53#include "newgrf_airporttiles.h"
54#include "order_backup.h"
55#include "newgrf_house.h"
56#include "company_gui.h"
58#include "linkgraph/refresh.h"
59#include "tunnelbridge_map.h"
60#include "station_cmd.h"
61#include "waypoint_cmd.h"
62#include "landscape_cmd.h"
63#include "rail_cmd.h"
64#include "newgrf_roadstop.h"
65#include "timer/timer.h"
69#include "cheat_type.h"
70#include "road_func.h"
71
73
74#include "table/strings.h"
75
76#include <bitset>
77
78#include "safeguards.h"
79
85/* static */ const FlowStat::SharesMap FlowStat::empty_sharesmap;
86
94{
95 assert(IsTileType(t, MP_STATION));
96
97 /* If the tile isn't an airport there's no chance it's a hangar. */
98 if (!IsAirport(t)) return false;
99
100 const Station *st = Station::GetByTile(t);
101 const AirportSpec *as = st->airport.GetSpec();
102
103 for (const auto &depot : as->depots) {
104 if (st->airport.GetRotatedTileFromOffset(depot.ti) == TileIndex(t)) return true;
105 }
106
107 return false;
108}
109
119template <class T, class F>
120CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
121{
122 ta.Expand(1);
123
124 /* check around to see if there are any stations there owned by the company */
125 for (TileIndex tile_cur : ta) {
126 if (IsTileType(tile_cur, MP_STATION)) {
127 StationID t = GetStationIndex(tile_cur);
128 if (!T::IsValidID(t) || T::Get(t)->owner != company || !filter(T::Get(t))) continue;
129 if (closest_station == StationID::Invalid()) {
130 closest_station = t;
131 } else if (closest_station != t) {
132 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
133 }
134 }
135 }
136 *st = (closest_station == StationID::Invalid()) ? nullptr : T::Get(closest_station);
137 return CommandCost();
138}
139
145typedef bool (*CMSAMatcher)(TileIndex tile);
146
154{
155 int num = 0;
156
157 for (int dx = -3; dx <= 3; dx++) {
158 for (int dy = -3; dy <= 3; dy++) {
159 TileIndex t = TileAddWrap(tile, dx, dy);
160 if (t != INVALID_TILE && cmp(t)) num++;
161 }
162 }
163
164 return num;
165}
166
172static bool CMSAMine(TileIndex tile)
173{
174 /* No industry */
175 if (!IsTileType(tile, MP_INDUSTRY)) return false;
176
177 const Industry *ind = Industry::GetByTile(tile);
178
179 /* No extractive industry */
181
182 for (const auto &p : ind->produced) {
183 /* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine.
184 * Also the production of passengers and mail is ignored. */
185 if (IsValidCargoType(p.cargo) &&
187 return true;
188 }
189 }
190
191 return false;
192}
193
199static bool CMSAWater(TileIndex tile)
200{
201 return IsTileType(tile, MP_WATER) && IsWater(tile);
202}
203
209static bool CMSATree(TileIndex tile)
210{
211 return IsTileType(tile, MP_TREES);
212}
213
214enum StationNaming : uint8_t {
215 STATIONNAMING_RAIL,
216 STATIONNAMING_ROAD,
217 STATIONNAMING_AIRPORT,
218 STATIONNAMING_OILRIG,
219 STATIONNAMING_DOCK,
220 STATIONNAMING_HELIPORT,
221};
222
225 std::bitset<STR_SV_STNAME_FALLBACK - STR_SV_STNAME> used_names;
226 std::bitset<NUM_INDUSTRYTYPES> indtypes;
227
228 bool IsAvailable(StringID str) const
229 {
230 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
231 return !this->used_names.test(str - STR_SV_STNAME);
232 }
233
234 void SetUsed(StringID str)
235 {
236 assert(IsInsideMM(str, STR_SV_STNAME, STR_SV_STNAME_FALLBACK));
237 this->used_names.set(str - STR_SV_STNAME);
238 }
239};
240
241static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
242{
243 const Town *t = st->town;
244
246
247 for (const Station *s : Station::Iterate()) {
248 if (s != st && s->town == t) {
249 if (s->indtype != IT_INVALID) {
250 sni.indtypes[s->indtype] = true;
251 StringID name = GetIndustrySpec(s->indtype)->station_name;
252 if (name != STR_UNDEFINED) {
253 /* Filter for other industrytypes with the same name */
254 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
255 const IndustrySpec *indsp = GetIndustrySpec(it);
256 if (indsp->enabled && indsp->station_name == name) sni.indtypes[it] = true;
257 }
258 }
259 continue;
260 }
261 if (IsInsideMM(s->string_id, STR_SV_STNAME, STR_SV_STNAME_FALLBACK)) {
262 auto str = s->string_id;
263 if (str == STR_SV_STNAME_FOREST) str = STR_SV_STNAME_WOODS;
264 sni.SetUsed(str);
265 }
266 }
267 }
268
269 for (auto indtile : SpiralTileSequence(tile, 7)) {
270 if (!IsTileType(indtile, MP_INDUSTRY)) continue;
271
272 /* If the station name is undefined it means that it doesn't name a station */
273 IndustryType indtype = GetIndustryType(indtile);
274 const IndustrySpec *indsp = GetIndustrySpec(indtype);
275 if (indsp->station_name == STR_UNDEFINED) continue;
276
277 /* In all cases if an industry that provides a name is found two of
278 * the standard names will be disabled. */
279 sni.SetUsed(STR_SV_STNAME_OILFIELD);
280 sni.SetUsed(STR_SV_STNAME_MINES);
281 if (sni.indtypes[indtype]) continue;
282
283 /* STR_NULL means it only disables oil rig/mines */
284 if (indsp->station_name != STR_NULL) {
285 st->indtype = indtype;
286 return STR_SV_STNAME_FALLBACK;
287 }
288 break;
289 }
290
291 /* check default names
292 * Oil rigs/mines name could be marked not free by looking for a near by industry. */
293 switch (name_class) {
294 case STATIONNAMING_AIRPORT:
295 if (sni.IsAvailable(STR_SV_STNAME_AIRPORT)) return STR_SV_STNAME_AIRPORT;
296 break;
297 case STATIONNAMING_OILRIG:
298 if (sni.IsAvailable(STR_SV_STNAME_OILFIELD)) return STR_SV_STNAME_OILFIELD;
299 break;
300 case STATIONNAMING_DOCK:
301 if (sni.IsAvailable(STR_SV_STNAME_DOCKS)) return STR_SV_STNAME_DOCKS;
302 break;
303 case STATIONNAMING_HELIPORT:
304 if (sni.IsAvailable(STR_SV_STNAME_HELIPORT)) return STR_SV_STNAME_HELIPORT;
305 break;
306 default:
307 break;
308 };
309
310 /* check mine? */
311 if (sni.IsAvailable(STR_SV_STNAME_MINES)) {
312 if (CountMapSquareAround(tile, CMSAMine) >= 2) {
313 return STR_SV_STNAME_MINES;
314 }
315 }
316
317 /* check close enough to town to get central as name? */
318 if (DistanceMax(tile, t->xy) < 8) {
319 if (sni.IsAvailable(STR_SV_STNAME)) return STR_SV_STNAME;
320 if (sni.IsAvailable(STR_SV_STNAME_CENTRAL)) return STR_SV_STNAME_CENTRAL;
321 }
322
323 /* Check lakeside */
324 if (sni.IsAvailable(STR_SV_STNAME_LAKESIDE) &&
325 DistanceFromEdge(tile) < 20 &&
326 CountMapSquareAround(tile, CMSAWater) >= 5) {
327 return STR_SV_STNAME_LAKESIDE;
328 }
329
330 /* Check woods */
331 if (sni.IsAvailable(STR_SV_STNAME_WOODS) && (
332 CountMapSquareAround(tile, CMSATree) >= 8 ||
334 ) {
335 return _settings_game.game_creation.landscape == LandscapeType::Tropic ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
336 }
337
338 /* check elevation compared to town */
339 int z = GetTileZ(tile);
340 int z2 = GetTileZ(t->xy);
341 if (z < z2) {
342 if (sni.IsAvailable(STR_SV_STNAME_VALLEY)) return STR_SV_STNAME_VALLEY;
343 } else if (z > z2) {
344 if (sni.IsAvailable(STR_SV_STNAME_HEIGHTS)) return STR_SV_STNAME_HEIGHTS;
345 }
346
347 /* check direction compared to town */
348 if (TileX(tile) < TileX(t->xy)) {
349 sni.SetUsed(STR_SV_STNAME_SOUTH);
350 sni.SetUsed(STR_SV_STNAME_WEST);
351 } else {
352 sni.SetUsed(STR_SV_STNAME_NORTH);
353 sni.SetUsed(STR_SV_STNAME_EAST);
354 }
355 if (TileY(tile) < TileY(t->xy)) {
356 sni.SetUsed(STR_SV_STNAME_SOUTH);
357 sni.SetUsed(STR_SV_STNAME_EAST);
358 } else {
359 sni.SetUsed(STR_SV_STNAME_NORTH);
360 sni.SetUsed(STR_SV_STNAME_WEST);
361 }
362
364 static const StringID fallback_names[] = {
365 STR_SV_STNAME_NORTH,
366 STR_SV_STNAME_SOUTH,
367 STR_SV_STNAME_EAST,
368 STR_SV_STNAME_WEST,
369 STR_SV_STNAME_TRANSFER,
370 STR_SV_STNAME_HALT,
371 STR_SV_STNAME_EXCHANGE,
372 STR_SV_STNAME_ANNEXE,
373 STR_SV_STNAME_SIDINGS,
374 STR_SV_STNAME_BRANCH,
375 STR_SV_STNAME_UPPER,
376 STR_SV_STNAME_LOWER,
377 };
378 for (auto str : fallback_names) {
379 if (sni.IsAvailable(str)) return str;
380 }
381 return STR_SV_STNAME_FALLBACK;
382}
383
390{
391 uint threshold = 8;
392
393 Station *best_station = nullptr;
394 ForAllStationsRadius(tile, threshold, [&](Station *st) {
395 if (!st->IsInUse() && st->owner == _current_company) {
396 uint cur_dist = DistanceManhattan(tile, st->xy);
397
398 if (cur_dist < threshold) {
399 threshold = cur_dist;
400 best_station = st;
401 } else if (cur_dist == threshold && best_station != nullptr) {
402 /* In case of a tie, lowest station ID wins */
403 if (st->index < best_station->index) best_station = st;
404 }
405 }
406 });
407
408 return best_station;
409}
410
411
413{
414 switch (type) {
415 case StationType::Rail:
416 *ta = this->train_station;
417 return;
418
419 case StationType::Airport:
420 *ta = this->airport;
421 return;
422
423 case StationType::Truck:
424 *ta = this->truck_station;
425 return;
426
427 case StationType::Bus:
428 *ta = this->bus_station;
429 return;
430
431 case StationType::Dock:
432 case StationType::Oilrig:
433 *ta = this->docking_station;
434 return;
435
436 default: NOT_REACHED();
437 }
438}
439
444{
445 Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
446
447 pt.y -= 32 * ZOOM_BASE;
448 if (this->facilities.Test(StationFacility::Airport) && this->airport.type == AT_OILRIG) pt.y -= 16 * ZOOM_BASE;
449
450 if (this->sign.kdtree_valid) _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeStation(this->index));
451
452 this->sign.UpdatePosition(pt.x, pt.y, GetString(STR_VIEWPORT_STATION, this->index, this->facilities), GetString(STR_STATION_NAME, this->index, this->facilities));
453
454 _viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeStation(this->index));
455
457}
458
464{
465 if (this->xy == new_xy) return;
466
467 _station_kdtree.Remove(this->index);
468
469 this->BaseStation::MoveSign(new_xy);
470
471 _station_kdtree.Insert(this->index);
472}
473
476{
477 for (BaseStation *st : BaseStation::Iterate()) {
478 st->UpdateVirtCoord();
479 }
480}
481
482void BaseStation::FillCachedName() const
483{
484 auto tmp_params = MakeParameters(this->index);
485 this->cached_name = GetStringWithArgs(Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params);
486}
487
488void ClearAllStationCachedNames()
489{
490 for (BaseStation *st : BaseStation::Iterate()) {
491 st->cached_name.clear();
492 }
493}
494
500CargoTypes GetAcceptanceMask(const Station *st)
501{
502 CargoTypes mask = 0;
503
504 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
505 if (it->status.Test(GoodsEntry::State::Acceptance)) SetBit(mask, std::distance(std::begin(st->goods), it));
506 }
507 return mask;
508}
509
515CargoTypes GetEmptyMask(const Station *st)
516{
517 CargoTypes mask = 0;
518
519 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
520 if (!it->HasData() || it->GetData().cargo.TotalCount() == 0) SetBit(mask, std::distance(std::begin(st->goods), it));
521 }
522 return mask;
523}
524
531static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
532{
533 StringID msg = reject ? STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_LIST : STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST;
535}
536
544CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
545{
546 CargoArray produced{};
547 FlatSet<IndustryID> industries;
548 TileArea ta = TileArea(north_tile, w, h).Expand(rad);
549
550 /* Loop over all tiles to get the produced cargo of
551 * everything except industries */
552 for (TileIndex tile : ta) {
553 if (IsTileType(tile, MP_INDUSTRY)) industries.insert(GetIndustryIndex(tile));
554 AddProducedCargo(tile, produced);
555 }
556
557 /* Loop over the seen industries. They produce cargo for
558 * anything that is within 'rad' of any one of their tiles.
559 */
560 for (IndustryID industry : industries) {
561 const Industry *i = Industry::Get(industry);
562 /* Skip industry with neutral station */
563 if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) continue;
564
565 for (const auto &p : i->produced) {
566 if (IsValidCargoType(p.cargo)) produced[p.cargo]++;
567 }
568 }
569
570 return produced;
571}
572
582CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted)
583{
584 CargoArray acceptance{};
585 if (always_accepted != nullptr) *always_accepted = 0;
586
587 TileArea ta = TileArea(center_tile, w, h).Expand(rad);
588
589 for (TileIndex tile : ta) {
590 /* Ignore industry if it has a neutral station. */
592
593 AddAcceptedCargo(tile, acceptance, always_accepted);
594 }
595
596 return acceptance;
597}
598
604static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
605{
606 CargoArray acceptance{};
607 if (always_accepted != nullptr) *always_accepted = 0;
608
610 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
611 AddAcceptedCargo(tile, acceptance, always_accepted);
612 }
613
614 return acceptance;
615}
616
622void UpdateStationAcceptance(Station *st, bool show_msg)
623{
624 /* old accepted goods types */
625 CargoTypes old_acc = GetAcceptanceMask(st);
626
627 /* And retrieve the acceptance. */
628 CargoArray acceptance{};
629 if (!st->rect.IsEmpty()) {
630 acceptance = GetAcceptanceAroundStation(st, &st->always_accepted);
631 }
632
633 /* Adjust in case our station only accepts fewer kinds of goods */
634 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
635 uint amt = acceptance[cargo];
636
637 /* Make sure the station can accept the goods type. */
638 bool is_passengers = IsCargoInClass(cargo, CargoClass::Passengers);
639 if ((!is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::TruckStop, StationFacility::Airport, StationFacility::Dock})) ||
640 (is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::BusStop, StationFacility::Airport, StationFacility::Dock}))) {
641 amt = 0;
642 }
643
644 GoodsEntry &ge = st->goods[cargo];
647 (*LinkGraph::Get(ge.link_graph))[ge.node].SetDemand(amt / 8);
648 }
649 }
650
651 /* Only show a message in case the acceptance was actually changed. */
652 CargoTypes new_acc = GetAcceptanceMask(st);
653 if (old_acc == new_acc) return;
654
655 /* show a message to report that the acceptance was changed? */
656 if (show_msg && st->owner == _local_company && st->IsInUse()) {
657 /* Combine old and new masks to get changes */
658 CargoTypes accepts = new_acc & ~old_acc;
659 CargoTypes rejects = ~new_acc & old_acc;
660
661 /* Show news message if there are any changes */
662 if (accepts != 0) ShowRejectOrAcceptNews(st, accepts, false);
663 if (rejects != 0) ShowRejectOrAcceptNews(st, rejects, true);
664 }
665
666 /* redraw the station view since acceptance changed */
668}
669
670static void UpdateStationSignCoord(BaseStation *st)
671{
672 const StationRect *r = &st->rect;
673
674 if (r->IsEmpty()) return; // no tiles belong to this station
675
676 /* clamp sign coord to be inside the station rect */
677 TileIndex new_xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
678 st->MoveSign(new_xy);
679
680 if (!Station::IsExpected(st)) return;
681 Station *full_station = Station::From(st);
682 for (const GoodsEntry &ge : full_station->goods) {
683 LinkGraphID lg = ge.link_graph;
684 if (!LinkGraph::IsValidID(lg)) continue;
685 (*LinkGraph::Get(lg))[ge.node].UpdateLocation(st->xy);
686 }
687}
688
698static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
699{
700 /* Find a deleted station close to us */
701 if (*st == nullptr && reuse) *st = GetClosestDeletedStation(area.tile);
702
703 if (*st != nullptr) {
704 if ((*st)->owner != _current_company) {
705 return CommandCost(CMD_ERROR);
706 }
707
708 CommandCost ret = (*st)->rect.BeforeAddRect(area.tile, area.w, area.h, StationRect::ADD_TEST);
709 if (ret.Failed()) return ret;
710 } else {
711 /* allocate and initialize new station */
712 if (!Station::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_STATIONS_LOADING);
713
714 if (flags.Test(DoCommandFlag::Execute)) {
715 *st = new Station(area.tile);
716 _station_kdtree.Insert((*st)->index);
717
718 (*st)->town = ClosestTownFromTile(area.tile, UINT_MAX);
719 (*st)->string_id = GenerateStationName(*st, area.tile, name_class);
720
722 (*st)->town->have_ratings.Set(_current_company);
723 }
724 }
725 }
726 return CommandCost();
727}
728
736{
737 if (!st->IsInUse()) {
738 st->delete_ctr = 0;
740 }
741 /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
742 UpdateStationSignCoord(st);
743}
744
751{
752 this->UpdateVirtCoord();
754
755 if (adding) {
756 this->RecomputeCatchment();
757 MarkCatchmentTilesDirty();
759 } else {
760 MarkCatchmentTilesDirty();
761 }
762
763 switch (type) {
764 case StationType::Rail:
766 break;
767 case StationType::Airport:
768 break;
769 case StationType::Truck:
770 case StationType::Bus:
772 break;
773 case StationType::Dock:
775 break;
776 default: NOT_REACHED();
777 }
778
779 if (adding) {
780 UpdateStationAcceptance(this, false);
782 } else {
784 this->RecomputeCatchment();
785 }
786
787}
788
790
800CommandCost CheckBuildableTile(TileIndex tile, DiagDirections invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge = true)
801{
802 if (check_bridge && IsBridgeAbove(tile)) {
803 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
804 }
805
807 if (ret.Failed()) return ret;
808
809 auto [tileh, z] = GetTileSlopeZ(tile);
810
811 /* Prohibit building if
812 * 1) The tile is "steep" (i.e. stretches two height levels).
813 * 2) The tile is non-flat and the build_on_slopes switch is disabled.
814 */
815 if ((!allow_steep && IsSteepSlope(tileh)) ||
817 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
818 }
819
821 int flat_z = z + GetSlopeMaxZ(tileh);
822 if (tileh != SLOPE_FLAT) {
823 /* Forbid building if the tile faces a slope in a invalid direction. */
824 for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) {
825 if (invalid_dirs.Test(dir) && !CanBuildDepotByTileh(dir, tileh)) {
826 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
827 }
828 }
829 cost.AddCost(_price[PR_BUILD_FOUNDATION]);
830 }
831
832 /* The level of this tile must be equal to allowed_z. */
833 if (allowed_z < 0) {
834 /* First tile. */
835 allowed_z = flat_z;
836 } else if (allowed_z != flat_z) {
837 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
838 }
839
840 return cost;
841}
842
850{
852 int allowed_z = -1;
853
854 for (; tile_iter != INVALID_TILE; ++tile_iter) {
855 CommandCost ret = CheckBuildableTile(tile_iter, {}, allowed_z, true);
856 if (ret.Failed()) return ret;
857 cost.AddCost(ret.GetCost());
858
859 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_iter);
860 if (ret.Failed()) return ret;
861 cost.AddCost(ret.GetCost());
862 }
863
864 return cost;
865}
866
883static 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)
884{
886 DiagDirections invalid_dirs = AxisToDiagDirs(axis);
887
888 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
889 bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
890
891 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false);
892 if (ret.Failed()) return ret;
893 cost.AddCost(ret.GetCost());
894
895 if (slope_cb) {
896 /* Do slope check if requested. */
897 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
898 if (ret.Failed()) return ret;
899 }
900
901 /* if station is set, then we have special handling to allow building on top of already existing stations.
902 * so station points to StationID::Invalid() if we can build on any station.
903 * Or it points to a station if we're only allowed to build on exactly that station. */
904 if (station != nullptr && IsTileType(tile_cur, MP_STATION)) {
905 if (!IsRailStation(tile_cur)) {
906 return ClearTile_Station(tile_cur, DoCommandFlag::Auto); // get error message
907 } else {
908 StationID st = GetStationIndex(tile_cur);
909 if (*station == StationID::Invalid()) {
910 *station = st;
911 } else if (*station != st) {
912 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
913 }
914 }
915 } else {
916 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
917 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
918 /* Don't overbuild signals. */
919 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
920
921 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
922 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
923 TrackBits tracks = GetTrackBits(tile_cur);
924 Track track = RemoveFirstTrack(&tracks);
925 Track expected_track = invalid_dirs.Test(DIAGDIR_NE) ? TRACK_X : TRACK_Y;
926
927 /* The existing track must align with the desired station axis. */
928 if (tracks == TRACK_BIT_NONE && track == expected_track) {
929 /* Check for trains having a reservation for this tile. */
930 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
931 Train *v = GetTrainForReservation(tile_cur, track);
932 if (v != nullptr) {
933 affected_vehicles.push_back(v);
934 }
935 }
936 ret = Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile_cur, track);
937 if (ret.Failed()) return ret;
938 cost.AddCost(ret.GetCost());
939 /* With DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) CmdLandscapeClear would fail since the rail still exists */
940 return cost;
941 }
942 }
943 }
944 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
945 if (ret.Failed()) return ret;
946 cost.AddCost(ret.GetCost());
947 }
948
949 return cost;
950}
951
965static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlags flags, DiagDirections invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt)
966{
968
969 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through);
970 if (ret.Failed()) return ret;
971 cost.AddCost(ret.GetCost());
972
973 /* If station is set, then we have special handling to allow building on top of already existing stations.
974 * Station points to StationID::Invalid() if we can build on any station.
975 * Or it points to a station if we're only allowed to build on exactly that station. */
976 if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
977 if (!IsAnyRoadStop(cur_tile)) {
978 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
979 } else {
980 if (station_type != GetStationType(cur_tile) ||
981 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
982 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
983 }
984 /* Drive-through station in the wrong direction. */
985 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
986 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
987 }
988 StationID st = GetStationIndex(cur_tile);
989 if (*station == StationID::Invalid()) {
990 *station = st;
991 } else if (*station != st) {
992 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
993 }
994 }
995 } else {
996 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
997 /* Road bits in the wrong direction. */
998 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
999 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
1000 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1001 switch (CountBits(rb)) {
1002 case 1:
1003 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1004
1005 case 2:
1006 if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1007 return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
1008
1009 default: // 3 or 4
1010 return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
1011 }
1012 }
1013
1014 if (build_over_road) {
1015 /* There is a road, check if we can build road+tram stop over it. */
1016 RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
1017 if (road_rt != INVALID_ROADTYPE) {
1018 Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
1019 if (road_owner == OWNER_TOWN) {
1020 if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
1022 ret = CheckOwnership(road_owner);
1023 if (ret.Failed()) return ret;
1024 }
1025 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
1026
1027 if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1028
1029 if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
1030 ret = CheckOwnership(road_owner);
1031 if (ret.Failed()) return ret;
1032 }
1033
1034 cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
1035 } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
1036 cost.AddCost(RoadBuildCost(rt) * 2);
1037 }
1038
1039 /* There is a tram, check if we can build road+tram stop over it. */
1040 RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
1041 if (tram_rt != INVALID_ROADTYPE) {
1042 Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
1043 if (Company::IsValidID(tram_owner) &&
1045 /* Disallow breaking end-of-line of someone else
1046 * so trams can still reverse on this tile. */
1047 HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
1048 ret = CheckOwnership(tram_owner);
1049 if (ret.Failed()) return ret;
1050 }
1051 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
1052
1053 if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1054
1055 cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
1056 } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
1057 cost.AddCost(RoadBuildCost(rt) * 2);
1058 }
1059 } else if (rt == INVALID_ROADTYPE) {
1060 return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
1061 } else {
1062 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
1063 if (ret.Failed()) return ret;
1064 cost.AddCost(ret.GetCost());
1065 cost.AddCost(RoadBuildCost(rt) * 2);
1066 }
1067 }
1068
1069 return cost;
1070}
1071
1079{
1080 TileArea cur_ta = st->train_station;
1081
1082 /* determine new size of train station region.. */
1083 int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
1084 int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
1085 new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
1086 new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
1087 new_ta.tile = TileXY(x, y);
1088
1089 /* make sure the final size is not too big. */
1091 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1092 }
1093
1094 return CommandCost();
1095}
1096
1097static inline uint8_t *CreateSingle(uint8_t *layout, int n)
1098{
1099 int i = n;
1100 do *layout++ = 0; while (--i);
1101 layout[((n - 1) >> 1) - n] = 2;
1102 return layout;
1103}
1104
1105static inline uint8_t *CreateMulti(uint8_t *layout, int n, uint8_t b)
1106{
1107 int i = n;
1108 do *layout++ = b; while (--i);
1109 if (n > 4) {
1110 layout[0 - n] = 0;
1111 layout[n - 1 - n] = 0;
1112 }
1113 return layout;
1114}
1115
1123void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const StationSpec *statspec)
1124{
1125 if (statspec != nullptr) {
1126 auto found = statspec->layouts.find(GetStationLayoutKey(numtracks, plat_len));
1127 if (found != std::end(statspec->layouts)) {
1128 /* Custom layout defined, copy to buffer. */
1129 std::copy(std::begin(found->second), std::end(found->second), layout);
1130 return;
1131 }
1132 }
1133
1134 if (plat_len == 1) {
1135 CreateSingle(layout, numtracks);
1136 } else {
1137 if (numtracks & 1) layout = CreateSingle(layout, plat_len);
1138 int n = numtracks >> 1;
1139
1140 while (--n >= 0) {
1141 layout = CreateMulti(layout, plat_len, 4);
1142 layout = CreateMulti(layout, plat_len, 6);
1143 }
1144 }
1145}
1146
1159template <class T, StringID error_message, class F>
1160CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1161{
1162 assert(*st == nullptr);
1163 bool check_surrounding = true;
1164
1165 if (existing_station != StationID::Invalid()) {
1166 if (adjacent && existing_station != station_to_join) {
1167 /* You can't build an adjacent station over the top of one that
1168 * already exists. */
1169 return CommandCost(error_message);
1170 } else {
1171 /* Extend the current station, and don't check whether it will
1172 * be near any other stations. */
1173 T *candidate = T::GetIfValid(existing_station);
1174 if (candidate != nullptr && filter(candidate)) *st = candidate;
1175 check_surrounding = (*st == nullptr);
1176 }
1177 } else {
1178 /* There's no station here. Don't check the tiles surrounding this
1179 * one if the company wanted to build an adjacent station. */
1180 if (adjacent) check_surrounding = false;
1181 }
1182
1183 if (check_surrounding) {
1184 /* Make sure there is no more than one other station around us that is owned by us. */
1185 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1186 if (ret.Failed()) return ret;
1187 }
1188
1189 /* Distant join */
1190 if (*st == nullptr && station_to_join != StationID::Invalid()) *st = T::GetIfValid(station_to_join);
1191
1192 return CommandCost();
1193}
1194
1204static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1205{
1206 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1207}
1208
1219CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1220{
1221 if (is_road) {
1222 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); });
1223 } else {
1224 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); });
1225 }
1226}
1227
1239
1251
1266static 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)
1267{
1269 bool length_price_ready = true;
1270 uint8_t tracknum = 0;
1271 int allowed_z = -1;
1272 for (TileIndex cur_tile : tile_area) {
1273 /* Clear the land below the station. */
1274 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1275 if (ret.Failed()) return ret;
1276
1277 /* Only add _price[PR_BUILD_STATION_RAIL_LENGTH] once for each valid plat_len. */
1278 if (tracknum == numtracks) {
1279 length_price_ready = true;
1280 tracknum = 0;
1281 } else {
1282 tracknum++;
1283 }
1284
1285 /* AddCost for new or rotated rail stations. */
1286 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1287 cost.AddCost(ret.GetCost());
1288 cost.AddCost(_price[PR_BUILD_STATION_RAIL]);
1289 cost.AddCost(RailBuildCost(rt));
1290
1291 if (length_price_ready) {
1292 cost.AddCost(_price[PR_BUILD_STATION_RAIL_LENGTH]);
1293 length_price_ready = false;
1294 }
1295 }
1296 }
1297
1298 return cost;
1299}
1300
1308{
1309 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1310 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlag::Pylons : StationSpec::TileFlags{};
1311 return statspec->tileflags[gfx];
1312}
1313
1320{
1321 const auto flags = GetStationTileFlags(GetStationGfx(tile), statspec);
1325}
1326
1341CommandCost 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)
1342{
1343 /* Does the authority allow this? */
1344 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1345 if (ret.Failed()) return ret;
1346
1347 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1348
1349 /* Check if the given station class is valid */
1350 if (static_cast<uint>(spec_class) >= StationClass::GetClassCount()) return CMD_ERROR;
1351 const StationClass *cls = StationClass::Get(spec_class);
1352 if (IsWaypointClass(*cls)) return CMD_ERROR;
1353 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1354 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1355
1356 int w_org, h_org;
1357 if (axis == AXIS_X) {
1358 w_org = plat_len;
1359 h_org = numtracks;
1360 } else {
1361 h_org = plat_len;
1362 w_org = numtracks;
1363 }
1364
1365 bool reuse = (station_to_join != NEW_STATION);
1366 if (!reuse) station_to_join = StationID::Invalid();
1367 bool distant_join = (station_to_join != StationID::Invalid());
1368
1369 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1370
1372
1373 /* these values are those that will be stored in train_tile and station_platforms */
1374 TileArea new_location(tile_org, w_org, h_org);
1375
1376 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1377 StationID est = StationID::Invalid();
1378 std::vector<Train *> affected_vehicles;
1379 /* Add construction and clearing expenses. */
1380 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1381 if (cost.Failed()) return cost;
1382
1383 Station *st = nullptr;
1384 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1385 if (ret.Failed()) return ret;
1386
1387 ret = BuildStationPart(&st, flags, reuse, new_location, STATIONNAMING_RAIL);
1388 if (ret.Failed()) return ret;
1389
1390 if (st != nullptr && st->train_station.tile != INVALID_TILE) {
1391 ret = CanExpandRailStation(st, new_location);
1392 if (ret.Failed()) return ret;
1393 }
1394
1395 /* Check if we can allocate a custom stationspec to this station */
1396 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1397 int specindex = AllocateSpecToStation(statspec, st, flags.Test(DoCommandFlag::Execute));
1398 if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1399
1400 if (statspec != nullptr) {
1401 /* Perform NewStation checks */
1402
1403 /* Check if the station size is permitted */
1404 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1405 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1406
1407 /* Check if the station is buildable */
1409 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1411 }
1412 }
1413
1414 if (flags.Test(DoCommandFlag::Execute)) {
1415 st->train_station = new_location;
1416 st->AddFacility(StationFacility::Train, new_location.tile);
1417
1418 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
1419
1420 if (statspec != nullptr) {
1421 /* Include this station spec's animation trigger bitmask
1422 * in the station's cached copy. */
1423 st->cached_anim_triggers.Set(statspec->animation.triggers);
1424 }
1425
1426 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1427 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1428 Track track = AxisToTrack(axis);
1429
1430 std::vector<uint8_t> layouts(numtracks * plat_len);
1431 GetStationLayout(layouts.data(), numtracks, plat_len, statspec);
1432
1433 uint8_t numtracks_orig = numtracks;
1434
1435 Company *c = Company::Get(st->owner);
1436 size_t layout_idx = 0;
1437 TileIndex tile_track = tile_org;
1438 do {
1439 TileIndex tile = tile_track;
1440 int w = plat_len;
1441 do {
1442 uint8_t layout = layouts[layout_idx++];
1443 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1444 /* Check for trains having a reservation for this tile. */
1446 if (v != nullptr) {
1447 affected_vehicles.push_back(v);
1449 }
1450 }
1451
1452 /* Railtype can change when overbuilding. */
1453 if (IsRailStationTile(tile)) {
1454 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1456 }
1457
1458 /* Remove animation if overbuilding */
1459 DeleteAnimatedTile(tile);
1460 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1461 MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, rt);
1462 /* Free the spec if we overbuild something */
1463 DeallocateSpecFromStation(st, old_specindex);
1464
1465 SetCustomStationSpecIndex(tile, specindex);
1466 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1467 SetAnimationFrame(tile, 0);
1468
1469 if (statspec != nullptr) {
1470 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1471 uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
1472
1473 /* As the station is not yet completely finished, the station does not yet exist. */
1474 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1475 if (callback != CALLBACK_FAILED) {
1476 if (callback <= UINT8_MAX) {
1477 SetStationGfx(tile, (callback & ~1) + axis);
1478 } else {
1480 }
1481 }
1482
1483 /* Trigger station animation -- after building? */
1484 TriggerStationAnimation(st, tile, StationAnimationTrigger::Built);
1485 }
1486
1487 SetRailStationTileFlags(tile, statspec);
1488
1489 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1491
1492 tile += tile_delta;
1493 } while (--w);
1494 AddTrackToSignalBuffer(tile_track, track, _current_company);
1495 YapfNotifyTrackLayoutChange(tile_track, track);
1496 tile_track += track_delta;
1497 } while (--numtracks);
1498
1499 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1500 /* Restore reservations of trains. */
1501 RestoreTrainReservation(affected_vehicles[i]);
1502 }
1503
1504 /* Check whether we need to expand the reservation of trains already on the station. */
1505 TileArea update_reservation_area;
1506 if (axis == AXIS_X) {
1507 update_reservation_area = TileArea(tile_org, 1, numtracks_orig);
1508 } else {
1509 update_reservation_area = TileArea(tile_org, numtracks_orig, 1);
1510 }
1511
1512 for (TileIndex tile : update_reservation_area) {
1513 /* Don't even try to make eye candy parts reserved. */
1514 if (IsStationTileBlocked(tile)) continue;
1515
1516 DiagDirection dir = AxisToDiagDir(axis);
1517 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1518 TileIndex platform_begin = tile;
1519 TileIndex platform_end = tile;
1520
1521 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1522 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1523 platform_begin = next_tile;
1524 }
1525 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1526 platform_end = next_tile;
1527 }
1528
1529 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1530 bool reservation = false;
1531 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1532 reservation = HasStationReservation(t);
1533 }
1534
1535 if (reservation) {
1536 SetRailStationPlatformReservation(platform_begin, dir, true);
1537 }
1538 }
1539
1540 st->MarkTilesDirty(false);
1541 st->AfterStationTileSetChange(true, StationType::Rail);
1542 }
1543
1544 return cost;
1545}
1546
1547static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1548{
1549restart:
1550
1551 /* too small? */
1552 if (ta.w != 0 && ta.h != 0) {
1553 /* check the left side, x = constant, y changes */
1554 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1555 /* the left side is unused? */
1556 if (++i == ta.h) {
1557 ta.tile += TileDiffXY(1, 0);
1558 ta.w--;
1559 goto restart;
1560 }
1561 }
1562
1563 /* check the right side, x = constant, y changes */
1564 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1565 /* the right side is unused? */
1566 if (++i == ta.h) {
1567 ta.w--;
1568 goto restart;
1569 }
1570 }
1571
1572 /* check the upper side, y = constant, x changes */
1573 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1574 /* the left side is unused? */
1575 if (++i == ta.w) {
1576 ta.tile += TileDiffXY(0, 1);
1577 ta.h--;
1578 goto restart;
1579 }
1580 }
1581
1582 /* check the lower side, y = constant, x changes */
1583 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1584 /* the left side is unused? */
1585 if (++i == ta.w) {
1586 ta.h--;
1587 goto restart;
1588 }
1589 }
1590 } else {
1591 ta.Clear();
1592 }
1593
1594 return ta;
1595}
1596
1597static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1598{
1599 return st->TileBelongsToRailStation(tile);
1600}
1601
1602static void MakeRailStationAreaSmaller(BaseStation *st)
1603{
1604 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1605}
1606
1607static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1608{
1609 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1610}
1611
1612static void MakeShipStationAreaSmaller(Station *st)
1613{
1614 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1615 UpdateStationDockingTiles(st);
1616}
1617
1618static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1619{
1620 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1621}
1622
1623void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1624{
1625 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1626}
1627
1638template <class T>
1639CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
1640{
1641 /* Count of the number of tiles removed */
1642 int quantity = 0;
1644 /* Accumulator for the errors seen during clearing. If no errors happen,
1645 * and the quantity is 0 there is no station. Otherwise it will be one
1646 * of the other error that got accumulated. */
1647 CommandCost error;
1648
1649 /* Do the action for every tile into the area */
1650 for (TileIndex tile : ta) {
1651 /* Make sure the specified tile is a rail station */
1652 if (!HasStationTileRail(tile)) continue;
1653
1654 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1656 error.AddCost(std::move(ret));
1657 if (error.Failed()) continue;
1658
1659 /* Check ownership of station */
1660 T *st = T::GetByTile(tile);
1661 if (st == nullptr) continue;
1662
1664 ret = CheckOwnership(st->owner);
1665 error.AddCost(std::move(ret));
1666 if (error.Failed()) continue;
1667 }
1668
1669 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1670 quantity++;
1671
1672 if (keep_rail || IsStationTileBlocked(tile)) {
1673 /* Don't refund the 'steel' of the track when we keep the
1674 * rail, or when the tile didn't have any rail at all. */
1675 total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
1676 }
1677
1678 if (flags.Test(DoCommandFlag::Execute)) {
1679 /* read variables before the station tile is removed */
1680 uint specindex = GetCustomStationSpecIndex(tile);
1681 Track track = GetRailStationTrack(tile);
1682 Owner owner = GetTileOwner(tile);
1683 RailType rt = GetRailType(tile);
1684 Train *v = nullptr;
1685
1686 if (HasStationReservation(tile)) {
1687 v = GetTrainForReservation(tile, track);
1688 if (v != nullptr) FreeTrainReservation(v);
1689 }
1690
1691 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1692 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1693
1694 DoClearSquare(tile);
1695 DeleteNewGRFInspectWindow(GSF_STATIONS, tile.base());
1696 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
1697 Company::Get(owner)->infrastructure.station--;
1699
1700 st->rect.AfterRemoveTile(st, tile);
1701 AddTrackToSignalBuffer(tile, track, owner);
1702 YapfNotifyTrackLayoutChange(tile, track);
1703
1704 DeallocateSpecFromStation(st, specindex);
1705
1706 include(affected_stations, st);
1707
1708 if (v != nullptr) RestoreTrainReservation(v);
1709 }
1710 }
1711
1712 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1713
1714 for (T *st : affected_stations) {
1715
1716 /* now we need to make the "spanned" area of the railway station smaller
1717 * if we deleted something at the edges.
1718 * we also need to adjust train_tile. */
1719 MakeRailStationAreaSmaller(st);
1720 UpdateStationSignCoord(st);
1721
1722 /* if we deleted the whole station, delete the train facility. */
1723 if (st->train_station.tile == INVALID_TILE) {
1727 MarkCatchmentTilesDirty();
1728 st->UpdateVirtCoord();
1730 }
1731 }
1732
1733 total_cost.AddCost(quantity * removal_cost);
1734 return total_cost;
1735}
1736
1747{
1748 if (end == 0) end = start;
1749 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1750
1751 TileArea ta(start, end);
1752 std::vector<Station *> affected_stations;
1753
1754 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], keep_rail);
1755 if (ret.Failed()) return ret;
1756
1757 /* Do all station specific functions here. */
1758 for (Station *st : affected_stations) {
1759
1761 st->MarkTilesDirty(false);
1762 MarkCatchmentTilesDirty();
1763 st->RecomputeCatchment();
1764 }
1765
1766 /* Now apply the rail cost to the number that we deleted */
1767 return ret;
1768}
1769
1780{
1781 if (end == 0) end = start;
1782 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1783
1784 TileArea ta(start, end);
1785 std::vector<Waypoint *> affected_stations;
1786
1787 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], keep_rail);
1788}
1789
1790
1799template <class T>
1801{
1802 /* Current company owns the station? */
1804 CommandCost ret = CheckOwnership(st->owner);
1805 if (ret.Failed()) return ret;
1806 }
1807
1808 /* determine width and height of platforms */
1809 TileArea ta = st->train_station;
1810
1811 assert(ta.w != 0 && ta.h != 0);
1812
1814 /* clear all areas of the station */
1815 for (TileIndex tile : ta) {
1816 /* only remove tiles that are actually train station tiles */
1817 if (st->TileBelongsToRailStation(tile)) {
1818 std::vector<T*> affected_stations; // dummy
1819 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1820 if (ret.Failed()) return ret;
1821 cost.AddCost(ret.GetCost());
1822 }
1823 }
1824
1825 return cost;
1826}
1827
1835{
1836 /* if there is flooding, remove platforms tile by tile */
1839 }
1840
1841 Station *st = Station::GetByTile(tile);
1842 CommandCost cost = RemoveRailStation(st, flags, _price[PR_CLEAR_STATION_RAIL]);
1843
1845
1846 return cost;
1847}
1848
1856{
1857 /* if there is flooding, remove waypoints tile by tile */
1860 }
1861
1862 return RemoveRailStation(Waypoint::GetByTile(tile), flags, _price[PR_CLEAR_WAYPOINT_RAIL]);
1863}
1864
1865
1871static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
1872{
1873 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1874
1875 if (*primary_stop == nullptr) {
1876 /* we have no roadstop of the type yet, so write a "primary stop" */
1877 return primary_stop;
1878 } else {
1879 /* there are stops already, so append to the end of the list */
1880 RoadStop *stop = *primary_stop;
1881 while (stop->next != nullptr) stop = stop->next;
1882 return &stop->next;
1883 }
1884}
1885
1886static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1887CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1888
1898static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1899{
1900 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1901}
1902
1916CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
1917{
1918 DiagDirections invalid_dirs{};
1919 if (is_drive_through) {
1920 invalid_dirs.Set(AxisToDiagDir(axis));
1921 invalid_dirs.Set(ReverseDiagDir(AxisToDiagDir(axis)));
1922 } else {
1923 invalid_dirs.Set(ddir);
1924 }
1925
1926 /* Check every tile in the area. */
1927 int allowed_z = -1;
1929 for (TileIndex cur_tile : tile_area) {
1930 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
1931 if (ret.Failed()) return ret;
1932
1933 bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
1934
1935 /* Only add costs if a stop doesn't already exist in the location */
1936 if (!is_preexisting_roadstop) {
1937 cost.AddCost(ret.GetCost());
1938 cost.AddCost(unit_cost);
1939 }
1940 }
1941
1942 return cost;
1943}
1944
1961CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
1962 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
1963{
1964 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= RoadStopType::End) return CMD_ERROR;
1965 bool reuse = (station_to_join != NEW_STATION);
1966 if (!reuse) station_to_join = StationID::Invalid();
1967 bool distant_join = (station_to_join != StationID::Invalid());
1968
1969 /* Check if the given station class is valid */
1970 if (static_cast<uint>(spec_class) >= RoadStopClass::GetClassCount()) return CMD_ERROR;
1971 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
1972 if (IsWaypointClass(*cls)) return CMD_ERROR;
1973 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1974
1975 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
1976 if (roadstopspec != nullptr) {
1977 if (stop_type == RoadStopType::Truck && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
1978 if (stop_type == RoadStopType::Bus && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
1979 if (!is_drive_through && roadstopspec->flags.Test(RoadStopSpecFlag::DriveThroughOnly)) return CMD_ERROR;
1980 }
1981
1982 /* Check if the requested road stop is too big */
1983 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1984 /* Check for incorrect width / length. */
1985 if (width == 0 || length == 0) return CMD_ERROR;
1986 /* Check if the first tile and the last tile are valid */
1987 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
1988
1989 TileArea roadstop_area(tile, width, length);
1990
1991 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1992
1993 /* Trams only have drive through stops */
1994 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
1995
1996 Axis axis = DiagDirToAxis(ddir);
1997
1999 if (ret.Failed()) return ret;
2000
2001 bool is_truck_stop = stop_type != RoadStopType::Bus;
2002
2003 /* Total road stop cost. */
2004 Money unit_cost;
2005 if (roadstopspec != nullptr) {
2006 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS);
2007 } else {
2008 unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
2009 }
2010 StationID est = StationID::Invalid();
2011 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, axis, ddir, &est, rt, unit_cost);
2012 if (cost.Failed()) return cost;
2013
2014 Station *st = nullptr;
2015 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2016 if (ret.Failed()) return ret;
2017
2018 /* Check if this number of road stops can be allocated. */
2019 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);
2020
2021 ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD);
2022 if (ret.Failed()) return ret;
2023
2024 /* Check if we can allocate a custom stationspec to this station */
2025 int specindex = AllocateSpecToRoadStop(roadstopspec, st, flags.Test(DoCommandFlag::Execute));
2026 if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2027
2028 if (roadstopspec != nullptr) {
2029 /* Perform NewGRF checks */
2030
2031 /* Check if the road stop is buildable */
2032 if (roadstopspec->callback_mask.Test(RoadStopCallbackMask::Avail)) {
2033 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? StationType::Truck : StationType::Bus, 0);
2034 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2035 }
2036 }
2037
2038 if (flags.Test(DoCommandFlag::Execute)) {
2039 /* Check every tile in the area. */
2040 for (TileIndex cur_tile : roadstop_area) {
2041 /* Get existing road types and owners before any tile clearing */
2042 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
2043 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
2044 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
2045 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
2046
2047 if (IsTileType(cur_tile, MP_STATION) && IsStationRoadStop(cur_tile)) {
2048 RemoveRoadStop(cur_tile, flags, specindex);
2049 }
2050
2051 if (roadstopspec != nullptr) {
2052 /* Include this road stop spec's animation trigger bitmask
2053 * in the station's cached copy. */
2054 st->cached_roadstop_anim_triggers.Set(roadstopspec->animation.triggers);
2055 }
2056
2057 RoadStop *road_stop = new RoadStop(cur_tile);
2058 /* Insert into linked list of RoadStops. */
2059 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2060 *currstop = road_stop;
2061
2062 if (is_truck_stop) {
2063 st->truck_station.Add(cur_tile);
2064 } else {
2065 st->bus_station.Add(cur_tile);
2066 }
2067
2068 /* Initialize an empty station. */
2069 st->AddFacility(is_truck_stop ? StationFacility::TruckStop : StationFacility::BusStop, cur_tile);
2070
2071 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
2072
2073 RoadStopType rs_type = is_truck_stop ? RoadStopType::Truck : RoadStopType::Bus;
2074 if (is_drive_through) {
2075 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2076 * bits first. */
2077 if (IsNormalRoadTile(cur_tile)) {
2078 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
2079 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
2080 }
2081
2082 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2083 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2084
2085 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == RoadStopType::Bus ? StationType::Bus : StationType::Truck), road_rt, tram_rt, axis);
2086 road_stop->MakeDriveThrough();
2087 } else {
2088 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2089 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2090 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2091 }
2094 Company::Get(st->owner)->infrastructure.station++;
2095
2096 SetCustomRoadStopSpecIndex(cur_tile, specindex);
2097 if (roadstopspec != nullptr) {
2098 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2099 TriggerRoadStopAnimation(st, cur_tile, StationAnimationTrigger::Built);
2100 }
2101
2102 MarkTileDirtyByTile(cur_tile);
2103 }
2104
2105 if (st != nullptr) {
2106 st->AfterStationTileSetChange(true, is_truck_stop ? StationType::Truck: StationType::Bus);
2107 }
2108 }
2109 return cost;
2110}
2111
2119static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2120{
2121 Station *st = Station::GetByTile(tile);
2122
2124 CommandCost ret = CheckOwnership(st->owner);
2125 if (ret.Failed()) return ret;
2126 }
2127
2128 bool is_truck = IsTruckStop(tile);
2129
2130 RoadStop **primary_stop;
2131 RoadStop *cur_stop;
2132 if (is_truck) { // truck stop
2133 primary_stop = &st->truck_stops;
2134 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Truck);
2135 } else {
2136 primary_stop = &st->bus_stops;
2137 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Bus);
2138 }
2139
2140 assert(cur_stop != nullptr);
2141
2142 /* don't do the check for drive-through road stops when company bankrupts */
2144 /* remove the 'going through road stop' status from all vehicles on that tile */
2145 if (flags.Test(DoCommandFlag::Execute)) {
2146 for (Vehicle *v : VehiclesOnTile(tile)) {
2147 if (v->type != VEH_ROAD) continue;
2148 /* Okay... we are a road vehicle on a drive through road stop.
2149 * But that road stop has just been removed, so we need to make
2150 * sure we are in a valid state... however, vehicles can also
2151 * turn on road stop tiles, so only clear the 'road stop' state
2152 * bits and only when the state was 'in road stop', otherwise
2153 * we'll end up clearing the turn around bits. */
2156 }
2157 }
2158 } else {
2160 if (ret.Failed()) return ret;
2161 }
2162
2163 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2164
2165 if (flags.Test(DoCommandFlag::Execute)) {
2166 if (*primary_stop == cur_stop) {
2167 /* removed the first stop in the list */
2168 *primary_stop = cur_stop->next;
2169 /* removed the only stop? */
2170 if (*primary_stop == nullptr) {
2173 }
2174 } else {
2175 /* tell the predecessor in the list to skip this stop */
2176 RoadStop *pred = *primary_stop;
2177 while (pred->next != cur_stop) pred = pred->next;
2178 pred->next = cur_stop->next;
2179 }
2180
2181 /* Update company infrastructure counts. */
2182 for (RoadTramType rtt : _roadtramtypes) {
2183 RoadType rt = GetRoadType(tile, rtt);
2185 }
2186
2187 Company::Get(st->owner)->infrastructure.station--;
2189
2190 uint specindex = GetCustomRoadStopSpecIndex(tile);
2191
2192 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2193
2194 if (IsDriveThroughStopTile(tile)) {
2195 /* Clears the tile for us */
2196 cur_stop->ClearDriveThrough();
2197 DeleteAnimatedTile(tile);
2198 } else {
2199 DoClearSquare(tile);
2200 }
2201
2202 delete cur_stop;
2203
2204 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2205 * this station, then look for any currently heading to the tile. */
2206 StationID station_id = st->index;
2208 [](const Vehicle *v) { return v->type == VEH_ROAD; },
2209 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2210 [station_id, tile](Vehicle *v) {
2211 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2212 v->SetDestTile(v->GetOrderStationLocation(station_id));
2213 }
2214 }
2215 );
2216
2217 st->rect.AfterRemoveTile(st, tile);
2218
2219 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
2220
2221 st->RemoveRoadStopTileData(tile);
2222 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2223
2224 /* Update the tile area of the truck/bus stop */
2225 if (is_truck) {
2226 st->truck_station.Clear();
2227 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2228 } else {
2229 st->bus_station.Clear();
2230 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2231 }
2232 }
2233
2234 Price category = is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS;
2235 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2236}
2237
2245CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2246{
2247 Waypoint *wp = Waypoint::GetByTile(tile);
2248
2250 CommandCost ret = CheckOwnership(wp->owner);
2251 if (ret.Failed()) return ret;
2252 }
2253
2254 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2255 if (!flags.Test(DoCommandFlag::Bankrupt)) {
2257 if (ret.Failed()) return ret;
2258 }
2259
2260 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2261
2262 if (flags.Test(DoCommandFlag::Execute)) {
2263 /* Update company infrastructure counts. */
2264 for (RoadTramType rtt : _roadtramtypes) {
2265 RoadType rt = GetRoadType(tile, rtt);
2267 }
2268
2269 Company::Get(wp->owner)->infrastructure.station--;
2271
2272 uint specindex = GetCustomRoadStopSpecIndex(tile);
2273
2274 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2275
2276 DoClearSquare(tile);
2277
2278 wp->rect.AfterRemoveTile(wp, tile);
2279
2280 wp->RemoveRoadStopTileData(tile);
2281 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2282
2283 if (replacement_spec_index < 0) {
2284 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2285
2286 UpdateStationSignCoord(wp);
2287
2288 /* if we deleted the whole waypoint, delete the road facility. */
2292 wp->UpdateVirtCoord();
2294 }
2295 }
2296 }
2297
2298 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(PR_CLEAR_STATION_TRUCK) : _price[PR_CLEAR_STATION_TRUCK]);
2299}
2300
2309static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
2310{
2312 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2313 bool had_success = false;
2314
2315 for (TileIndex cur_tile : roadstop_area) {
2316 /* Make sure the specified tile is a road stop of the correct type */
2317 if (!IsTileType(cur_tile, MP_STATION) || !IsAnyRoadStop(cur_tile) || IsRoadWaypoint(cur_tile) != road_waypoint) continue;
2318
2319 /* Save information on to-be-restored roads before the stop is removed. */
2320 RoadBits road_bits = ROAD_NONE;
2321 RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
2322 Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
2323 if (IsDriveThroughStopTile(cur_tile)) {
2324 for (RoadTramType rtt : _roadtramtypes) {
2325 road_type[rtt] = GetRoadType(cur_tile, rtt);
2326 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2327 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2328 /* If we don't want to preserve our roads then restore only roads of others. */
2329 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2330 }
2331 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2332 }
2333
2334 CommandCost ret;
2335 if (road_waypoint) {
2336 ret = RemoveRoadWaypointStop(cur_tile, flags);
2337 } else {
2338 ret = RemoveRoadStop(cur_tile, flags);
2339 }
2340 if (ret.Failed()) {
2341 last_error = std::move(ret);
2342 continue;
2343 }
2344 cost.AddCost(ret.GetCost());
2345 had_success = true;
2346
2347 /* Restore roads. */
2348 if (flags.Test(DoCommandFlag::Execute) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
2349 MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2350 road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
2351
2352 /* Update company infrastructure counts. */
2353 int count = CountBits(road_bits);
2354 UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
2355 UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
2356 }
2357 }
2358
2359 return had_success ? cost : last_error;
2360}
2361
2372CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2373{
2374 if (stop_type >= RoadStopType::End) return CMD_ERROR;
2375 /* Check for incorrect width / height. */
2376 if (width == 0 || height == 0) return CMD_ERROR;
2377 /* Check if the first tile and the last tile are valid */
2378 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2379 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2380 if (remove_road && flags.Test(DoCommandFlag::Bankrupt)) return CMD_ERROR;
2381
2382 TileArea roadstop_area(tile, width, height);
2383
2384 return RemoveGenericRoadStop(flags, roadstop_area, false, remove_road);
2385}
2386
2395{
2396 if (end == 0) end = start;
2397 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2398
2399 TileArea roadstop_area(start, end);
2400
2401 return RemoveGenericRoadStop(flags, roadstop_area, true, false);
2402}
2403
2412uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2413{
2414 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2415 * So no need to go any further*/
2416 if (as->noise_level < 2) return as->noise_level;
2417
2418 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2419 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2420 * Basically, it says that the less tolerant a town is, the bigger the distance before
2421 * an actual decrease can be granted */
2422 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2423
2424 /* now, we want to have the distance segmented using the distance judged bareable by town
2425 * This will give us the coefficient of reduction the distance provides. */
2426 uint noise_reduction = distance / town_tolerance_distance;
2427
2428 /* If the noise reduction equals the airport noise itself, don't give it for free.
2429 * Otherwise, simply reduce the airport's level. */
2430 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2431}
2432
2443Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2444{
2445 assert(Town::GetNumItems() > 0);
2446
2447 Town *nearest = nullptr;
2448
2449 auto width = as->size_x;
2450 auto height = as->size_y;
2451 if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
2452
2453 uint perimeter_min_x = TileX(tile);
2454 uint perimeter_min_y = TileY(tile);
2455 uint perimeter_max_x = perimeter_min_x + width - 1;
2456 uint perimeter_max_y = perimeter_min_y + height - 1;
2457
2458 mindist = UINT_MAX - 1; // prevent overflow
2459
2460 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2461 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2462 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2463 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) {
2464 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2465 if (t == nullptr) continue;
2466
2467 uint dist = DistanceManhattan(t->xy, cur_tile);
2468 if (dist == mindist && t->index < nearest->index) nearest = t;
2469 if (dist < mindist) {
2470 nearest = t;
2471 mindist = dist;
2472 }
2473 }
2474 }
2475
2476 return nearest;
2477}
2478
2486static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2487{
2489}
2490
2491
2494{
2495 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2496
2497 for (const Station *st : Station::Iterate()) {
2498 if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
2499 uint dist;
2500 Town *nearest = AirportGetNearestTown(st, dist);
2501 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2502 }
2503 }
2504}
2505
2516CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2517{
2518 bool reuse = (station_to_join != NEW_STATION);
2519 if (!reuse) station_to_join = StationID::Invalid();
2520 bool distant_join = (station_to_join != StationID::Invalid());
2521
2522 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2523
2524 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2525
2527 if (ret.Failed()) return ret;
2528
2529 /* Check if a valid, buildable airport was chosen for construction */
2530 const AirportSpec *as = AirportSpec::Get(airport_type);
2531 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2532 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2533
2534 Direction rotation = as->layouts[layout].rotation;
2535 int w = as->size_x;
2536 int h = as->size_y;
2537 if (rotation == DIR_E || rotation == DIR_W) std::swap(w, h);
2538 TileArea airport_area = TileArea(tile, w, h);
2539
2541 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2542 }
2543
2544 AirportTileTableIterator tile_iter(as->layouts[layout].tiles, tile);
2545 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2546 if (cost.Failed()) return cost;
2547
2548 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2549 uint dist;
2550 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2551 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2552
2553 /* Check if local auth would allow a new airport */
2554 StringID authority_refuse_message = STR_NULL;
2555 Town *authority_refuse_town = nullptr;
2556
2558 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2559 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2560 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2561 authority_refuse_town = nearest;
2562 }
2563 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2564 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2565 uint num = 0;
2566 for (const Station *st : Station::Iterate()) {
2567 if (st->town == t && st->facilities.Test(StationFacility::Airport) && st->airport.type != AT_OILRIG) num++;
2568 }
2569 if (num >= 2) {
2570 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2571 authority_refuse_town = t;
2572 }
2573 }
2574
2575 if (authority_refuse_message != STR_NULL) {
2576 return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index);
2577 }
2578
2579 Station *st = nullptr;
2580 ret = FindJoiningStation(StationID::Invalid(), station_to_join, allow_adjacent, airport_area, &st);
2581 if (ret.Failed()) return ret;
2582
2583 /* Distant join */
2584 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2585
2586 ret = BuildStationPart(&st, flags, reuse, airport_area, GetAirport(airport_type)->flags.Test(AirportFTAClass::Flag::Airplanes) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT);
2587 if (ret.Failed()) return ret;
2588
2589 if (st != nullptr && st->airport.tile != INVALID_TILE) {
2590 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2591 }
2592
2593 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2594 cost.AddCost(_price[PR_BUILD_STATION_AIRPORT]);
2595 }
2596
2597 if (flags.Test(DoCommandFlag::Execute)) {
2598 /* Always add the noise, so there will be no need to recalculate when option toggles */
2599 nearest->noise_reached += newnoise_level;
2600
2602 st->airport.type = airport_type;
2603 st->airport.layout = layout;
2604 st->airport.blocks = {};
2605 st->airport.rotation = rotation;
2606
2607 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
2608
2609 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2610 Tile t(iter);
2611 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID);
2612 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2613 st->airport.Add(iter);
2614
2616 }
2617
2618 /* Only call the animation trigger after all tiles have been built */
2619 for (AirportTileTableIterator iter(as->layouts[layout].tiles, tile); iter != INVALID_TILE; ++iter) {
2620 TriggerAirportTileAnimation(st, iter, AirportAnimationTrigger::Built);
2621 }
2622
2624
2625 Company::Get(st->owner)->infrastructure.airport++;
2626
2627 st->AfterStationTileSetChange(true, StationType::Airport);
2629
2632 }
2633 }
2634
2635 return cost;
2636}
2637
2645{
2646 Station *st = Station::GetByTile(tile);
2647
2649 CommandCost ret = CheckOwnership(st->owner);
2650 if (ret.Failed()) return ret;
2651 }
2652
2653 tile = st->airport.tile;
2654
2656
2657 for (const Aircraft *a : Aircraft::Iterate()) {
2658 if (!a->IsNormalAircraft()) continue;
2659 if (a->targetairport == st->index && a->state != FLYING) {
2660 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2661 }
2662 }
2663
2664 if (flags.Test(DoCommandFlag::Execute)) {
2665 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2666 TileIndex tile_cur = st->airport.GetHangarTile(i);
2667 OrderBackup::Reset(tile_cur, false);
2669 }
2670
2671 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2672 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2673 * need of recalculation */
2674 uint dist;
2675 Town *nearest = AirportGetNearestTown(st, dist);
2677
2680 }
2681 }
2682
2683 for (TileIndex tile_cur : st->airport) {
2684 if (!st->TileBelongsToAirport(tile_cur)) continue;
2685
2686 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2687 if (ret.Failed()) return ret;
2688
2689 cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
2690
2691 if (flags.Test(DoCommandFlag::Execute)) {
2692 DoClearSquare(tile_cur);
2693 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur.base());
2694 }
2695 }
2696
2697 if (flags.Test(DoCommandFlag::Execute)) {
2698 /* Clear the persistent storage. */
2699 delete st->airport.psa;
2700
2701 st->rect.AfterRemoveRect(st, st->airport);
2702
2703 st->airport.Clear();
2706
2708
2709 Company::Get(st->owner)->infrastructure.airport--;
2710
2711 st->AfterStationTileSetChange(false, StationType::Airport);
2712
2713 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
2714 }
2715
2716 return cost;
2717}
2718
2726{
2727 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2728 Station *st = Station::Get(station_id);
2729
2731
2732 CommandCost ret = CheckOwnership(st->owner);
2733 if (ret.Failed()) return ret;
2734
2735 if (flags.Test(DoCommandFlag::Execute)) {
2738 }
2739 return CommandCost();
2740}
2741
2748bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2749{
2750 for (const OrderList *orderlist : OrderList::Iterate()) {
2751 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2752 assert(v != nullptr);
2753 if ((v->owner == company) != include_company) continue;
2754
2755 for (const Order &order : orderlist->GetOrders()) {
2756 if (order.GetDestination() == station && (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT))) {
2757 return true;
2758 }
2759 }
2760 }
2761 return false;
2762}
2763
2764static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
2765 {-1, 0},
2766 { 0, 0},
2767 { 0, 0},
2768 { 0, -1}
2769};
2770static const uint8_t _dock_w_chk[4] = { 2, 1, 2, 1 };
2771static const uint8_t _dock_h_chk[4] = { 1, 2, 1, 2 };
2772
2781CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
2782{
2783 bool reuse = (station_to_join != NEW_STATION);
2784 if (!reuse) station_to_join = StationID::Invalid();
2785 bool distant_join = (station_to_join != StationID::Invalid());
2786
2787 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2788
2790 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2791 direction = ReverseDiagDir(direction);
2792
2793 /* Docks cannot be placed on rapids */
2794 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2795
2797 if (ret.Failed()) return ret;
2798
2799 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
2800
2801 CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
2802 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
2803 if (ret.Failed()) return ret;
2804 cost.AddCost(ret.GetCost());
2805
2806 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2807
2808 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2809 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2810 }
2811
2812 if (IsBridgeAbove(tile_cur)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
2813
2814 /* Get the water class of the water tile before it is cleared.*/
2815 WaterClass wc = GetWaterClass(tile_cur);
2816
2817 bool add_cost = !IsWaterTile(tile_cur);
2818 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
2819 if (ret.Failed()) return ret;
2820 if (add_cost) cost.AddCost(ret.GetCost());
2821
2822 tile_cur += TileOffsByDiagDir(direction);
2823 if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) {
2824 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2825 }
2826
2827 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2828 _dock_w_chk[direction], _dock_h_chk[direction]);
2829
2830 /* middle */
2831 Station *st = nullptr;
2832 ret = FindJoiningStation(StationID::Invalid(), station_to_join, adjacent, dock_area, &st);
2833 if (ret.Failed()) return ret;
2834
2835 /* Distant join */
2836 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2837
2838 ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
2839 if (ret.Failed()) return ret;
2840
2841 if (flags.Test(DoCommandFlag::Execute)) {
2842 st->ship_station.Add(tile);
2843 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2844 st->ship_station.Add(flat_tile);
2846
2847 st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
2848
2849 /* If the water part of the dock is on a canal, update infrastructure counts.
2850 * This is needed as we've cleared that tile before.
2851 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2852 * See: MakeWaterKeepingClass() */
2853 if (wc == WATER_CLASS_CANAL && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WATER_CLASS_CANAL && IsTileOwner(flat_tile, _current_company))) {
2854 Company::Get(st->owner)->infrastructure.water++;
2855 }
2856 Company::Get(st->owner)->infrastructure.station += 2;
2857
2858 MakeDock(tile, st->owner, st->index, direction, wc);
2859 UpdateStationDockingTiles(st);
2860
2861 st->AfterStationTileSetChange(true, StationType::Dock);
2862 }
2863
2864 return cost;
2865}
2866
2867void RemoveDockingTile(TileIndex t)
2868{
2869 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2870 TileIndex tile = t + TileOffsByDiagDir(d);
2871 if (!IsValidTile(tile)) continue;
2872
2873 if (IsTileType(tile, MP_STATION)) {
2874 Station *st = Station::GetByTile(tile);
2875 if (st != nullptr) UpdateStationDockingTiles(st);
2876 } else if (IsTileType(tile, MP_INDUSTRY)) {
2878 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
2879 }
2880 }
2881}
2882
2889{
2890 assert(IsValidTile(tile));
2891
2892 /* Clear and maybe re-set docking tile */
2893 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2894 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
2895 if (!IsValidTile(docking_tile)) continue;
2896
2897 if (IsPossibleDockingTile(docking_tile)) {
2898 SetDockingTile(docking_tile, false);
2899 CheckForDockingTile(docking_tile);
2900 }
2901 }
2902}
2903
2910{
2911 assert(IsDockTile(t));
2912
2913 StationGfx gfx = GetStationGfx(t);
2914 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
2915
2916 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2917 TileIndex tile = t + TileOffsByDiagDir(d);
2918 if (!IsValidTile(tile)) continue;
2919 if (!IsDockTile(tile)) continue;
2920 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
2921 }
2922
2923 return INVALID_TILE;
2924}
2925
2933{
2934 Station *st = Station::GetByTile(tile);
2935 CommandCost ret = CheckOwnership(st->owner);
2936 if (ret.Failed()) return ret;
2937
2938 if (!IsDockTile(tile)) return CMD_ERROR;
2939
2940 TileIndex tile1 = FindDockLandPart(tile);
2941 if (tile1 == INVALID_TILE) return CMD_ERROR;
2942 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
2943
2944 ret = EnsureNoVehicleOnGround(tile1);
2945 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
2946 if (ret.Failed()) return ret;
2947
2948 if (flags.Test(DoCommandFlag::Execute)) {
2949 DoClearSquare(tile1);
2950 MarkTileDirtyByTile(tile1);
2951 MakeWaterKeepingClass(tile2, st->owner);
2952
2953 st->rect.AfterRemoveTile(st, tile1);
2954 st->rect.AfterRemoveTile(st, tile2);
2955
2956 MakeShipStationAreaSmaller(st);
2957 if (st->ship_station.tile == INVALID_TILE) {
2958 st->ship_station.Clear();
2959 st->docking_station.Clear();
2962 }
2963
2964 Company::Get(st->owner)->infrastructure.station -= 2;
2965
2966 st->AfterStationTileSetChange(false, StationType::Dock);
2967
2970
2971 for (Ship *s : Ship::Iterate()) {
2972 /* Find all ships going to our dock. */
2973 if (s->current_order.GetDestination() != st->index) {
2974 continue;
2975 }
2976
2977 /* Find ships that are marked as "loading" but are no longer on a
2978 * docking tile. Force them to leave the station (as they were loading
2979 * on the removed dock). */
2980 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
2981 s->LeaveStation();
2982 }
2983
2984 /* If we no longer have a dock, mark the order as invalid and send
2985 * the ship to the next order (or, if there is none, make it
2986 * wander the world). */
2987 if (s->current_order.IsType(OT_GOTO_STATION) && !st->facilities.Test(StationFacility::Dock)) {
2988 s->SetDestTile(s->GetOrderStationLocation(st->index));
2989 }
2990 }
2991 }
2992
2993 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
2994}
2995
2996#include "table/station_land.h"
2997
3005{
3006 const auto &layouts = _station_display_datas[to_underlying(st)];
3007 if (gfx >= layouts.size()) gfx &= 1;
3008 return layouts.data() + gfx;
3009}
3010
3020bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3021{
3022 bool snow_desert;
3023 switch (*ground) {
3024 case SPR_RAIL_TRACK_X:
3025 case SPR_MONO_TRACK_X:
3026 case SPR_MGLV_TRACK_X:
3027 snow_desert = false;
3028 *overlay_offset = RTO_X;
3029 break;
3030
3031 case SPR_RAIL_TRACK_Y:
3032 case SPR_MONO_TRACK_Y:
3033 case SPR_MGLV_TRACK_Y:
3034 snow_desert = false;
3035 *overlay_offset = RTO_Y;
3036 break;
3037
3038 case SPR_RAIL_TRACK_X_SNOW:
3039 case SPR_MONO_TRACK_X_SNOW:
3040 case SPR_MGLV_TRACK_X_SNOW:
3041 snow_desert = true;
3042 *overlay_offset = RTO_X;
3043 break;
3044
3045 case SPR_RAIL_TRACK_Y_SNOW:
3046 case SPR_MONO_TRACK_Y_SNOW:
3047 case SPR_MGLV_TRACK_Y_SNOW:
3048 snow_desert = true;
3049 *overlay_offset = RTO_Y;
3050 break;
3051
3052 default:
3053 return false;
3054 }
3055
3056 if (ti != nullptr) {
3057 /* Decide snow/desert from tile */
3059 case LandscapeType::Arctic:
3060 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3061 break;
3062
3063 case LandscapeType::Tropic:
3064 snow_desert = GetTropicZone(ti->tile) == TROPICZONE_DESERT;
3065 break;
3066
3067 default:
3068 break;
3069 }
3070 }
3071
3072 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3073 return true;
3074}
3075
3076static void DrawTile_Station(TileInfo *ti)
3077{
3078 const NewGRFSpriteLayout *layout = nullptr;
3079 SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_layout and t
3080 DrawTileSpriteSpan tmp_layout;
3081 const DrawTileSprites *t = nullptr;
3082 int32_t total_offset;
3083 const RailTypeInfo *rti = nullptr;
3084 uint32_t relocation = 0;
3085 uint32_t ground_relocation = 0;
3086 BaseStation *st = nullptr;
3087 const StationSpec *statspec = nullptr;
3088 uint tile_layout = 0;
3089
3090 if (HasStationRail(ti->tile)) {
3091 rti = GetRailTypeInfo(GetRailType(ti->tile));
3092 total_offset = rti->GetRailtypeSpriteOffset();
3093
3094 if (IsCustomStationSpecIndex(ti->tile)) {
3095 /* look for customization */
3096 st = BaseStation::GetByTile(ti->tile);
3097 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3098
3099 if (statspec != nullptr) {
3100 tile_layout = GetStationGfx(ti->tile);
3101
3103 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3104 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
3105 }
3106
3107 /* Ensure the chosen tile layout is valid for this custom station */
3108 if (!statspec->renderdata.empty()) {
3109 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3110 if (!layout->NeedsPreprocessing()) {
3111 t = layout;
3112 layout = nullptr;
3113 }
3114 }
3115 }
3116 }
3117 } else {
3118 total_offset = 0;
3119 }
3120
3121 StationGfx gfx = GetStationGfx(ti->tile);
3122 if (IsAirport(ti->tile)) {
3123 gfx = GetAirportGfx(ti->tile);
3124 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3125 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3126 if (ats->grf_prop.HasSpriteGroups() && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3127 return;
3128 }
3129 /* No sprite group (or no valid one) found, meaning no graphics associated.
3130 * Use the substitute one instead */
3131 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3132 gfx = ats->grf_prop.subst_id;
3133 }
3134 switch (gfx) {
3135 case APT_RADAR_GRASS_FENCE_SW:
3136 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3137 break;
3138 case APT_GRASS_FENCE_NE_FLAG:
3139 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3140 break;
3141 case APT_RADAR_FENCE_SW:
3142 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3143 break;
3144 case APT_RADAR_FENCE_NE:
3145 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3146 break;
3147 case APT_GRASS_FENCE_NE_FLAG_2:
3148 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3149 break;
3150 }
3151 }
3152
3153 Owner owner = GetTileOwner(ti->tile);
3154
3155 PaletteID palette;
3156 if (Company::IsValidID(owner)) {
3157 palette = GetCompanyPalette(owner);
3158 } else {
3159 /* Some stations are not owner by a company, namely oil rigs */
3160 palette = PALETTE_TO_GREY;
3161 }
3162
3163 if (layout == nullptr && (t == nullptr || t->GetSequence().empty())) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3164
3165 /* don't show foundation for docks */
3166 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3167 if (statspec != nullptr && statspec->flags.Test(StationSpecFlag::CustomFoundations)) {
3168 /* Station has custom foundations.
3169 * Check whether the foundation continues beyond the tile's upper sides. */
3170 uint edge_info = 0;
3171 auto [slope, z] = GetFoundationPixelSlope(ti->tile);
3172 if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
3173 if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
3174 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, tile_layout, edge_info);
3175 if (image == 0) goto draw_default_foundation;
3176
3178 /* Station provides extended foundations. */
3179
3180 static const uint8_t foundation_parts[] = {
3181 0, 0, 0, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3182 0, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3183 0, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3184 7, 8, 9 // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3185 };
3186
3187 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
3188 } else {
3189 /* Draw simple foundations, built up from 8 possible foundation sprites. */
3190
3191 /* Each set bit represents one of the eight composite sprites to be drawn.
3192 * 'Invalid' entries will not drawn but are included for completeness. */
3193 static const uint8_t composite_foundation_parts[] = {
3194 /* Invalid (00000000), Invalid (11010001), Invalid (11100100), SLOPE_SW (11100000) */
3195 0x00, 0xD1, 0xE4, 0xE0,
3196 /* Invalid (11001010), SLOPE_EW (11001001), SLOPE_SE (11000100), SLOPE_WSE (11000000) */
3197 0xCA, 0xC9, 0xC4, 0xC0,
3198 /* Invalid (11010010), SLOPE_NW (10010001), SLOPE_NS (11100100), SLOPE_NWS (10100000) */
3199 0xD2, 0x91, 0xE4, 0xA0,
3200 /* SLOPE_NE (01001010), SLOPE_ENW (00001001), SLOPE_SEN (01000100) */
3201 0x4A, 0x09, 0x44
3202 };
3203
3204 uint8_t parts = composite_foundation_parts[ti->tileh];
3205
3206 /* If foundations continue beyond the tile's upper sides then
3207 * mask out the last two pieces. */
3208 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3209 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3210
3211 if (parts == 0) {
3212 /* We always have to draw at least one sprite to make sure there is a boundingbox and a sprite with the
3213 * correct offset for the childsprites.
3214 * So, draw the (completely empty) sprite of the default foundations. */
3215 goto draw_default_foundation;
3216 }
3217
3219 for (int i = 0; i < 8; i++) {
3220 if (HasBit(parts, i)) {
3221 AddSortableSpriteToDraw(image + i, PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
3222 }
3223 }
3225 }
3226
3227 OffsetGroundSprite(0, -8);
3229 } else {
3230draw_default_foundation:
3232 }
3233 }
3234
3235 bool draw_ground = false;
3236
3237 if (IsBuoy(ti->tile)) {
3238 DrawWaterClassGround(ti);
3239 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
3240 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3241 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3242 if (ti->tileh == SLOPE_FLAT) {
3243 DrawWaterClassGround(ti);
3244 } else {
3245 assert(IsDock(ti->tile));
3246 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3247 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WATER_CLASS_INVALID;
3248 if (wc == WATER_CLASS_SEA) {
3249 DrawShoreTile(ti->tileh);
3250 } else {
3251 DrawClearLandTile(ti, 3);
3252 }
3253 }
3254 } else if (IsRoadWaypointTile(ti->tile)) {
3256 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3257 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3258 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3259 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3260 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3261 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3262
3263 if (ti->tileh != SLOPE_FLAT) {
3265 }
3266
3267 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3268 } else {
3269 if (layout != nullptr) {
3270 /* Sprite layout which needs preprocessing */
3271 bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround);
3272 processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3273 GetCustomStationRelocation(processor, statspec, st, ti->tile);
3274 tmp_layout = processor.GetLayout();
3275 t = &tmp_layout;
3276 total_offset = 0;
3277 } else if (statspec != nullptr) {
3278 /* Simple sprite layout */
3279 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3281 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3282 }
3283 ground_relocation += rti->fallback_railtype;
3284 }
3285
3286 draw_ground = true;
3287 }
3288
3289 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3290 SpriteID image = t->ground.sprite;
3291 PaletteID pal = t->ground.pal;
3292 RailTrackOffset overlay_offset;
3293 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3294 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
3295 DrawGroundSprite(image, PAL_NONE);
3296 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3297
3298 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3299 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
3300 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3301 }
3302 } else {
3303 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3304 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3305 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3306
3307 /* PBS debugging, draw reserved tracks darker */
3308 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3309 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
3310 }
3311 }
3312 }
3313
3315
3316 if (IsAnyRoadStop(ti->tile)) {
3317 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3318 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3319 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3320 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3321
3322 StationGfx view = GetStationGfx(ti->tile);
3323 StationType type = GetStationType(ti->tile);
3324
3325 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3326 RoadStopDrawModes stop_draw_mode{};
3327 if (stopspec != nullptr) {
3328 stop_draw_mode = stopspec->draw_mode;
3329 st = BaseStation::GetByTile(ti->tile);
3330 std::array<int32_t, 1> regs100;
3331 auto result = GetRoadStopLayout(ti, stopspec, st, type, view, regs100);
3332 if (result.has_value()) {
3333 if (stopspec->flags.Test(RoadStopSpecFlag::DrawModeRegister)) {
3334 stop_draw_mode = static_cast<RoadStopDrawMode>(regs100[0]);
3335 }
3336 if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) {
3337 draw_ground = true;
3338 }
3339 processor = std::move(*result);
3340 tmp_layout = processor.GetLayout();
3341 t = &tmp_layout;
3342 }
3343 }
3344
3345 /* Draw ground sprite */
3346 if (draw_ground) {
3347 SpriteID image = t->ground.sprite;
3348 PaletteID pal = t->ground.pal;
3349 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3350 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3351 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3352 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3353 }
3354 }
3355
3356 if (IsDriveThroughStopTile(ti->tile)) {
3357 if (type != StationType::RoadWaypoint && (stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Overlay))) {
3358 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
3359 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3360 }
3361 } else {
3362 /* Non-drivethrough road stops are only valid for roads. */
3363 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3364
3365 if ((stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Road)) && road_rti->UsesOverlay()) {
3366 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
3367 DrawGroundSprite(ground + view, PAL_NONE);
3368 }
3369 }
3370
3371 if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
3372 /* Draw road, tram catenary */
3373 DrawRoadCatenary(ti);
3374 }
3375 }
3376
3377 if (IsRailWaypoint(ti->tile)) {
3378 /* Don't offset the waypoint graphics; they're always the same. */
3379 total_offset = 0;
3380 }
3381
3382 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
3383}
3384
3385void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3386{
3387 int32_t total_offset = 0;
3389 const DrawTileSprites *t = GetStationTileLayout(st, image);
3390 const RailTypeInfo *railtype_info = nullptr;
3391
3392 if (railtype != INVALID_RAILTYPE) {
3393 railtype_info = GetRailTypeInfo(railtype);
3394 total_offset = railtype_info->GetRailtypeSpriteOffset();
3395 }
3396
3397 SpriteID img = t->ground.sprite;
3398 RailTrackOffset overlay_offset;
3399 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3400 SpriteID ground = GetCustomRailSprite(railtype_info, INVALID_TILE, RTSG_GROUND);
3401 DrawSprite(img, PAL_NONE, x, y);
3402 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3403 } else {
3404 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3405 }
3406
3407 if (roadtype != INVALID_ROADTYPE) {
3408 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3409 if (image >= 4) {
3410 /* Drive-through stop */
3411 uint sprite_offset = 5 - image;
3412
3413 /* Road underlay takes precedence over tram */
3414 if (roadtype_info->UsesOverlay()) {
3415 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_GROUND);
3416 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3417
3418 SpriteID overlay = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_OVERLAY);
3419 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3420 } else if (RoadTypeIsTram(roadtype)) {
3421 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3422 }
3423 } else {
3424 /* Bay stop */
3425 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3426 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_ROADSTOP);
3427 DrawSprite(ground + image, PAL_NONE, x, y);
3428 }
3429 }
3430 }
3431
3432 /* Default waypoint has no railtype specific sprites */
3433 DrawRailTileSeqInGUI(x, y, t, (st == StationType::RailWaypoint || st == StationType::RoadWaypoint) ? 0 : total_offset, 0, pal);
3434}
3435
3436static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)
3437{
3438 return GetTileMaxPixelZ(tile);
3439}
3440
3441static Foundation GetFoundation_Station(TileIndex, Slope tileh)
3442{
3443 return FlatteningFoundation(tileh);
3444}
3445
3446static void FillTileDescRoadStop(TileIndex tile, TileDesc &td)
3447{
3448 RoadType road_rt = GetRoadTypeRoad(tile);
3449 RoadType tram_rt = GetRoadTypeTram(tile);
3450 Owner road_owner = INVALID_OWNER;
3451 Owner tram_owner = INVALID_OWNER;
3452 if (road_rt != INVALID_ROADTYPE) {
3453 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3454 td.roadtype = rti->strings.name;
3455 td.road_speed = rti->max_speed / 2;
3456 road_owner = GetRoadOwner(tile, RTT_ROAD);
3457 }
3458
3459 if (tram_rt != INVALID_ROADTYPE) {
3460 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3461 td.tramtype = rti->strings.name;
3462 td.tram_speed = rti->max_speed / 2;
3463 tram_owner = GetRoadOwner(tile, RTT_TRAM);
3464 }
3465
3466 if (IsDriveThroughStopTile(tile)) {
3467 /* Is there a mix of owners? */
3468 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
3469 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
3470 uint i = 1;
3471 if (road_owner != INVALID_OWNER) {
3472 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3473 td.owner[i] = road_owner;
3474 i++;
3475 }
3476 if (tram_owner != INVALID_OWNER) {
3477 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3478 td.owner[i] = tram_owner;
3479 }
3480 }
3481 }
3482}
3483
3484void FillTileDescRailStation(TileIndex tile, TileDesc &td)
3485{
3486 const StationSpec *spec = GetStationSpec(tile);
3487
3488 if (spec != nullptr) {
3490 td.station_name = spec->name;
3491
3492 if (spec->grf_prop.HasGrfFile()) {
3493 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3494 td.grf = gc->GetName();
3495 }
3496 }
3497
3498 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3499 td.rail_speed = rti->max_speed;
3500 td.railtype = rti->strings.name;
3501}
3502
3503void FillTileDescAirport(TileIndex tile, TileDesc &td)
3504{
3505 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3507 td.airport_name = as->name;
3508
3510 td.airport_tile_name = ats->name;
3511
3512 if (as->grf_prop.HasGrfFile()) {
3513 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3514 td.grf = gc->GetName();
3515 } else if (ats->grf_prop.HasGrfFile()) {
3516 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3517 td.grf = gc->GetName();
3518 }
3519}
3520
3521static void GetTileDesc_Station(TileIndex tile, TileDesc &td)
3522{
3523 td.owner[0] = GetTileOwner(tile);
3525
3526 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3527 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3528 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3529
3530 StringID str;
3531 switch (GetStationType(tile)) {
3532 default: NOT_REACHED();
3533 case StationType::Rail: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3534 case StationType::Airport:
3535 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3536 break;
3537 case StationType::Truck: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3538 case StationType::Bus: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3539 case StationType::Oilrig: {
3540 const Industry *i = Station::GetByTile(tile)->industry;
3541 const IndustrySpec *is = GetIndustrySpec(i->type);
3542 td.owner[0] = i->owner;
3543 str = is->name;
3544 if (is->grf_prop.HasGrfFile()) td.grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3545 break;
3546 }
3547 case StationType::Dock: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3548 case StationType::Buoy: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3549 case StationType::RailWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3550 case StationType::RoadWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3551 }
3552 td.str = str;
3553}
3554
3555
3556static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
3557{
3558 TrackBits trackbits = TRACK_BIT_NONE;
3559
3560 switch (mode) {
3561 case TRANSPORT_RAIL:
3562 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3563 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
3564 }
3565 break;
3566
3567 case TRANSPORT_WATER:
3568 /* buoy is coded as a station, it is always on open water */
3569 if (IsBuoy(tile)) {
3570 trackbits = TRACK_BIT_ALL;
3571 /* remove tracks that connect NE map edge */
3572 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
3573 /* remove tracks that connect NW map edge */
3574 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
3575 }
3576 break;
3577
3578 case TRANSPORT_ROAD:
3579 if (IsAnyRoadStop(tile)) {
3580 RoadTramType rtt = (RoadTramType)sub_mode;
3581 if (!HasTileRoadType(tile, rtt)) break;
3582
3583 if (IsBayRoadStopTile(tile)) {
3584 DiagDirection dir = GetBayRoadStopDir(tile);
3585 if (side != INVALID_DIAGDIR && dir != side) break;
3586 trackbits = DiagDirToDiagTrackBits(dir);
3587 } else {
3588 Axis axis = GetDriveThroughStopAxis(tile);
3589 if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
3590 trackbits = AxisToTrackBits(axis);
3591 }
3592 }
3593 break;
3594
3595 default:
3596 break;
3597 }
3598
3600}
3601
3602
3603static void TileLoop_Station(TileIndex tile)
3604{
3605 auto *st = BaseStation::GetByTile(tile);
3606 switch (GetStationType(tile)) {
3607 case StationType::Airport:
3608 TriggerAirportTileAnimation(Station::From(st), tile, AirportAnimationTrigger::TileLoop);
3609 break;
3610
3611 case StationType::Rail:
3612 case StationType::RailWaypoint:
3613 TriggerStationAnimation(st, tile, StationAnimationTrigger::TileLoop);
3614 break;
3615
3616 case StationType::Dock:
3617 if (!IsTileFlat(tile)) break; // only handle water part
3618 [[fallthrough]];
3619
3620 case StationType::Oilrig: //(station part)
3621 case StationType::Buoy:
3622 TileLoop_Water(tile);
3623 break;
3624
3625 case StationType::Truck:
3626 case StationType::Bus:
3627 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3628 break;
3629
3630 case StationType::RoadWaypoint: {
3632 case LandscapeType::Arctic:
3633 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3635 MarkTileDirtyByTile(tile);
3636 }
3637 break;
3638
3639 case LandscapeType::Tropic:
3642 MarkTileDirtyByTile(tile);
3643 }
3644 break;
3645
3646 default: break;
3647 }
3648
3649 HouseZone new_zone = HouseZone::TownEdge;
3650 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3651 if (t != nullptr) {
3652 new_zone = GetTownRadiusGroup(t, tile);
3653 }
3654
3655 /* Adjust road ground type depending on 'new_zone' */
3656 Roadside new_rs = new_zone != HouseZone::TownEdge ? ROADSIDE_PAVED : ROADSIDE_GRASS;
3657 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3658
3659 if (new_rs != cur_rs) {
3660 SetRoadWaypointRoadside(tile, cur_rs == ROADSIDE_BARREN ? new_rs : ROADSIDE_BARREN);
3661 MarkTileDirtyByTile(tile);
3662 }
3663
3664 TriggerRoadStopAnimation(st, tile, StationAnimationTrigger::TileLoop);
3665 break;
3666 }
3667
3668 default: break;
3669 }
3670}
3671
3672
3673static void AnimateTile_Station(TileIndex tile)
3674{
3675 if (HasStationRail(tile)) {
3676 AnimateStationTile(tile);
3677 return;
3678 }
3679
3680 if (IsAirport(tile)) {
3681 AnimateAirportTile(tile);
3682 return;
3683 }
3684
3685 if (IsAnyRoadStopTile(tile)) {
3686 AnimateRoadStopTile(tile);
3687 return;
3688 }
3689}
3690
3691
3692static bool ClickTile_Station(TileIndex tile)
3693{
3694 const BaseStation *bst = BaseStation::GetByTile(tile);
3695
3698 } else if (IsHangar(tile)) {
3699 const Station *st = Station::From(bst);
3701 } else {
3703 }
3704 return true;
3705}
3706
3707static VehicleEnterTileStates VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
3708{
3709 if (v->type == VEH_TRAIN) {
3710 StationID station_id = GetStationIndex(tile);
3711 if (!v->current_order.ShouldStopAtStation(v, station_id)) return {};
3712 if (!IsRailStation(tile) || !v->IsFrontEngine()) return {};
3713
3714 int station_ahead;
3715 int station_length;
3716 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3717
3718 /* Stop whenever that amount of station ahead + the distance from the
3719 * begin of the platform to the stop location is longer than the length
3720 * of the platform. Station ahead 'includes' the current tile where the
3721 * vehicle is on, so we need to subtract that. */
3722 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return {};
3723
3725
3726 x &= 0xF;
3727 y &= 0xF;
3728
3729 if (DiagDirToAxis(dir) != AXIS_X) std::swap(x, y);
3730 if (y == TILE_SIZE / 2) {
3731 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
3732 stop &= TILE_SIZE - 1;
3733
3734 if (x == stop) {
3735 return VehicleEnterTileState::EnteredStation; // enter station
3736 } else if (x < stop) {
3738 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3739 if (spd < v->cur_speed) v->cur_speed = spd;
3740 }
3741 }
3742 } else if (v->type == VEH_ROAD) {
3744 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
3745 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3746 /* Attempt to allocate a parking bay in a road stop */
3747 if (RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv)) return {};
3749 }
3750 }
3751 }
3752
3753 return {};
3754}
3755
3761{
3762 /* Collect cargoes accepted since the last big tick. */
3763 CargoTypes cargoes = 0;
3764 for (CargoType cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
3765 if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) SetBit(cargoes, cargo_type);
3766 }
3767
3768 /* Anything to do? */
3769 if (cargoes == 0) return;
3770
3771 /* Loop over all houses in the catchment. */
3773 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3774 if (IsTileType(tile, MP_HOUSE)) {
3776 }
3777 }
3778}
3779
3787{
3788 if (!st->IsInUse()) {
3789 if (++st->delete_ctr >= 8) delete st;
3790 return false;
3791 }
3792
3793 if (Station::IsExpected(st)) {
3795
3796 for (GoodsEntry &ge : Station::From(st)->goods) {
3797 ge.status.Reset(GoodsEntry::State::AcceptedBigtick);
3798 }
3799 }
3800
3801
3803
3804 return true;
3805}
3806
3807static inline void byte_inc_sat(uint8_t *p)
3808{
3809 uint8_t b = *p + 1;
3810 if (b != 0) *p = b;
3811}
3812
3819static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3820{
3821 /* If truncating also punish the source stations' ratings to
3822 * decrease the flow of incoming cargo. */
3823
3824 if (!ge->HasData()) return;
3825
3826 StationCargoAmountMap waiting_per_source;
3827 ge->GetData().cargo.Truncate(amount, &waiting_per_source);
3828 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3829 Station *source_station = Station::GetIfValid(i->first);
3830 if (source_station == nullptr) continue;
3831
3832 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3833 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3834 }
3835}
3836
3837static void UpdateStationRating(Station *st)
3838{
3839 bool waiting_changed = false;
3840
3841 byte_inc_sat(&st->time_since_load);
3842 byte_inc_sat(&st->time_since_unload);
3843
3844 for (const CargoSpec *cs : CargoSpec::Iterate()) {
3845 GoodsEntry *ge = &st->goods[cs->Index()];
3846 /* Slowly increase the rating back to its original level in the case we
3847 * didn't deliver cargo yet to this station. This happens when a bribe
3848 * failed while you didn't moved that cargo yet to a station. */
3849 if (!ge->HasRating() && ge->rating < INITIAL_STATION_RATING) {
3850 ge->rating++;
3851 }
3852
3853 /* Only change the rating if we are moving this cargo */
3854 if (ge->HasRating()) {
3855 byte_inc_sat(&ge->time_since_pickup);
3858 ge->last_speed = 0;
3859 TruncateCargo(cs, ge);
3860 waiting_changed = true;
3861 continue;
3862 }
3863
3864 bool skip = false;
3865 int rating = 0;
3866 uint waiting = ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0;
3867
3868 /* num_dests is at least 1 if there is any cargo as
3869 * StationID::Invalid() is also a destination.
3870 */
3871 uint num_dests = ge->HasData() ? static_cast<uint>(ge->GetData().cargo.Packets()->MapSize()) : 0;
3872
3873 /* Average amount of cargo per next hop, but prefer solitary stations
3874 * with only one or two next hops. They are allowed to have more
3875 * cargo waiting per next hop.
3876 * With manual cargo distribution waiting_avg = waiting / 2 as then
3877 * StationID::Invalid() is the only destination.
3878 */
3879 uint waiting_avg = waiting / (num_dests + 1);
3880
3882 ge->rating = rating = MAX_STATION_RATING;
3883 skip = true;
3884 } else if (cs->callback_mask.Test(CargoCallbackMask::StationRatingCalc)) {
3885 /* Perform custom station rating. If it succeeds the speed, days in transit and
3886 * waiting cargo ratings must not be executed. */
3887
3888 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
3889 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
3890
3891 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
3892 | (ClampTo<uint16_t>(ge->max_waiting_cargo) << 8)
3893 | (ClampTo<uint8_t>(last_speed) << 24);
3894 /* Convert to the 'old' vehicle types */
3895 uint32_t var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
3896 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
3897 if (callback != CALLBACK_FAILED) {
3898 skip = true;
3899 rating = GB(callback, 0, 14);
3900
3901 /* Simulate a 15 bit signed value */
3902 if (HasBit(callback, 14)) rating -= 0x4000;
3903 }
3904 }
3905
3906 if (!skip) {
3907 int b = ge->last_speed - 85;
3908 if (b >= 0) rating += b >> 2;
3909
3910 uint8_t waittime = ge->time_since_pickup;
3911 if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
3912 if (waittime <= 21) rating += 25;
3913 if (waittime <= 12) rating += 25;
3914 if (waittime <= 6) rating += 45;
3915 if (waittime <= 3) rating += 35;
3916
3917 rating -= 90;
3918 if (ge->max_waiting_cargo <= 1500) rating += 55;
3919 if (ge->max_waiting_cargo <= 1000) rating += 35;
3920 if (ge->max_waiting_cargo <= 600) rating += 10;
3921 if (ge->max_waiting_cargo <= 300) rating += 20;
3922 if (ge->max_waiting_cargo <= 100) rating += 10;
3923 }
3924
3925 if (Company::IsValidID(st->owner) && st->town->statues.Test(st->owner)) rating += 26;
3926
3927 uint8_t age = ge->last_age;
3928 if (age < 3) rating += 10;
3929 if (age < 2) rating += 10;
3930 if (age < 1) rating += 13;
3931
3932 {
3933 int or_ = ge->rating; // old rating
3934
3935 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
3936 ge->rating = rating = ClampTo<uint8_t>(or_ + Clamp(rating - or_, -2, 2));
3937
3938 /* if rating is <= 64 and more than 100 items waiting on average per destination,
3939 * remove some random amount of goods from the station */
3940 if (rating <= 64 && waiting_avg >= 100) {
3941 int dec = Random() & 0x1F;
3942 if (waiting_avg < 200) dec &= 7;
3943 waiting -= (dec + 1) * num_dests;
3944 waiting_changed = true;
3945 }
3946
3947 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
3948 if (rating <= 127 && waiting != 0) {
3949 uint32_t r = Random();
3950 if (rating <= (int)GB(r, 0, 7)) {
3951 /* Need to have int, otherwise it will just overflow etc. */
3952 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) - 1) * num_dests), 0);
3953 waiting_changed = true;
3954 }
3955 }
3956
3957 /* At some point we really must cap the cargo. Previously this
3958 * was a strict 4095, but now we'll have a less strict, but
3959 * increasingly aggressive truncation of the amount of cargo. */
3960 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
3961 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
3962 static const uint MAX_WAITING_CARGO = 1 << 15;
3963
3964 if (waiting > WAITING_CARGO_THRESHOLD) {
3965 uint difference = waiting - WAITING_CARGO_THRESHOLD;
3966 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
3967
3968 waiting = std::min(waiting, MAX_WAITING_CARGO);
3969 waiting_changed = true;
3970 }
3971
3972 /* We can't truncate cargo that's already reserved for loading.
3973 * Thus StoredCount() here. */
3974 if (waiting_changed && waiting < (ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0)) {
3975 /* Feed back the exact own waiting cargo at this station for the
3976 * next rating calculation. */
3977 ge->max_waiting_cargo = 0;
3978
3979 TruncateCargo(cs, ge, ge->GetData().cargo.AvailableCount() - waiting);
3980 } else {
3981 /* If the average number per next hop is low, be more forgiving. */
3982 ge->max_waiting_cargo = waiting_avg;
3983 }
3984 }
3985 }
3986 }
3987
3988 StationID index = st->index;
3989 if (waiting_changed) {
3990 SetWindowDirty(WC_STATION_VIEW, index); // update whole window
3991 } else {
3992 SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
3993 }
3994}
3995
4004void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
4005{
4006 GoodsEntry &ge = st->goods[cargo];
4007
4008 /* Reroute cargo in station. */
4009 if (ge.HasData()) ge.GetData().cargo.Reroute(UINT_MAX, &ge.GetData().cargo, avoid, avoid2, &ge);
4010
4011 /* Reroute cargo staged to be transferred. */
4012 for (Vehicle *v : st->loading_vehicles) {
4013 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4014 if (u->cargo_type != cargo) continue;
4015 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4016 }
4017 }
4018}
4019
4029{
4030 for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
4031 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL);
4032 GoodsEntry &ge = from->goods[cargo];
4034 if (lg == nullptr) continue;
4035 std::vector<NodeID> to_remove{};
4036 for (Edge &edge : (*lg)[ge.node].edges) {
4037 Station *to = Station::Get((*lg)[edge.dest_node].station);
4038 assert(to->goods[cargo].node == edge.dest_node);
4039 assert(TimerGameEconomy::date >= edge.LastUpdate());
4041 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4042 bool updated = false;
4043
4044 if (auto_distributed) {
4045 /* Have all vehicles refresh their next hops before deciding to
4046 * remove the node. */
4047 std::vector<Vehicle *> vehicles;
4048 for (const OrderList *l : OrderList::Iterate()) {
4049 bool found_from = false;
4050 bool found_to = false;
4051 for (const Order &order : l->GetOrders()) {
4052 if (!order.IsType(OT_GOTO_STATION) && !order.IsType(OT_IMPLICIT)) continue;
4053 if (order.GetDestination() == from->index) {
4054 found_from = true;
4055 if (found_to) break;
4056 } else if (order.GetDestination() == to->index) {
4057 found_to = true;
4058 if (found_from) break;
4059 }
4060 }
4061 if (!found_to || !found_from) continue;
4062 vehicles.push_back(l->GetFirstSharedVehicle());
4063 }
4064
4065 auto iter = vehicles.begin();
4066 while (iter != vehicles.end()) {
4067 Vehicle *v = *iter;
4068 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4070 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4071 }
4072 if (edge.LastUpdate() == TimerGameEconomy::date) {
4073 updated = true;
4074 break;
4075 }
4076
4077 Vehicle *next_shared = v->NextShared();
4078 if (next_shared) {
4079 *iter = next_shared;
4080 ++iter;
4081 } else {
4082 iter = vehicles.erase(iter);
4083 }
4084
4085 if (iter == vehicles.end()) iter = vehicles.begin();
4086 }
4087 }
4088
4089 if (!updated) {
4090 /* If it's still considered dead remove it. */
4091 to_remove.emplace_back(to->goods[cargo].node);
4092 if (ge.HasData()) ge.GetData().flows.DeleteFlows(to->index);
4093 RerouteCargo(from, cargo, to->index, from->index);
4094 }
4095 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4096 edge.Restrict();
4097 if (ge.HasData()) ge.GetData().flows.RestrictFlows(to->index);
4098 RerouteCargo(from, cargo, to->index, from->index);
4099 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4100 edge.Release();
4101 }
4102 }
4103 /* Remove dead edges. */
4104 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4105
4106 assert(TimerGameEconomy::date >= lg->LastCompression());
4108 lg->Compress();
4109 }
4110 }
4111}
4112
4122void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
4123{
4124 GoodsEntry &ge1 = st->goods[cargo];
4125 Station *st2 = Station::Get(next_station_id);
4126 GoodsEntry &ge2 = st2->goods[cargo];
4127 LinkGraph *lg = nullptr;
4128 if (ge1.link_graph == LinkGraphID::Invalid()) {
4129 if (ge2.link_graph == LinkGraphID::Invalid()) {
4131 lg = new LinkGraph(cargo);
4133 ge2.link_graph = lg->index;
4134 ge2.node = lg->AddNode(st2);
4135 } else {
4136 Debug(misc, 0, "Can't allocate link graph");
4137 }
4138 } else {
4139 lg = LinkGraph::Get(ge2.link_graph);
4140 }
4141 if (lg) {
4142 ge1.link_graph = lg->index;
4143 ge1.node = lg->AddNode(st);
4144 }
4145 } else if (ge2.link_graph == LinkGraphID::Invalid()) {
4146 lg = LinkGraph::Get(ge1.link_graph);
4147 ge2.link_graph = lg->index;
4148 ge2.node = lg->AddNode(st2);
4149 } else {
4150 lg = LinkGraph::Get(ge1.link_graph);
4151 if (ge1.link_graph != ge2.link_graph) {
4153 if (lg->Size() < lg2->Size()) {
4155 lg2->Merge(lg); // Updates GoodsEntries of lg
4156 lg = lg2;
4157 } else {
4159 lg->Merge(lg2); // Updates GoodsEntries of lg2
4160 }
4161 }
4162 }
4163 if (lg != nullptr) {
4164 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, modes);
4165 }
4166}
4167
4174void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4175{
4176 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4177 if (v->refit_cap > 0) {
4178 /* The cargo count can indeed be higher than the refit_cap if
4179 * wagons have been auto-replaced and subsequently auto-
4180 * refitted to a higher capacity. The cargo gets redistributed
4181 * among the wagons in that case.
4182 * As usage is not such an important figure anyway we just
4183 * ignore the additional cargo then.*/
4184 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4185 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EdgeUpdateMode::Increase);
4186 }
4187 }
4188}
4189
4190/* called for every station each tick */
4191static void StationHandleSmallTick(BaseStation *st)
4192{
4193 if (st->facilities.Test(StationFacility::Waypoint) || !st->IsInUse()) return;
4194
4195 uint8_t b = st->delete_ctr + 1;
4196 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4197 st->delete_ctr = b;
4198
4199 if (b == 0) UpdateStationRating(Station::From(st));
4200}
4201
4202void OnTick_Station()
4203{
4204 if (_game_mode == GM_EDITOR) return;
4205
4206 for (BaseStation *st : BaseStation::Iterate()) {
4207 StationHandleSmallTick(st);
4208
4209 /* Clean up the link graph about once a week. */
4212 };
4213
4214 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4216 /* Stop processing this station if it was deleted */
4217 if (!StationHandleBigTick(st)) continue;
4218 }
4219
4220 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4222 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4223 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::AcceptanceTick);
4224 if (Station::IsExpected(st)) TriggerAirportAnimation(Station::From(st), AirportAnimationTrigger::AcceptanceTick);
4225 }
4226 }
4227}
4228
4230static const IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto)
4231{
4232 for (Station *st : Station::Iterate()) {
4233 for (GoodsEntry &ge : st->goods) {
4236 }
4237 }
4238});
4239
4240void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4241{
4242 ForAllStationsRadius(tile, radius, [&](Station *st) {
4243 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4244 for (GoodsEntry &ge : st->goods) {
4245 if (ge.status.Any()) {
4246 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4247 }
4248 }
4249 }
4250 });
4251}
4252
4253static uint UpdateStationWaiting(Station *st, CargoType cargo, uint amount, Source source)
4254{
4255 /* We can't allocate a CargoPacket? Then don't do anything
4256 * at all; i.e. just discard the incoming cargo. */
4257 if (!CargoPacket::CanAllocateItem()) return 0;
4258
4259 GoodsEntry &ge = st->goods[cargo];
4260 amount += ge.amount_fract;
4261 ge.amount_fract = GB(amount, 0, 8);
4262
4263 amount >>= 8;
4264 /* No new "real" cargo item yet. */
4265 if (amount == 0) return 0;
4266
4267 StationID next = ge.GetVia(st->index);
4268 ge.GetOrCreateData().cargo.Append(new CargoPacket(st->index, amount, source), next);
4269 LinkGraph *lg = nullptr;
4270 if (ge.link_graph == LinkGraphID::Invalid()) {
4272 lg = new LinkGraph(cargo);
4274 ge.link_graph = lg->index;
4275 ge.node = lg->AddNode(st);
4276 } else {
4277 Debug(misc, 0, "Can't allocate link graph");
4278 }
4279 } else {
4280 lg = LinkGraph::Get(ge.link_graph);
4281 }
4282 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4283
4284 if (!ge.HasRating()) {
4287 }
4288
4290 TriggerStationAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4291 TriggerAirportAnimation(st, AirportAnimationTrigger::NewCargo, cargo);
4292 TriggerRoadStopRandomisation(st, st->xy, StationRandomTrigger::NewCargo, cargo);
4293 TriggerRoadStopAnimation(st, st->xy, StationAnimationTrigger::NewCargo, cargo);
4294
4295
4297 st->MarkTilesDirty(true);
4298 return amount;
4299}
4300
4301static bool IsUniqueStationName(const std::string &name)
4302{
4303 for (const Station *st : Station::Iterate()) {
4304 if (!st->name.empty() && st->name == name) return false;
4305 }
4306
4307 return true;
4308}
4309
4317CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
4318{
4319 Station *st = Station::GetIfValid(station_id);
4320 if (st == nullptr) return CMD_ERROR;
4321
4322 CommandCost ret = CheckOwnership(st->owner);
4323 if (ret.Failed()) return ret;
4324
4325 bool reset = text.empty();
4326
4327 if (!reset) {
4329 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4330 }
4331
4332 if (flags.Test(DoCommandFlag::Execute)) {
4333 st->cached_name.clear();
4334 if (reset) {
4335 st->name.clear();
4336 } else {
4337 st->name = text;
4338 }
4339
4340 st->UpdateVirtCoord();
4342 }
4343
4344 return CommandCost();
4345}
4346
4347static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4348{
4349 for (Station *st : nearby) {
4350 if (st->TileIsInCatchment(tile)) stations.insert(st);
4351 }
4352}
4353
4359{
4360 if (this->tile != INVALID_TILE) {
4361 if (IsTileType(this->tile, MP_HOUSE)) {
4362 /* Town nearby stations need to be filtered per tile. */
4363 assert(this->w == 1 && this->h == 1);
4364 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4365 } else {
4366 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4367 this->stations.insert(st);
4368 return true;
4369 });
4370 }
4371 this->tile = INVALID_TILE;
4372 }
4373 return this->stations;
4374}
4375
4376
4377static bool CanMoveGoodsToStation(const Station *st, CargoType cargo)
4378{
4379 /* Is the station reserved exclusively for somebody else? */
4380 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4381
4382 /* Lowest possible rating, better not to give cargo anymore. */
4383 if (st->goods[cargo].rating == 0) return false;
4384
4385 /* Selectively servicing stations, and not this one. */
4386 if (_settings_game.order.selectgoods && !st->goods[cargo].HasVehicleEverTriedLoading()) return false;
4387
4389 /* Passengers are never served by just a truck stop. */
4390 if (st->facilities == StationFacility::TruckStop) return false;
4391 } else {
4392 /* Non-passengers are never served by just a bus stop. */
4393 if (st->facilities == StationFacility::BusStop) return false;
4394 }
4395 return true;
4396}
4397
4398uint MoveGoodsToStation(CargoType cargo, uint amount, Source source, const StationList &all_stations, Owner exclusivity)
4399{
4400 /* Return if nothing to do. Also the rounding below fails for 0. */
4401 if (all_stations.empty()) return 0;
4402 if (amount == 0) return 0;
4403
4404 Station *first_station = nullptr;
4405 typedef std::pair<Station *, uint> StationInfo;
4406 std::vector<StationInfo> used_stations;
4407
4408 for (Station *st : all_stations) {
4409 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4410 if (!CanMoveGoodsToStation(st, cargo)) continue;
4411
4412 /* Avoid allocating a vector if there is only one station to significantly
4413 * improve performance in this common case. */
4414 if (first_station == nullptr) {
4415 first_station = st;
4416 continue;
4417 }
4418 if (used_stations.empty()) {
4419 used_stations.reserve(2);
4420 used_stations.emplace_back(first_station, 0);
4421 }
4422 used_stations.emplace_back(st, 0);
4423 }
4424
4425 /* no stations around at all? */
4426 if (first_station == nullptr) return 0;
4427
4428 if (used_stations.empty()) {
4429 /* only one station around */
4430 amount *= first_station->goods[cargo].rating + 1;
4431 return UpdateStationWaiting(first_station, cargo, amount, source);
4432 }
4433
4434 ReferenceThroughBaseContainer<std::array<uint32_t, OWNER_END.base()>> company_best = {}; // best rating for each company, including OWNER_NONE
4435 ReferenceThroughBaseContainer<std::array<uint32_t, OWNER_END.base()>> company_sum = {}; // sum of ratings for each company
4436 uint best_rating = 0;
4437 uint best_sum = 0; // sum of best ratings for each company
4438
4439 for (auto &p : used_stations) {
4440 auto owner = p.first->owner;
4441 auto rating = p.first->goods[cargo].rating;
4442 if (rating > company_best[owner]) {
4443 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4444 company_best[owner] = rating;
4445 if (rating > best_rating) best_rating = rating;
4446 }
4447 company_sum[owner] += rating;
4448 }
4449
4450 /* From now we'll calculate with fractional cargo amounts.
4451 * First determine how much cargo we really have. */
4452 amount *= best_rating + 1;
4453
4454 uint moving = 0;
4455 for (auto &p : used_stations) {
4456 Owner owner = p.first->owner;
4457 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4458 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4459 p.second = amount * company_best[owner] * p.first->goods[cargo].rating / best_sum / company_sum[owner];
4460 moving += p.second;
4461 }
4462
4463 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4464 if (amount > moving) {
4465 std::stable_sort(used_stations.begin(), used_stations.end(), [cargo](const StationInfo &a, const StationInfo &b) {
4466 return b.first->goods[cargo].rating < a.first->goods[cargo].rating;
4467 });
4468
4469 assert(amount - moving <= used_stations.size());
4470 for (uint i = 0; i < amount - moving; i++) {
4471 used_stations[i].second++;
4472 }
4473 }
4474
4475 uint moved = 0;
4476 for (auto &p : used_stations) {
4477 moved += UpdateStationWaiting(p.first, cargo, p.second, source);
4478 }
4479
4480 return moved;
4481}
4482
4483void UpdateStationDockingTiles(Station *st)
4484{
4485 st->docking_station.Clear();
4486
4487 /* For neutral stations, start with the industry area instead of dock area */
4488 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4489
4490 if (area->tile == INVALID_TILE) return;
4491
4492 int x = TileX(area->tile);
4493 int y = TileY(area->tile);
4494
4495 /* Expand the area by a tile on each side while
4496 * making sure that we remain inside the map. */
4497 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4498 int x1 = std::max<int>(x - 1, 0);
4499
4500 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4501 int y1 = std::max<int>(y - 1, 0);
4502
4503 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4504 for (TileIndex tile : ta) {
4505 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4506 }
4507}
4508
4509void BuildOilRig(TileIndex tile)
4510{
4511 if (!Station::CanAllocateItem()) {
4512 Debug(misc, 0, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4513 return;
4514 }
4515
4516 Station *st = new Station(tile);
4517 _station_kdtree.Insert(st->index);
4518 st->town = ClosestTownFromTile(tile, UINT_MAX);
4519
4520 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
4521
4522 assert(IsTileType(tile, MP_INDUSTRY));
4523 /* Mark industry as associated both ways */
4524 st->industry = Industry::GetByTile(tile);
4525 st->industry->neutral_station = st;
4526 DeleteAnimatedTile(tile);
4527 MakeOilrig(tile, st->index, GetWaterClass(tile));
4528
4529 st->owner = OWNER_NONE;
4530 st->airport.type = AT_OILRIG;
4531 st->airport.rotation = DIR_N;
4532 st->airport.Add(tile);
4533 st->ship_station.Add(tile);
4536 UpdateStationDockingTiles(st);
4537
4538 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
4539
4540 st->UpdateVirtCoord();
4541
4542 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4543 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4544 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4546 StationList nearby = std::move(st->industry->stations_near);
4547 st->industry->stations_near.clear();
4548 for (Station *near : nearby) {
4549 near->RecomputeCatchment(true);
4550 UpdateStationAcceptance(near, true);
4551 }
4552 }
4553
4554 st->RecomputeCatchment();
4555 UpdateStationAcceptance(st, false);
4556}
4557
4558void DeleteOilRig(TileIndex tile)
4559{
4560 Station *st = Station::GetByTile(tile);
4561
4562 MakeWaterKeepingClass(tile, OWNER_NONE);
4563
4564 /* The oil rig station is not supposed to be shared with anything else */
4565 [[maybe_unused]] static constexpr StationFacilities expected_facility{StationFacility::Airport, StationFacility::Dock};
4566 assert(st->facilities == expected_facility && st->airport.type == AT_OILRIG);
4567 if (st->industry != nullptr && st->industry->neutral_station == st) {
4568 /* Don't leave dangling neutral station pointer */
4569 st->industry->neutral_station = nullptr;
4570 }
4571 delete st;
4572}
4573
4574static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4575{
4576
4577 if (IsAnyRoadStopTile(tile)) {
4578 for (RoadTramType rtt : _roadtramtypes) {
4579 /* Update all roadtypes, no matter if they are present */
4580 if (GetRoadOwner(tile, rtt) == old_owner) {
4581 RoadType rt = GetRoadType(tile, rtt);
4582 if (rt != INVALID_ROADTYPE) {
4583 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4584 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4585 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4586 }
4587 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4588 }
4589 }
4590 }
4591
4592 if (!IsTileOwner(tile, old_owner)) return;
4593
4594 if (new_owner != INVALID_OWNER) {
4595 /* Update company infrastructure counts. Only do it here
4596 * if the new owner is valid as otherwise the clear
4597 * command will do it for us. No need to dirty windows
4598 * here, we'll redraw the whole screen anyway.*/
4599 Company *old_company = Company::Get(old_owner);
4600 Company *new_company = Company::Get(new_owner);
4601
4602 /* Update counts for underlying infrastructure. */
4603 switch (GetStationType(tile)) {
4604 case StationType::Rail:
4605 case StationType::RailWaypoint:
4606 if (!IsStationTileBlocked(tile)) {
4607 old_company->infrastructure.rail[GetRailType(tile)]--;
4608 new_company->infrastructure.rail[GetRailType(tile)]++;
4609 }
4610 break;
4611
4612 case StationType::Bus:
4613 case StationType::Truck:
4614 case StationType::RoadWaypoint:
4615 /* Road stops were already handled above. */
4616 break;
4617
4618 case StationType::Buoy:
4619 case StationType::Dock:
4620 if (GetWaterClass(tile) == WATER_CLASS_CANAL) {
4621 old_company->infrastructure.water--;
4622 new_company->infrastructure.water++;
4623 }
4624 break;
4625
4626 default:
4627 break;
4628 }
4629
4630 /* Update station tile count. */
4631 if (!IsBuoy(tile) && !IsAirport(tile)) {
4632 old_company->infrastructure.station--;
4633 new_company->infrastructure.station++;
4634 }
4635
4636 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4637 SetTileOwner(tile, new_owner);
4639 } else {
4640 if (IsDriveThroughStopTile(tile)) {
4641 /* Remove the drive-through road stop */
4642 if (IsRoadWaypoint(tile)) {
4644 } else {
4646 }
4647 assert(IsTileType(tile, MP_ROAD));
4648 /* Change owner of tile and all roadtypes */
4649 ChangeTileOwner(tile, old_owner, new_owner);
4650 } else {
4652 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4653 * Update owner of buoy if it was not removed (was in orders).
4654 * Do not update when owned by OWNER_WATER (sea and rivers). */
4655 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4656 }
4657 }
4658}
4659
4669{
4670 /* Water flooding can always clear road stops. */
4671 if (_current_company == OWNER_WATER) return CommandCost();
4672
4673 CommandCost ret;
4674
4675 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4676 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
4677 if (tram_owner != OWNER_NONE) {
4678 ret = CheckOwnership(tram_owner);
4679 if (ret.Failed()) return ret;
4680 }
4681 }
4682
4683 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4684 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
4685 if (road_owner == OWNER_TOWN) {
4686 ret = CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags);
4687 if (ret.Failed()) return ret;
4688 } else if (road_owner != OWNER_NONE) {
4689 ret = CheckOwnership(road_owner);
4690 if (ret.Failed()) return ret;
4691 }
4692 }
4693
4694 return CommandCost();
4695}
4696
4704{
4705 if (flags.Test(DoCommandFlag::Auto)) {
4706 switch (GetStationType(tile)) {
4707 default: break;
4708 case StationType::Rail: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4709 case StationType::RailWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4710 case StationType::Airport: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4711 case StationType::Truck: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4712 case StationType::Bus: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4713 case StationType::RoadWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4714 case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4715 case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4716 case StationType::Oilrig:
4717 return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG);
4718 }
4719 }
4720
4721 switch (GetStationType(tile)) {
4722 case StationType::Rail: return RemoveRailStation(tile, flags);
4723 case StationType::RailWaypoint: return RemoveRailWaypoint(tile, flags);
4724 case StationType::Airport: return RemoveAirport(tile, flags);
4725 case StationType::Truck: [[fallthrough]];
4726 case StationType::Bus:
4727 if (IsDriveThroughStopTile(tile)) {
4728 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4729 if (remove_road.Failed()) return remove_road;
4730 }
4731 return RemoveRoadStop(tile, flags);
4732 case StationType::RoadWaypoint: {
4733 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4734 if (remove_road.Failed()) return remove_road;
4735 return RemoveRoadWaypointStop(tile, flags);
4736 }
4737 case StationType::Buoy: return RemoveBuoy(tile, flags);
4738 case StationType::Dock: return RemoveDock(tile, flags);
4739 default: break;
4740 }
4741
4742 return CMD_ERROR;
4743}
4744
4745static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
4746{
4748 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4749 * TTDP does not call it.
4750 */
4751 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4752 switch (GetStationType(tile)) {
4753 case StationType::RailWaypoint:
4754 case StationType::Rail: {
4755 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4756 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4757 }
4758
4759 case StationType::Airport:
4760 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4761
4762 case StationType::Truck:
4763 case StationType::Bus:
4764 case StationType::RoadWaypoint: {
4765 if (IsDriveThroughStopTile(tile)) {
4766 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4767 } else {
4768 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4769 }
4770 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4771 }
4772
4773 default: break;
4774 }
4775 }
4776 }
4777 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
4778}
4779
4786{
4787 uint32_t prev = 0;
4788 for (const auto &it : this->shares) {
4789 if (it.second == st) {
4790 return it.first - prev;
4791 } else {
4792 prev = it.first;
4793 }
4794 }
4795 return 0;
4796}
4797
4805{
4806 if (this->unrestricted == 0) return StationID::Invalid();
4807 assert(!this->shares.empty());
4808 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
4809 assert(it != this->shares.end() && it->first <= this->unrestricted);
4810 if (it->second != excluded && it->second != excluded2) return it->second;
4811
4812 /* We've hit one of the excluded stations.
4813 * Draw another share, from outside its range. */
4814
4815 uint end = it->first;
4816 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
4817 uint interval = end - begin;
4818 if (interval >= this->unrestricted) return StationID::Invalid(); // Only one station in the map.
4819 uint new_max = this->unrestricted - interval;
4820 uint rand = RandomRange(new_max);
4821 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
4822 this->shares.upper_bound(rand + interval);
4823 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
4824 if (it2->second != excluded && it2->second != excluded2) return it2->second;
4825
4826 /* We've hit the second excluded station.
4827 * Same as before, only a bit more complicated. */
4828
4829 uint end2 = it2->first;
4830 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
4831 uint interval2 = end2 - begin2;
4832 if (interval2 >= new_max) return StationID::Invalid(); // Only the two excluded stations in the map.
4833 new_max -= interval2;
4834 if (begin > begin2) {
4835 std::swap(begin, begin2);
4836 std::swap(end, end2);
4837 std::swap(interval, interval2);
4838 }
4839 rand = RandomRange(new_max);
4840 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
4841 if (rand < begin) {
4842 it3 = this->shares.upper_bound(rand);
4843 } else if (rand < begin2 - interval) {
4844 it3 = this->shares.upper_bound(rand + interval);
4845 } else {
4846 it3 = this->shares.upper_bound(rand + interval + interval2);
4847 }
4848 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
4849 return it3->second;
4850}
4851
4858{
4859 assert(!this->shares.empty());
4860 SharesMap new_shares;
4861 uint i = 0;
4862 for (const auto &it : this->shares) {
4863 new_shares[++i] = it.second;
4864 if (it.first == this->unrestricted) this->unrestricted = i;
4865 }
4866 this->shares.swap(new_shares);
4867 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
4868}
4869
4877{
4878 /* We assert only before changing as afterwards the shares can actually
4879 * be empty. In that case the whole flow stat must be deleted then. */
4880 assert(!this->shares.empty());
4881
4882 uint removed_shares = 0;
4883 uint added_shares = 0;
4884 uint last_share = 0;
4885 SharesMap new_shares;
4886 for (const auto &it : this->shares) {
4887 if (it.second == st) {
4888 if (flow < 0) {
4889 uint share = it.first - last_share;
4890 if (flow == INT_MIN || (uint)(-flow) >= share) {
4891 removed_shares += share;
4892 if (it.first <= this->unrestricted) this->unrestricted -= share;
4893 if (flow != INT_MIN) flow += share;
4894 last_share = it.first;
4895 continue; // remove the whole share
4896 }
4897 removed_shares += (uint)(-flow);
4898 } else {
4899 added_shares += (uint)(flow);
4900 }
4901 if (it.first <= this->unrestricted) this->unrestricted += flow;
4902
4903 /* If we don't continue above the whole flow has been added or
4904 * removed. */
4905 flow = 0;
4906 }
4907 new_shares[it.first + added_shares - removed_shares] = it.second;
4908 last_share = it.first;
4909 }
4910 if (flow > 0) {
4911 new_shares[last_share + (uint)flow] = st;
4912 if (this->unrestricted < last_share) {
4913 this->ReleaseShare(st);
4914 } else {
4915 this->unrestricted += flow;
4916 }
4917 }
4918 this->shares.swap(new_shares);
4919}
4920
4927{
4928 assert(!this->shares.empty());
4929 uint flow = 0;
4930 uint last_share = 0;
4931 SharesMap new_shares;
4932 for (auto &it : this->shares) {
4933 if (flow == 0) {
4934 if (it.first > this->unrestricted) return; // Not present or already restricted.
4935 if (it.second == st) {
4936 flow = it.first - last_share;
4937 this->unrestricted -= flow;
4938 } else {
4939 new_shares[it.first] = it.second;
4940 }
4941 } else {
4942 new_shares[it.first - flow] = it.second;
4943 }
4944 last_share = it.first;
4945 }
4946 if (flow == 0) return;
4947 new_shares[last_share + flow] = st;
4948 this->shares.swap(new_shares);
4949 assert(!this->shares.empty());
4950}
4951
4958{
4959 assert(!this->shares.empty());
4960 uint flow = 0;
4961 uint next_share = 0;
4962 bool found = false;
4963 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
4964 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
4965 if (found) {
4966 flow = next_share - it->first;
4967 this->unrestricted += flow;
4968 break;
4969 } else {
4970 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
4971 if (it->second == st) found = true;
4972 }
4973 next_share = it->first;
4974 }
4975 if (flow == 0) return;
4976 SharesMap new_shares;
4977 new_shares[flow] = st;
4978 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
4979 if (it->second != st) {
4980 new_shares[flow + it->first] = it->second;
4981 } else {
4982 flow = 0;
4983 }
4984 }
4985 this->shares.swap(new_shares);
4986 assert(!this->shares.empty());
4987}
4988
4995{
4996 assert(runtime > 0);
4997 SharesMap new_shares;
4998 uint share = 0;
4999 for (auto i : this->shares) {
5000 share = std::max(share + 1, i.first * 30 / runtime);
5001 new_shares[share] = i.second;
5002 if (this->unrestricted == i.first) this->unrestricted = share;
5003 }
5004 this->shares.swap(new_shares);
5005}
5006
5013void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5014{
5015 FlowStatMap::iterator origin_it = this->find(origin);
5016 if (origin_it == this->end()) {
5017 this->emplace(origin, FlowStat(via, flow));
5018 } else {
5019 origin_it->second.ChangeShare(via, flow);
5020 assert(!origin_it->second.GetShares()->empty());
5021 }
5022}
5023
5032void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5033{
5034 FlowStatMap::iterator prev_it = this->find(origin);
5035 if (prev_it == this->end()) {
5036 FlowStat fs(via, flow);
5037 fs.AppendShare(StationID::Invalid(), flow);
5038 this->emplace(origin, fs);
5039 } else {
5040 prev_it->second.ChangeShare(via, flow);
5041 prev_it->second.ChangeShare(StationID::Invalid(), flow);
5042 assert(!prev_it->second.GetShares()->empty());
5043 }
5044}
5045
5051{
5052 for (auto &i : *this) {
5053 FlowStat &fs = i.second;
5054 uint local = fs.GetShare(StationID::Invalid());
5055 if (local > INT_MAX) { // make sure it fits in an int
5056 fs.ChangeShare(self, -INT_MAX);
5057 fs.ChangeShare(StationID::Invalid(), -INT_MAX);
5058 local -= INT_MAX;
5059 }
5060 fs.ChangeShare(self, -(int)local);
5061 fs.ChangeShare(StationID::Invalid(), -(int)local);
5062
5063 /* If the local share is used up there must be a share for some
5064 * remote station. */
5065 assert(!fs.GetShares()->empty());
5066 }
5067}
5068
5076{
5077 StationIDStack ret;
5078 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5079 FlowStat &s_flows = f_it->second;
5080 s_flows.ChangeShare(via, INT_MIN);
5081 if (s_flows.GetShares()->empty()) {
5082 ret.Push(f_it->first.base());
5083 this->erase(f_it++);
5084 } else {
5085 ++f_it;
5086 }
5087 }
5088 return ret;
5089}
5090
5096{
5097 for (auto &it : *this) {
5098 it.second.RestrictShare(via);
5099 }
5100}
5101
5107{
5108 for (auto &it : *this) {
5109 it.second.ReleaseShare(via);
5110 }
5111}
5112
5118{
5119 uint ret = 0;
5120 for (const auto &it : *this) {
5121 ret += (--(it.second.GetShares()->end()))->first;
5122 }
5123 return ret;
5124}
5125
5132{
5133 uint ret = 0;
5134 for (const auto &it : *this) {
5135 ret += it.second.GetShare(via);
5136 }
5137 return ret;
5138}
5139
5146{
5147 FlowStatMap::const_iterator i = this->find(from);
5148 if (i == this->end()) return 0;
5149 return (--(i->second.GetShares()->end()))->first;
5150}
5151
5159{
5160 FlowStatMap::const_iterator i = this->find(from);
5161 if (i == this->end()) return 0;
5162 return i->second.GetShare(via);
5163}
5164
5165extern const TileTypeProcs _tile_type_station_procs = {
5166 DrawTile_Station, // draw_tile_proc
5167 GetSlopePixelZ_Station, // get_slope_z_proc
5168 ClearTile_Station, // clear_tile_proc
5169 nullptr, // add_accepted_cargo_proc
5170 GetTileDesc_Station, // get_tile_desc_proc
5171 GetTileTrackStatus_Station, // get_tile_track_status_proc
5172 ClickTile_Station, // click_tile_proc
5173 AnimateTile_Station, // animate_tile_proc
5174 TileLoop_Station, // tile_loop_proc
5175 ChangeTileOwner_Station, // change_tile_owner_proc
5176 nullptr, // add_produced_cargo_proc
5177 VehicleEnter_Station, // vehicle_enter_tile_proc
5178 GetFoundation_Station, // get_foundation_proc
5179 TerraformTile_Station, // terraform_tile_proc
5180};
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 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.
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 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.
void insert(const Tkey &key)
Insert a key into the set.
uint GetFlow() const
Get the sum of all flows from this FlowStatMap.
void PassOnFlow(StationID origin, StationID via, uint amount)
Pass on some flow, remembering it as invalid, for later subtraction from locally consumed flow.
void AddFlow(StationID origin, StationID via, uint amount)
Add some flow from "origin", going via "via".
uint GetFlowFrom(StationID from) const
Get the sum of flows from a specific station from this FlowStatMap.
void FinalizeLocalConsumption(StationID self)
Subtract invalid flows from locally consumed flow.
void ReleaseFlows(StationID via)
Release all flows at a station for specific cargo and destination.
StationIDStack DeleteFlows(StationID via)
Delete all flows at a station for specific cargo and destination.
uint GetFlowFromVia(StationID from, StationID via) const
Get the flow from a specific station via a specific other station.
uint GetFlowVia(StationID via) const
Get the sum of flows via a specific station from this FlowStatMap.
void RestrictFlows(StationID via)
Restrict all flows at a station for specific cargo and destination.
Flow statistics telling how much flow should be sent along a link.
static const SharesMap empty_sharesmap
Static instance of FlowStat::SharesMap.
void ScaleToMonthly(uint runtime)
Scale all shares from link graph's runtime to monthly values.
void RestrictShare(StationID st)
Restrict a flow by moving it to the end of the map and decreasing the amount of unrestricted flow.
uint GetShare(StationID st) const
Get flow for a station.
StationID GetVia() const
Get a station a package can be routed to.
const SharesMap * GetShares() const
Get the actual shares as a const pointer so that they can be iterated over.
SharesMap shares
Shares of flow to be sent via specified station (or consumed locally).
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...
uint unrestricted
Limit for unrestricted shares.
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 Unqueue(LinkGraph *lg)
Remove a link graph from the execution queue.
void Queue(LinkGraph *lg)
Queue a link graph for execution.
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.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:118
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:128
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:222
struct RailTypeInfo::@20 base_sprites
Struct containing the main sprites.
StringID name
Name of this rail type.
Definition rail.h:167
uint8_t fallback_railtype
Original railtype number to use when drawing non-newgrf railtypes, or when drawing stations.
Definition rail.h:192
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:288
struct RailTypeInfo::@23 strings
Strings associated with the rail type.
A sort-of mixin that adds 'at(pos)' and 'operator[](pos)' implementations for 'ConvertibleThroughBase...
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:133
StringID name
Name of this rail type.
Definition road.h:94
struct RoadTypeInfo::@26 strings
Strings associated with the rail type.
Minimal stack that uses a pool to avoid pointers.
void Push(const Titem &item)
Pushes a new item onto the stack if there is still space in the underlying pool.
Generate TileIndices around a center tile or tile area, with increasing distance.
Add dynamic register values to a sprite layout.
DrawTileSpriteSpan GetLayout() const
Returns the result spritelayout after preprocessing.
uint Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
Routes packets with station "avoid" as next hop to a different place.
uint AvailableCount() const
Returns sum of cargo still available for loading at the sation.
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.
StationList stations
List of stations nearby.
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.
uint StoredCount() const
Returns sum of cargo on board the vehicle (ie not only reserved).
Iterate over all vehicles on a tile.
Functions related to clear (MP_CLEAR) land.
CommandCost CommandCostWithParam(StringID str, uint64_t value)
Return an error status, with string and parameter.
Definition command.cpp:417
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
@ Auto
don't allow building on structures
Definition of stuff that is very close to a company, like the company struct itself.
PaletteID GetCompanyPalette(CompanyID company)
Get the palette for recolouring with a company colour.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
void DirtyCompanyInfrastructureWindows(CompanyID company)
Redraw all windows with company infrastructure counts.
GUI Functions related to companies.
static constexpr Owner OWNER_END
Last + 1 owner.
static constexpr Owner OWNER_TOWN
A town owns the tile, or a town is expanding.
static constexpr Owner OWNER_NONE
The tile has no ownership.
static constexpr Owner INVALID_OWNER
An invalid owner.
static constexpr Owner OWNER_WATER
The tile/execution is done by "water".
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh)
Find out if the slope of the tile is suitable to build a depot of given direction.
Definition depot_func.h:27
void ShowDepotWindow(TileIndex tile, VehicleType type)
Opens a depot window.
bool IsValidAxis(Axis d)
Checks if an integer value is a valid Axis.
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
DiagDirections AxisToDiagDirs(Axis a)
Converts an Axis to DiagDirections.
bool IsValidDiagDirection(DiagDirection d)
Checks if an integer value is a valid DiagDirection.
DiagDirection AxisToDiagDir(Axis a)
Converts an Axis to a DiagDirection.
Axis OtherAxis(Axis a)
Select the other axis as provided.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
Direction
Defines the 8 directions on the map.
@ DIR_N
North.
@ DIR_W
West.
@ DIR_E
East.
Axis
Allow incrementing of DiagDirDiff variables.
@ AXIS_X
The X axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_END
Used for iterations.
@ DIAGDIR_BEGIN
Used for iterations.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
@ DIAGDIR_SW
Southwest.
static const uint ROAD_STOP_TRACKBIT_FACTOR
Multiplier for how many regular track bits a bay stop counts.
@ EXPENSES_CONSTRUCTION
Construction costs.
Price
Enumeration of all base prices for use with Prices.
void DrawRailCatenary(const TileInfo *ti)
Draws overhead wires and pylons for electric railways.
Definition elrail.cpp:567
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:1024
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
uint8_t GetSnowLine()
Get the current snow line, either variable or static.
void MarkTilesDirty(bool cargo_change) const
Marks the tiles of the station as dirty.
Definition station.cpp:247
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
HouseZone
Definition house.h:55
Base of all industries.
bool IsTileForestIndustry(TileIndex tile)
Check whether the tile is a forest.
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
IndustryType GetIndustryType(Tile tile)
Retrieve the type for this industry.
IndustryID GetIndustryIndex(Tile t)
Get the industry ID of the given tile.
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like IT_...
@ Extractive
Like mines.
void ChangeTileOwner(TileIndex tile, Owner old_owner, Owner new_owner)
Change the owner of a tile.
void DrawFoundation(TileInfo *ti, Foundation f)
Draw foundation f at tile ti.
std::tuple< Slope, int > GetFoundationPixelSlope(TileIndex tile)
Get slope of a tile on top of a (possible) foundation If a tile does not have a foundation,...
Definition landscape.h:65
uint ApplyPixelFoundationToSlope(Foundation f, Slope &s)
Applies a foundation to a slope.
Definition landscape.h:126
Point RemapCoords2(int x, int y)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:95
Command definitions related to landscape (slopes etc.).
@ Random
Randomise borders.
Some typedefs for the main game.
@ Increase
Increase capacity.
@ DT_MANUAL
Manual distribution. No link graph calculations are run.
uint DistanceFromEdge(TileIndex tile)
Param the minimum distance to an edge.
Definition map.cpp:202
uint DistanceMax(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles.
Definition map.cpp:174
TileIndex TileAddWrap(TileIndex tile, int addx, int addy)
This function checks if we add addx/addy to tile, if we do wrap around the edges.
Definition map.cpp:93
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:142
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:372
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:439
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:388
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:554
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:424
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:414
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:569
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
uint8_t StationGfx
Copy from station_map.h.
StationGfx GetTranslatedAirportTileID(StationGfx gfx)
Do airporttile gfx ID translation for NewGRFs.
NewGRF handling of airport tiles.
@ NoAnimation
There is no animation.
@ DrawTileLayout
Use callback to select a tile layout to use when drawing.
@ Avail
Availability of station in construction window.
@ SlopeCheck
Check slope of new station tiles.
@ StationRatingCalc
custom station rating for this cargo type
@ CBID_STATION_BUILD_TILE_LAYOUT
Called when building a station to customize the tile layout.
@ CBID_STATION_DRAW_TILE_LAYOUT
Choose a tile layout to draw, instead of the standard range.
@ CBID_CARGO_STATION_RATING_CALC
Called to calculate part of a station rating.
@ CBID_STATION_AVAILABILITY
Determine whether a newstation should be made available to build.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
@ Avail
Availability of road stop in construction window.
SpriteID GetCanalSprite(CanalFeature feature, TileIndex tile)
Lookup the base sprite to use for a canal.
Handling of NewGRF canals.
Cargo support for NewGRFs.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
bool Convert8bitBooleanCallback(const GRFFile *grffile, uint16_t cbid, uint16_t cb_res)
Converts a callback result into a boolean.
GRFConfig * GetGRFConfig(uint32_t grfid, uint32_t mask)
Retrieve a NewGRF from the current config by its grfid.
Functions/types related to NewGRF debugging.
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Delete inspect window for a given feature and index.
void TriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, CargoTypes trigger_cargoes)
Run watched cargo accepted callback for a house.
Functions related to NewGRF houses.
SpriteID GetCustomRailSprite(const RailTypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of rail types.
NewGRF definitions and structures for road stops.
RoadStopDrawMode
Different draw modes to disallow rendering of some parts of the stop or road.
@ 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.
int AllocateSpecToStation(const StationSpec *statspec, BaseStation *st, bool exec)
Allocate a StationSpec to a Station.
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 DeallocateSpecFromStation(BaseStation *st, uint8_t specindex)
Deallocate a StationSpec from a Station.
void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger station randomisation.
CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, uint8_t plat_len, uint8_t numtracks)
Check the slope of a tile of a new station.
Header file for NewGRF stations.
@ CustomFoundations
Draw custom foundations.
@ SeparateGround
Use different sprite set for ground sprites.
@ ExtendedFoundations
Extended foundation block instead of simple.
uint16_t GetStationLayoutKey(uint8_t platforms, uint8_t length)
Get the station layout key for a given station layout size.
StationClassID
Functions related to news.
void AddNewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1={}, NewsReference ref2={}, std::unique_ptr< NewsAllocatedData > &&data=nullptr, AdviceType advice_type=AdviceType::Invalid)
Add a new newsitem to be shown.
Definition news_gui.cpp:902
@ Acceptance
A type of cargo is (no longer) accepted.
@ Small
Small news item. (Information window with text and viewport)
@ InColour
News item is shown in colour (otherwise it is shown in black & white).
Functions related to order backups.
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:331
void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
Set the reservation for a complete station platform.
Definition pbs.cpp:57
PBS support routines.
bool ValParamRailType(const RailType rail)
Validate functions for rail building.
Definition rail.cpp:208
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:381
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition rail.h:341
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:300
@ RTSG_GROUND
Main group of ground images.
Definition rail.h:43
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:42
RailTrackOffset
Offsets for sprites within an overlay/underlay set.
Definition rail.h:61
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:63
@ RTO_X
Piece of rail in X direction.
Definition rail.h:62
Command definitions for rail.
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
TrackBits GetRailReservationTrackBits(Tile t)
Returns the reserved track bits of the tile.
Definition rail_map.h:194
bool HasSignals(Tile t)
Checks if a rail tile has signals.
Definition rail_map.h:72
static debug_inline bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
Pseudo random number generator.
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
Definition of link refreshing utility.
bool ValParamRoadType(RoadType roadtype)
Validate functions for rail building.
Definition road.cpp:152
bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
Checks if an engine of the given RoadType got power on a tile with a given RoadType.
Definition road.h:258
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:230
@ ROTSG_OVERLAY
Optional: Images for overlaying track.
Definition road.h:52
@ ROTSG_ROADSTOP
Required: Bay stop surface.
Definition road.h:61
@ ROTSG_GROUND
Required: Main group of ground images.
Definition road.h:53
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:268
void DrawRoadGroundSprites(const TileInfo *ti, RoadBits road, RoadBits tram, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, Roadside roadside, bool snow_or_desert)
Draw road ground sprites.
void DrawRoadCatenary(const TileInfo *ti)
Draws the catenary for the given tile.
void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count)
Update road infrastructure counts for a company.
Definition road_cmd.cpp:186
CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadTramType rtt, DoCommandFlags flags, bool town_check)
Is it allowed to remove the given road bits from the given tile?
Definition road_cmd.cpp:258
void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, uint road_offset, uint tram_offset, bool draw_underlay)
Draw road underlay and overlay sprites.
Functions related to roads.
RoadBits AxisToRoadBits(Axis a)
Create the road-part which belongs to the given Axis.
Definition road_func.h:111
Functions used internally by the roads.
bool MayHaveRoad(Tile t)
Test whether a tile can have road/tram types.
Definition road_map.cpp:21
RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance)
Returns the RoadBits on an arbitrary tile Special behaviour:
Definition road_map.cpp:54
void SetRoadOwner(Tile t, RoadTramType rtt, Owner o)
Set the owner of a specific road type.
Definition road_map.h:235
static debug_inline bool IsNormalRoadTile(Tile t)
Return whether a tile is a normal road tile.
Definition road_map.h:58
RoadBits GetRoadBits(Tile t, RoadTramType rtt)
Get the present road bits for a specific road type.
Definition road_map.h:112
DisallowedRoadDirections GetDisallowedRoadDirections(Tile t)
Gets the disallowed directions.
Definition road_map.h:285
bool HasTileRoadType(Tile t, RoadTramType rtt)
Check if a tile has a road or a tram road type.
Definition road_map.h:195
void MakeRoadNormal(Tile t, RoadBits bits, RoadType road_rt, RoadType tram_rt, TownID town, Owner road, Owner tram)
Make a normal road tile.
Definition road_map.h:615
RoadBits GetAllRoadBits(Tile tile)
Get all set RoadBits on the given tile.
Definition road_map.h:125
Owner GetRoadOwner(Tile t, RoadTramType rtt)
Get the owner of a specific road type.
Definition road_map.h:218
Roadside
The possible road side decorations.
Definition road_map.h:457
@ ROADSIDE_PAVED
Road with paved sidewalks.
Definition road_map.h:460
@ ROADSIDE_BARREN
Road on barren land.
Definition road_map.h:458
@ ROADSIDE_GRASS
Road on grass.
Definition road_map.h:459
RoadBits
Enumeration for the road parts on a tile.
Definition road_type.h:40
@ ROAD_NONE
No road-part is build.
Definition road_type.h:41
@ ROAD_Y
Full road along the y-axis (north-west + south-east)
Definition road_type.h:47
@ ROAD_X
Full road along the x-axis (south-west + north-east)
Definition road_type.h:46
RoadType
The different roadtypes we support.
Definition road_type.h:23
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:28
@ DRD_NONE
None of the directions are disallowed.
Definition road_type.h:62
Base class for roadstops.
Road vehicle states.
@ RVSB_IN_ROAD_STOP
The vehicle is in a road stop.
Definition roadveh.h:49
@ RVSB_ROAD_STOP_TRACKDIR_MASK
Only bits 0 and 3 are used to encode the trackdir for road stops.
Definition roadveh.h:57
@ RVS_IN_DT_ROAD_STOP
The vehicle is in a drive-through road stop.
Definition roadveh.h:46
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Base for ships.
bool IsShipDestinationTile(TileIndex tile, StationID station)
Test if a tile is a docking tile for the given station.
Definition ship_cmd.cpp:640
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:587
@ 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:101
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:91
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:170
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1611
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1551
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1541
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
Definition sprites.h:1554
Base classes/functions for stations.
void ForAllStationsAroundTiles(const TileArea &ta, Func func)
Call a function on all stations that have any part of the requested area within their catchment.
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index=-1)
Remove a bus station/truck stop.
static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
Common part of building various station parts and possibly attaching them to an existing one.
static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlags flags)
Remove a rail waypoint.
static const IntervalTimer< TimerGameEconomy > _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto) { for(Station *st :Station::Iterate()) { for(GoodsEntry &ge :st->goods) { ge.status.Set(GoodsEntry::State::LastMonth, ge.status.Test(GoodsEntry::State::CurrentMonth));ge.status.Reset(GoodsEntry::State::CurrentMonth);} } })
Economy monthly loop for stations.
CargoTypes GetEmptyMask(const Station *st)
Get a mask of the cargo types that are empty at the station.
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.
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 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.
static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, 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.
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.
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 GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const StationSpec *statspec)
Create the station layout for the given number of tracks and platform length.
void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid2)
Reroute cargo of type c at station st or in any vehicles unloading there.
CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags)
Clear a single tile of a station.
void UpdateAllStationVirtCoords()
Update the virtual coords needed to draw the station sign for all stations.
static bool CMSAWater(TileIndex tile)
Check whether the tile is water.
CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
Place an Airport.
void TriggerWatchedCargoCallbacks(Station *st)
Run the watched cargo callback for all houses in the catchment area.
static CommandCost CanRemoveRoadWithStop(TileIndex tile, DoCommandFlags flags)
Check if a drive-through road stop tile can be cleared.
static RoadStop ** FindRoadStopSpot(bool truck_station, Station *st)
CargoTypes GetAcceptanceMask(const Station *st)
Get a mask of the cargo types that the station accepts.
static void RestoreTrainReservation(Train *v)
Restore platform reservation during station building/removing.
void UpdateAirportsNoise()
Recalculate the noise generated by the airports of each town.
static bool CMSATree(TileIndex tile)
Check whether the tile is a tree.
CommandCost RemoveRailStation(T *st, DoCommandFlags flags, Money removal_cost)
Remove a rail station/waypoint.
void UpdateStationAcceptance(Station *st, bool show_msg)
Update the acceptance for a station.
static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
Find a nearby station that joins this road stop.
CommandCost CmdOpenCloseAirport(DoCommandFlags flags, StationID station_id)
Open/close an airport to incoming aircraft.
static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_tile, int &allowed_z, DoCommandFlags flags, Axis axis, StationID *station, RailType rt, std::vector< Train * > &affected_vehicles, StationClassID spec_class, uint16_t spec_index, uint8_t plat_len, uint8_t numtracks)
Checks if a rail station can be built at the given tile.
static TileIndex FindDockLandPart(TileIndex t)
Find the part of a dock that is land-based.
static void FreeTrainReservation(Train *v)
Clear platform reservation during station building/removing.
void SetRailStationTileFlags(TileIndex tile, const StationSpec *statspec)
Set rail station tile flags for the given tile.
static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
Get the acceptance of cargoes around the station in.
static CommandCost RemoveDock(TileIndex tile, DoCommandFlags flags)
Remove a dock.
bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
Check whether a sprite is a track sprite, which can be replaced by a non-track ground sprite and a ra...
CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
Get the cargo types being produced around the tile (in a rectangle).
CommandCost CmdRemoveFromRailStation(DoCommandFlags flags, TileIndex start, TileIndex end, bool keep_rail)
Remove a single tile from a rail station.
static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
Find a nearby station that joins this station.
void DeleteStaleLinks(Station *from)
Check all next hops of cargo packets in this station for existence of a a valid link they may use to ...
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.
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
Calculates cost of new road stops within the area.
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.
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.
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?
Axis GetDriveThroughStopAxis(Tile t)
Gets the axis of the drive through stop.
void SetStationTileHavePylons(Tile t, bool b)
Set the catenary pylon state of the rail station.
bool IsOilRig(Tile t)
Is tile t part of an oilrig?
bool IsBuoyTile(Tile t)
Is tile t a buoy tile?
void MakeRailStation(Tile t, Owner o, StationID sid, Axis a, uint8_t section, RailType rt)
Make the given tile a rail station tile.
bool HasStationReservation(Tile t)
Get the reservation state of the rail station.
void MakeDock(Tile t, Owner o, StationID sid, DiagDirection d, WaterClass wc)
Make the given tile a dock tile.
DiagDirection GetBayRoadStopDir(Tile t)
Gets the direction the bay road stop entrance points towards.
bool IsDock(Tile t)
Is tile t a dock tile?
bool IsAnyRoadStopTile(Tile t)
Is tile t a road stop station?
void SetStationTileBlocked(Tile t, bool b)
Set the blocked state of the rail station.
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition station_map.h:56
void SetCustomRoadStopSpecIndex(Tile t, uint8_t specindex)
Set the custom road stop spec for this tile.
RoadStopType
Types of RoadStops.
@ Bus
A standard stop for buses.
@ Truck
A standard stop for trucks.
@ End
End of valid types.
@ Dock
Station with a dock.
@ Waypoint
Station is a waypoint.
@ TruckStop
Station with truck stops.
@ Train
Station with train station.
@ Airport
Station with an airport.
@ BusStop
Station with bus stops.
StationType
Station types.
@ NewCargo
Trigger station on new cargo arrival.
std::set< Station *, StationCompare > StationList
List of stations.
@ Built
Trigger tile when built.
@ TileLoop
Trigger in the periodic tile loop.
@ NewCargo
Trigger station on new cargo arrival.
@ AcceptanceTick
Trigger station every 250 ticks.
static const uint MAX_LENGTH_STATION_NAME_CHARS
The maximum length of a station name in characters including '\0'.
@ Built
Triggered when the airport is built (for all tiles at the same time).
@ TileLoop
Triggered in the periodic tile loop.
@ NewCargo
Triggered when new cargo arrives at the station (for all tiles at the same time).
@ AcceptanceTick
Triggered every 250 ticks (for all tiles at the same time).
Types related to the station widgets.
@ WID_SV_CLOSE_AIRPORT
'Close airport' button.
@ WID_SV_ROADVEHS
List of scheduled road vehs button.
@ WID_SV_ACCEPT_RATING_LIST
List of accepted cargoes / rating of cargoes.
@ WID_SV_SHIPS
List of scheduled ships button.
@ WID_SV_TRAINS
List of scheduled trains button.
Definition of base types and functions in a cross-platform compatible way.
size_t Utf8StringLength(std::string_view str)
Get the length of an UTF-8 encoded string in number of characters and thus not the number of bytes th...
Definition string.cpp:347
Functions related to low-level strings.
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:327
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:91
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:415
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.
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:72
@ Airplanes
Can planes land on this airport type?
Defines the data structure for an airport.
StringID name
name of this airport
SubstituteGRFFileProps grf_prop
Properties related to the grf file.
std::vector< AirportTileLayout > layouts
List of layouts composing the airport.
bool IsWithinMapBounds(uint8_t table, TileIndex index) const
Check if the airport would be within the map bounds at the given tile.
uint8_t size_y
size of airport in y direction
uint8_t size_x
size of airport in x direction
static const AirportSpec * Get(uint8_t type)
Retrieve airport spec for the given airport.
std::span< const HangarTileTable > depots
Position of the depots on the airports.
bool IsAvailable() const
Check whether this airport is available to build.
uint8_t noise_level
noise that this airport generates
Defines the data structure of each individual tile of an airport.
static const AirportTileSpec * Get(StationGfx gfx)
Retrieve airport tile spec for the given airport tile.
SubstituteGRFFileProps grf_prop
properties related the the grf file
StringID name
Tile Subname string, land information on this tile will give you "AirportName (TileSubname)".
AnimationInfo< AirportAnimationTriggers > animation
Information about the animation.
static const AirportTileSpec * GetByTile(TileIndex tile)
Retrieve airport tile spec for the given airport tile.
AirportBlocks blocks
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
TileIndex GetRotatedTileFromOffset(TileIndexDiffC tidc) const
Add the tileoffset to the base tile of this airport but rotate it first.
uint GetHangarNum(TileIndex tile) const
Get the hangar number of the hangar at a specific tile.
Direction rotation
How this airport is rotated.
uint8_t type
Type of this airport,.
PersistentStorage * psa
Persistent storage for NewGRF airports.
uint GetNumHangars() const
Get the number of hangars on this airport.
TileIndex GetHangarTile(uint hangar_num) const
Get the first tile of the given hangar.
const AirportSpec * GetSpec() const
Get the AirportSpec that from the airport type of this airport.
uint8_t layout
Airport layout number.
Base class for all station-ish types.
StringID string_id
Default name (town area) of station.
TileIndex xy
Base tile of the station.
std::vector< SpecMapping< StationSpec > > speclist
List of rail station specs of this station.
StationFacilities facilities
The facilities that this station has.
std::string cached_name
NOSAVE: Cache of the resolved name of the station, if not using a custom name.
TileArea train_station
Tile area the train 'station' part covers.
StationAnimationTriggers cached_anim_triggers
NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
Owner owner
The owner of this station.
virtual void UpdateVirtCoord()=0
Update the coordinated of the sign (as shown in the viewport).
uint8_t delete_ctr
Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is ...
StationAnimationTriggers cached_roadstop_anim_triggers
NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing sh...
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
bool IsInUse() const
Check whether the base station currently is in use; in use means that it is not scheduled for deletio...
Town * town
The town this station is associated with.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
virtual bool TileBelongsToRailStation(TileIndex tile) const =0
Check whether a specific tile belongs to this station.
TrackedViewportSign sign
NOSAVE: Dimensions of sign.
TimerGameCalendar::Date build_date
Date of construction.
std::string name
Custom name.
VehicleType type
Type of vehicle.
Class for storing amounts of cargo.
Definition cargo_type.h:113
Container for cargo from the same location and time.
Definition cargopacket.h:41
Specification of a cargo type.
Definition cargotype.h:74
CargoClasses classes
Classes of this cargo type.
Definition cargotype.h:81
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:108
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat station_rating
Fix station ratings at 100%.
Definition cheat_type.h:35
GUISettings gui
settings related to the GUI
uint32_t station
Count of company owned station tiles.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
uint32_t water
Count of company owned track bits for canals.
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
bool build_on_slopes
allow building on slopes
bool road_stop_on_town_road
allow building of drive-through road stops on town owned roads
bool road_stop_on_competitor_road
allow building of drive-through road stops on roads owned by competitors
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:58
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:43
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:44
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:64
IndustryType type
type of industry.
Definition industry.h:100
ProducedCargoes produced
produced cargo slots
Definition industry.h:95
Owner owner
owner of the industry. Which SHOULD always be (imho) OWNER_NONE
Definition industry.h:101
static Industry * GetByTile(TileIndex tile)
Get the industry of the given tile.
Definition industry.h:236
TileArea location
Location of the industry.
Definition industry.h:92
Station * neutral_station
Associated neutral station.
Definition industry.h:94
StationList stations_near
NOSAVE: List of nearby stations.
Definition industry.h:108
An edge in the link graph.
Definition linkgraph.h:42
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:278
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:269
static debug_inline uint Size()
Get the size of the map.
Definition map_func.h:287
Tindex class_index
Class index of this spec, invalid until class is allocated.
NewGRF supplied spritelayout.
bool NeedsPreprocessing() const
Tests whether this spritelayout needs preprocessing by SpriteLayoutProcessor, or whether it can be us...
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
Coordinates of a point in 2D.
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.
CargoGRFFileProps grf_prop
Link to NewGRF.
Money GetClearCost(Price category) const
Get the cost for clearing a road stop of this type.
A Stop for a Road Vehicle.
RoadStop * next
Next stop of the given type at this station.
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp:255
void MakeDriveThrough()
Join this road stop to another 'base' road stop if possible; fill all necessary data to become an act...
Definition roadstop.cpp:62
void ClearDriveThrough()
Prepare for removal of this stop; update other neighbouring stops if needed.
Definition roadstop.cpp:124
Buses, trucks and trams belong to this class.
Definition roadveh.h:98
uint8_t state
Definition roadveh.h:100
All ships have this type.
Definition ship.h:32
A location from where cargo can come from (or go to).
Definition source_type.h:32
static bool IsValidID(auto index)
Tests whether given index is a valid index for station of this type.
static bool IsExpected(const BaseStation *st)
Helper for checking whether the given station is of this type.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
Returns an iterable ensemble of all valid stations of type T.
static Station * Get(auto index)
Gets station with given index.
static Station * GetIfValid(auto index)
Returns station if the index is a valid index for this station type.
static Station * From(BaseStation *st)
Converts a BaseStation to SpecializedStation with type checking.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
T * Last()
Get the last vehicle in the chain.
bool HasSpriteGroups() const
Check whether the entity has sprite groups.
Information to handle station action 0 property 24 correctly.
std::bitset< NUM_INDUSTRYTYPES > indtypes
Bit set indicating when an industry type has been found.
std::bitset< STR_SV_STNAME_FALLBACK - STR_SV_STNAME > used_names
Used default station suffixes.
StationRect - used to track station spread out rectangle - cheaper than scanning whole map.
uint8_t station_spread
amount a station may spread
bool serve_neutral_industries
company stations can serve industries with attached neutral stations
bool distant_join_stations
allow to join non-adjacent stations
Station specification.
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.
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...
void GetTileArea(TileArea *ta, StationType type) const override
Get the tile area for a given station type.
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:38
uint16_t rail_speed
Speed limit of rail (bridges and track)
Definition tile_cmd.h:51
std::optional< std::string > grf
newGRF used for the tile contents
Definition tile_cmd.h:49
StringID station_name
Type of station within the class.
Definition tile_cmd.h:45
StringID str
Description of the tile.
Definition tile_cmd.h:39
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:43
std::array< Owner, 4 > owner
Name of the owner(s)
Definition tile_cmd.h:41
StringID airport_class
Name of the airport class.
Definition tile_cmd.h:46
StringID airport_name
Name of the airport.
Definition tile_cmd.h:47
uint16_t tram_speed
Speed limit of tram (bridges and track)
Definition tile_cmd.h:55
StringID roadtype
Type of road on the tile.
Definition tile_cmd.h:52
StringID tramtype
Type of tram on the tile.
Definition tile_cmd.h:54
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:50
uint16_t road_speed
Speed limit of road (bridges and track)
Definition tile_cmd.h:53
std::array< StringID, 4 > owner_type
Type of each owner.
Definition tile_cmd.h:42
StringID airport_tile_name
Name of the airport tile.
Definition tile_cmd.h:48
StringID station_class
Class of station.
Definition tile_cmd.h:44
A pair-construct of a TileIndexDiff.
Definition map_type.h:31
Tile information, used while rendering the tile.
Definition tile_cmd.h:29
int z
Height.
Definition tile_cmd.h:34
int x
X position of the tile in unit coordinates.
Definition tile_cmd.h:30
Slope tileh
Slope of the tile.
Definition tile_cmd.h:32
TileIndex tile
Tile index.
Definition tile_cmd.h:33
int y
Y position of the tile in unit coordinates.
Definition tile_cmd.h:31
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:144
Town data structure.
Definition town.h:52
CompanyMask statues
which companies have a statue?
Definition town.h:68
TileIndex xy
town center tile
Definition town.h:53
uint16_t noise_reached
level of noise that all the airports are generating
Definition town.h:66
uint16_t MaxTownNoise() const
Calculate the max town noise.
Definition town.h:123
CompanyID exclusivity
which company has exclusivity
Definition town.h:73
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:74
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
static const uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
@ 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
@ MP_TREES
Tile got trees.
Definition tile_type.h:52
@ MP_ROAD
A tile with road (or tram tracks)
Definition tile_type.h:50
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
@ MP_HOUSE
A house by a town.
Definition tile_type.h:51
@ MP_WATER
Water tile.
Definition tile_type.h:54
@ MP_INDUSTRY
Part of an industry.
Definition tile_type.h:56
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
Definition of Interval and OneShot timers.
Definition of the game-calendar-timer.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.
Base of the town class.
Town * ClosestTownFromTile(TileIndex tile, uint threshold)
Return the town closest (in distance or ownership) to a given tile, within a given threshold.
Town * CalcClosestTownFromTile(TileIndex tile, uint threshold=UINT_MAX)
Return the town closest to the given tile within threshold.
HouseZone GetTownRadiusGroup(const Town *t, TileIndex tile)
Returns the bit corresponding to the town zone of the specified tile.
CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlags flags)
Checks whether the local authority allows construction of a new station (rail, road,...
TrackBits TrackToTrackBits(Track track)
Maps a Track to the corresponding TrackBits value.
Definition track_func.h:77
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
bool IsReversingRoadTrackdir(Trackdir dir)
Checks whether the trackdir means that we are reversing.
Definition track_func.h:673
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:247
TrackStatus CombineTrackStatus(TrackdirBits trackdirbits, TrackdirBits red_signals)
Builds a TrackStatus.
Definition track_func.h:388
TrackBits AxisToTrackBits(Axis a)
Maps an Axis to the corresponding TrackBits value.
Definition track_func.h:88
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:524
Track AxisToTrack(Axis a)
Convert an Axis to the corresponding Track AXIS_X -> TRACK_X AXIS_Y -> TRACK_Y Uses the fact that the...
Definition track_func.h:66
Track RemoveFirstTrack(TrackBits *tracks)
Removes first Track from TrackBits and returns it.
Definition track_func.h:131
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:439
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:39
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:41
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:38
@ TRACK_BIT_NONE
No track.
Definition track_type.h:36
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:37
@ TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:50
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:42
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
Track
These are used to specify a single track.
Definition track_type.h:19
@ TRACK_Y
Track along the y-axis (north-west to south-east)
Definition track_type.h:22
@ TRACK_X
Track along the x-axis (north-east to south-west)
Definition track_type.h:21
Base for the train class.
bool TryPathReserve(Train *v, bool mark_as_stuck=false, bool first_tile_okay=false)
Try to reserve a path to a safe position.
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length)
Get the stop location of (the center) of the front vehicle of a train at a platform of a station.
@ TO_BUILDINGS
company buildings - depots, stations, HQ, ...
TransportType
Available types of transport.
@ TRANSPORT_RAIL
Transport by train.
@ TRANSPORT_ROAD
Transport by road vehicle.
@ TRANSPORT_WATER
Transport over water.
Functions that have tunnels and bridges in common.
CommandCost EnsureNoVehicleOnGround(TileIndex tile)
Ensure there is no vehicle at the ground at the given position.
Definition vehicle.cpp:527
@ TrainSlowing
Train is slowing down.
Functions related to vehicles.
@ VEH_INVALID
Non-existing type of vehicle.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
Functions and type for generating vehicle lists.
void FindVehiclesWithOrder(VehiclePredicate veh_pred, OrderPredicate ord_pred, VehicleFunc veh_func)
Find vehicles matching an order.
void OffsetGroundSprite(int x, int y)
Called when a foundation has been drawn for the current tile.
Definition viewport.cpp:591
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:759
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, 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:769
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:579
Functions related to (drawing on) viewports.
Functions related to water (management)
void TileLoop_Water(TileIndex tile)
Let a water tile floods its diagonal adjoining tiles called from tunnelbridge_cmd,...
void CheckForDockingTile(TileIndex t)
Mark the supplied tile as a docking tile if it is suitable for docking.
bool HasTileWaterGround(Tile t)
Checks whether the tile has water at the ground.
Definition water_map.h:350
bool IsTileOnWater(Tile t)
Tests if the tile was built on water.
Definition water_map.h:136
WaterClass
classes of water (for WATER_TILE_CLEAR water tile type).
Definition water_map.h:39
@ WATER_CLASS_SEA
Sea.
Definition water_map.h:40
@ WATER_CLASS_CANAL
Canal.
Definition water_map.h:41
@ WATER_CLASS_INVALID
Used for industry tiles on land (also for oilrig if newgrf says so).
Definition water_map.h:43
bool HasTileWaterClass(Tile t)
Checks whether the tile has an waterclass associated.
Definition water_map.h:101
WaterClass GetWaterClass(Tile t)
Get the water class at a tile.
Definition water_map.h:112
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:371
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:361
bool IsWater(Tile t)
Is it a plain water tile?
Definition water_map.h:147
bool IsWaterTile(Tile t)
Is it a water tile with plain water?
Definition water_map.h:190
Base of waypoints.
@ WPF_ROAD
This is a road waypoint.
CommandCost RemoveBuoy(TileIndex tile, DoCommandFlags flags)
Remove a buoy.
Command definitions related to waypoints.
Functions related to waypoints.
void ShowWaypointWindow(const Waypoint *wp)
Show the window for the given waypoint.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1182
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3173
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:3265
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:3160
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3147
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:3282
@ 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.