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