OpenTTD Source 20250312-master-gcdcc6b491d
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 "aircraft.h"
12#include "bridge_map.h"
13#include "vehiclelist_func.h"
14#include "viewport_func.h"
15#include "viewport_kdtree.h"
16#include "command_func.h"
17#include "town.h"
18#include "news_func.h"
19#include "train.h"
20#include "ship.h"
21#include "roadveh.h"
22#include "industry.h"
23#include "newgrf_cargo.h"
24#include "newgrf_debug.h"
25#include "newgrf_station.h"
26#include "newgrf_canal.h" /* For the buoy */
28#include "road_internal.h" /* For drawing catenary/checking road removal */
29#include "autoslope.h"
30#include "water.h"
31#include "strings_func.h"
32#include "clear_func.h"
34#include "vehicle_func.h"
35#include "string_func.h"
36#include "animated_tile_func.h"
37#include "elrail_func.h"
38#include "station_base.h"
39#include "station_func.h"
40#include "station_kdtree.h"
41#include "roadstop_base.h"
42#include "newgrf_railtype.h"
43#include "newgrf_roadtype.h"
44#include "waypoint_base.h"
45#include "waypoint_func.h"
46#include "pbs.h"
47#include "debug.h"
48#include "core/random_func.hpp"
50#include "company_base.h"
52#include "newgrf_airporttiles.h"
53#include "order_backup.h"
54#include "newgrf_house.h"
55#include "company_gui.h"
57#include "linkgraph/refresh.h"
58#include "tunnelbridge_map.h"
59#include "station_cmd.h"
60#include "waypoint_cmd.h"
61#include "landscape_cmd.h"
62#include "rail_cmd.h"
63#include "newgrf_roadstop.h"
64#include "timer/timer.h"
68#include "cheat_type.h"
69#include "road_func.h"
70
72
73#include "table/strings.h"
74
75#include <bitset>
76
77#include "safeguards.h"
78
84/* static */ const FlowStat::SharesMap FlowStat::empty_sharesmap;
85
93{
94 assert(IsTileType(t, MP_STATION));
95
96 /* If the tile isn't an airport there's no chance it's a hangar. */
97 if (!IsAirport(t)) return false;
98
99 const Station *st = Station::GetByTile(t);
100 const AirportSpec *as = st->airport.GetSpec();
101
102 for (const auto &depot : as->depots) {
103 if (st->airport.GetRotatedTileFromOffset(depot.ti) == TileIndex(t)) return true;
104 }
105
106 return false;
107}
108
118template <class T, class F>
119CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
120{
121 ta.Expand(1);
122
123 /* check around to see if there are any stations there owned by the company */
124 for (TileIndex tile_cur : ta) {
125 if (IsTileType(tile_cur, MP_STATION)) {
126 StationID t = GetStationIndex(tile_cur);
127 if (!T::IsValidID(t) || T::Get(t)->owner != company || !filter(T::Get(t))) continue;
128 if (closest_station == StationID::Invalid()) {
129 closest_station = t;
130 } else if (closest_station != t) {
131 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
132 }
133 }
134 }
135 *st = (closest_station == StationID::Invalid()) ? nullptr : T::Get(closest_station);
136 return CommandCost();
137}
138
144typedef bool (*CMSAMatcher)(TileIndex tile);
145
153{
154 int num = 0;
155
156 for (int dx = -3; dx <= 3; dx++) {
157 for (int dy = -3; dy <= 3; dy++) {
158 TileIndex t = TileAddWrap(tile, dx, dy);
159 if (t != INVALID_TILE && cmp(t)) num++;
160 }
161 }
162
163 return num;
164}
165
171static bool CMSAMine(TileIndex tile)
172{
173 /* No industry */
174 if (!IsTileType(tile, MP_INDUSTRY)) return false;
175
176 const Industry *ind = Industry::GetByTile(tile);
177
178 /* No extractive industry */
180
181 for (const auto &p : ind->produced) {
182 /* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine.
183 * Also the production of passengers and mail is ignored. */
184 if (IsValidCargoType(p.cargo) &&
186 return true;
187 }
188 }
189
190 return false;
191}
192
198static bool CMSAWater(TileIndex tile)
199{
200 return IsTileType(tile, MP_WATER) && IsWater(tile);
201}
202
208static bool CMSATree(TileIndex tile)
209{
210 return IsTileType(tile, MP_TREES);
211}
212
213#define M(x) ((x) - STR_SV_STNAME)
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 uint32_t free_names;
227 std::bitset<NUM_INDUSTRYTYPES> indtypes;
228};
229
238static bool FindNearIndustryName(TileIndex tile, void *user_data)
239{
240 /* All already found industry types */
242 if (!IsTileType(tile, MP_INDUSTRY)) return false;
243
244 /* If the station name is undefined it means that it doesn't name a station */
245 IndustryType indtype = GetIndustryType(tile);
246 if (GetIndustrySpec(indtype)->station_name == STR_UNDEFINED) return false;
247
248 /* In all cases if an industry that provides a name is found two of
249 * the standard names will be disabled. */
250 sni->free_names &= ~(1 << M(STR_SV_STNAME_OILFIELD) | 1 << M(STR_SV_STNAME_MINES));
251 return !sni->indtypes[indtype];
252}
253
254static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
255{
256 static const uint32_t _gen_station_name_bits[] = {
257 0, // STATIONNAMING_RAIL
258 0, // STATIONNAMING_ROAD
259 1U << M(STR_SV_STNAME_AIRPORT), // STATIONNAMING_AIRPORT
260 1U << M(STR_SV_STNAME_OILFIELD), // STATIONNAMING_OILRIG
261 1U << M(STR_SV_STNAME_DOCKS), // STATIONNAMING_DOCK
262 1U << M(STR_SV_STNAME_HELIPORT), // STATIONNAMING_HELIPORT
263 };
264
265 const Town *t = st->town;
266
268 sni.free_names = UINT32_MAX;
269
270 for (const Station *s : Station::Iterate()) {
271 if (s != st && s->town == t) {
272 if (s->indtype != IT_INVALID) {
273 sni.indtypes[s->indtype] = true;
274 StringID name = GetIndustrySpec(s->indtype)->station_name;
275 if (name != STR_UNDEFINED) {
276 /* Filter for other industrytypes with the same name */
277 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
278 const IndustrySpec *indsp = GetIndustrySpec(it);
279 if (indsp->enabled && indsp->station_name == name) sni.indtypes[it] = true;
280 }
281 }
282 continue;
283 }
284 uint str = M(s->string_id);
285 if (str <= 0x20) {
286 if (str == M(STR_SV_STNAME_FOREST)) {
287 str = M(STR_SV_STNAME_WOODS);
288 }
289 ClrBit(sni.free_names, str);
290 }
291 }
292 }
293
294 TileIndex indtile = tile;
295 if (CircularTileSearch(&indtile, 7, FindNearIndustryName, &sni)) {
296 /* An industry has been found nearby */
297 IndustryType indtype = GetIndustryType(indtile);
298 const IndustrySpec *indsp = GetIndustrySpec(indtype);
299 /* STR_NULL means it only disables oil rig/mines */
300 if (indsp->station_name != STR_NULL) {
301 st->indtype = indtype;
302 return STR_SV_STNAME_FALLBACK;
303 }
304 }
305
306 /* Oil rigs/mines name could be marked not free by looking for a near by industry. */
307
308 /* check default names */
309 uint32_t tmp = sni.free_names & _gen_station_name_bits[name_class];
310 if (tmp != 0) return STR_SV_STNAME + FindFirstBit(tmp);
311
312 /* check mine? */
313 if (HasBit(sni.free_names, M(STR_SV_STNAME_MINES))) {
314 if (CountMapSquareAround(tile, CMSAMine) >= 2) {
315 return STR_SV_STNAME_MINES;
316 }
317 }
318
319 /* check close enough to town to get central as name? */
320 if (DistanceMax(tile, t->xy) < 8) {
321 if (HasBit(sni.free_names, M(STR_SV_STNAME))) return STR_SV_STNAME;
322
323 if (HasBit(sni.free_names, M(STR_SV_STNAME_CENTRAL))) return STR_SV_STNAME_CENTRAL;
324 }
325
326 /* Check lakeside */
327 if (HasBit(sni.free_names, M(STR_SV_STNAME_LAKESIDE)) &&
328 DistanceFromEdge(tile) < 20 &&
329 CountMapSquareAround(tile, CMSAWater) >= 5) {
330 return STR_SV_STNAME_LAKESIDE;
331 }
332
333 /* Check woods */
334 if (HasBit(sni.free_names, M(STR_SV_STNAME_WOODS)) && (
335 CountMapSquareAround(tile, CMSATree) >= 8 ||
337 ) {
338 return _settings_game.game_creation.landscape == LandscapeType::Tropic ? STR_SV_STNAME_FOREST : STR_SV_STNAME_WOODS;
339 }
340
341 /* check elevation compared to town */
342 int z = GetTileZ(tile);
343 int z2 = GetTileZ(t->xy);
344 if (z < z2) {
345 if (HasBit(sni.free_names, M(STR_SV_STNAME_VALLEY))) return STR_SV_STNAME_VALLEY;
346 } else if (z > z2) {
347 if (HasBit(sni.free_names, M(STR_SV_STNAME_HEIGHTS))) return STR_SV_STNAME_HEIGHTS;
348 }
349
350 /* check direction compared to town */
351 static const int8_t _direction_and_table[] = {
352 ~( (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
353 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
354 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_EAST)) | (1 << M(STR_SV_STNAME_NORTH)) ),
355 ~( (1 << M(STR_SV_STNAME_SOUTH)) | (1 << M(STR_SV_STNAME_WEST)) | (1 << M(STR_SV_STNAME_EAST)) ),
356 };
357
358 sni.free_names &= _direction_and_table[
359 (TileX(tile) < TileX(t->xy)) +
360 (TileY(tile) < TileY(t->xy)) * 2];
361
363 static const uint32_t fallback_names = (
364 (1U << M(STR_SV_STNAME_NORTH)) |
365 (1U << M(STR_SV_STNAME_SOUTH)) |
366 (1U << M(STR_SV_STNAME_EAST)) |
367 (1U << M(STR_SV_STNAME_WEST)) |
368 (1U << M(STR_SV_STNAME_TRANSFER)) |
369 (1U << M(STR_SV_STNAME_HALT)) |
370 (1U << M(STR_SV_STNAME_EXCHANGE)) |
371 (1U << M(STR_SV_STNAME_ANNEXE)) |
372 (1U << M(STR_SV_STNAME_SIDINGS)) |
373 (1U << M(STR_SV_STNAME_BRANCH)) |
374 (1U << M(STR_SV_STNAME_UPPER)) |
375 (1U << M(STR_SV_STNAME_LOWER))
376 );
377
378 sni.free_names &= fallback_names;
379 return (sni.free_names == 0) ? STR_SV_STNAME_FALLBACK : (STR_SV_STNAME + FindFirstBit(sni.free_names));
380}
381#undef M
382
389{
390 uint threshold = 8;
391
392 Station *best_station = nullptr;
393 ForAllStationsRadius(tile, threshold, [&](Station *st) {
394 if (!st->IsInUse() && st->owner == _current_company) {
395 uint cur_dist = DistanceManhattan(tile, st->xy);
396
397 if (cur_dist < threshold) {
398 threshold = cur_dist;
399 best_station = st;
400 } else if (cur_dist == threshold && best_station != nullptr) {
401 /* In case of a tie, lowest station ID wins */
402 if (st->index < best_station->index) best_station = st;
403 }
404 }
405 });
406
407 return best_station;
408}
409
410
412{
413 switch (type) {
414 case StationType::Rail:
415 *ta = this->train_station;
416 return;
417
418 case StationType::Airport:
419 *ta = this->airport;
420 return;
421
422 case StationType::Truck:
423 *ta = this->truck_station;
424 return;
425
426 case StationType::Bus:
427 *ta = this->bus_station;
428 return;
429
430 case StationType::Dock:
431 case StationType::Oilrig:
432 *ta = this->docking_station;
433 return;
434
435 default: NOT_REACHED();
436 }
437}
438
443{
444 Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
445
446 pt.y -= 32 * ZOOM_BASE;
447 if (this->facilities.Test(StationFacility::Airport) && this->airport.type == AT_OILRIG) pt.y -= 16 * ZOOM_BASE;
448
449 if (this->sign.kdtree_valid) _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeStation(this->index));
450
451 this->sign.UpdatePosition(pt.x, pt.y, GetString(STR_VIEWPORT_STATION, this->index, this->facilities), GetString(STR_STATION_NAME, this->index, this->facilities));
452
453 _viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeStation(this->index));
454
456}
457
463{
464 if (this->xy == new_xy) return;
465
466 _station_kdtree.Remove(this->index);
467
468 this->BaseStation::MoveSign(new_xy);
469
470 _station_kdtree.Insert(this->index);
471}
472
475{
476 for (BaseStation *st : BaseStation::Iterate()) {
477 st->UpdateVirtCoord();
478 }
479}
480
481void BaseStation::FillCachedName() const
482{
483 auto tmp_params = MakeParameters(this->index);
484 this->cached_name = GetStringWithArgs(Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params);
485}
486
487void ClearAllStationCachedNames()
488{
489 for (BaseStation *st : BaseStation::Iterate()) {
490 st->cached_name.clear();
491 }
492}
493
499CargoTypes GetAcceptanceMask(const Station *st)
500{
501 CargoTypes mask = 0;
502
503 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
504 if (HasBit(it->status, GoodsEntry::GES_ACCEPTANCE)) SetBit(mask, std::distance(std::begin(st->goods), it));
505 }
506 return mask;
507}
508
514CargoTypes GetEmptyMask(const Station *st)
515{
516 CargoTypes mask = 0;
517
518 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
519 if (!it->HasData() || it->GetData().cargo.TotalCount() == 0) SetBit(mask, std::distance(std::begin(st->goods), it));
520 }
521 return mask;
522}
523
530static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
531{
532 StringID msg = reject ? STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_LIST : STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST;
534}
535
543CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
544{
545 CargoArray produced{};
546 std::set<IndustryID> industries;
547 TileArea ta = TileArea(north_tile, w, h).Expand(rad);
548
549 /* Loop over all tiles to get the produced cargo of
550 * everything except industries */
551 for (TileIndex tile : ta) {
552 if (IsTileType(tile, MP_INDUSTRY)) industries.insert(GetIndustryIndex(tile));
553 AddProducedCargo(tile, produced);
554 }
555
556 /* Loop over the seen industries. They produce cargo for
557 * anything that is within 'rad' of any one of their tiles.
558 */
559 for (IndustryID industry : industries) {
560 const Industry *i = Industry::Get(industry);
561 /* Skip industry with neutral station */
562 if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) continue;
563
564 for (const auto &p : i->produced) {
565 if (IsValidCargoType(p.cargo)) produced[p.cargo]++;
566 }
567 }
568
569 return produced;
570}
571
581CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted)
582{
583 CargoArray acceptance{};
584 if (always_accepted != nullptr) *always_accepted = 0;
585
586 TileArea ta = TileArea(center_tile, w, h).Expand(rad);
587
588 for (TileIndex tile : ta) {
589 /* Ignore industry if it has a neutral station. */
591
592 AddAcceptedCargo(tile, acceptance, always_accepted);
593 }
594
595 return acceptance;
596}
597
603static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
604{
605 CargoArray acceptance{};
606 if (always_accepted != nullptr) *always_accepted = 0;
607
609 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
610 AddAcceptedCargo(tile, acceptance, always_accepted);
611 }
612
613 return acceptance;
614}
615
621void UpdateStationAcceptance(Station *st, bool show_msg)
622{
623 /* old accepted goods types */
624 CargoTypes old_acc = GetAcceptanceMask(st);
625
626 /* And retrieve the acceptance. */
627 CargoArray acceptance{};
628 if (!st->rect.IsEmpty()) {
629 acceptance = GetAcceptanceAroundStation(st, &st->always_accepted);
630 }
631
632 /* Adjust in case our station only accepts fewer kinds of goods */
633 for (CargoType i = 0; i < NUM_CARGO; i++) {
634 uint amt = acceptance[i];
635
636 /* Make sure the station can accept the goods type. */
637 bool is_passengers = IsCargoInClass(i, CargoClass::Passengers);
638 if ((!is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::TruckStop, StationFacility::Airport, StationFacility::Dock})) ||
639 (is_passengers && !st->facilities.Any({StationFacility::Train, StationFacility::BusStop, StationFacility::Airport, StationFacility::Dock}))) {
640 amt = 0;
641 }
642
643 GoodsEntry &ge = st->goods[i];
644 SB(ge.status, GoodsEntry::GES_ACCEPTANCE, 1, amt >= 8);
646 (*LinkGraph::Get(ge.link_graph))[ge.node].SetDemand(amt / 8);
647 }
648 }
649
650 /* Only show a message in case the acceptance was actually changed. */
651 CargoTypes new_acc = GetAcceptanceMask(st);
652 if (old_acc == new_acc) return;
653
654 /* show a message to report that the acceptance was changed? */
655 if (show_msg && st->owner == _local_company && st->IsInUse()) {
656 /* Combine old and new masks to get changes */
657 CargoTypes accepts = new_acc & ~old_acc;
658 CargoTypes rejects = ~new_acc & old_acc;
659
660 /* Show news message if there are any changes */
661 if (accepts != 0) ShowRejectOrAcceptNews(st, accepts, false);
662 if (rejects != 0) ShowRejectOrAcceptNews(st, rejects, true);
663 }
664
665 /* redraw the station view since acceptance changed */
667}
668
669static void UpdateStationSignCoord(BaseStation *st)
670{
671 const StationRect *r = &st->rect;
672
673 if (r->IsEmpty()) return; // no tiles belong to this station
674
675 /* clamp sign coord to be inside the station rect */
676 TileIndex new_xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
677 st->MoveSign(new_xy);
678
679 if (!Station::IsExpected(st)) return;
680 Station *full_station = Station::From(st);
681 for (const GoodsEntry &ge : full_station->goods) {
682 LinkGraphID lg = ge.link_graph;
683 if (!LinkGraph::IsValidID(lg)) continue;
684 (*LinkGraph::Get(lg))[ge.node].UpdateLocation(st->xy);
685 }
686}
687
697static CommandCost BuildStationPart(Station **st, DoCommandFlags flags, bool reuse, TileArea area, StationNaming name_class)
698{
699 /* Find a deleted station close to us */
700 if (*st == nullptr && reuse) *st = GetClosestDeletedStation(area.tile);
701
702 if (*st != nullptr) {
703 if ((*st)->owner != _current_company) {
704 return CommandCost(CMD_ERROR);
705 }
706
707 CommandCost ret = (*st)->rect.BeforeAddRect(area.tile, area.w, area.h, StationRect::ADD_TEST);
708 if (ret.Failed()) return ret;
709 } else {
710 /* allocate and initialize new station */
711 if (!Station::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_STATIONS_LOADING);
712
713 if (flags.Test(DoCommandFlag::Execute)) {
714 *st = new Station(area.tile);
715 _station_kdtree.Insert((*st)->index);
716
717 (*st)->town = ClosestTownFromTile(area.tile, UINT_MAX);
718 (*st)->string_id = GenerateStationName(*st, area.tile, name_class);
719
721 (*st)->town->have_ratings.Set(_current_company);
722 }
723 }
724 }
725 return CommandCost();
726}
727
735{
736 if (!st->IsInUse()) {
737 st->delete_ctr = 0;
739 }
740 /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
741 UpdateStationSignCoord(st);
742}
743
750{
751 this->UpdateVirtCoord();
753
754 if (adding) {
755 this->RecomputeCatchment();
756 MarkCatchmentTilesDirty();
758 } else {
759 MarkCatchmentTilesDirty();
760 }
761
762 switch (type) {
763 case StationType::Rail:
765 break;
766 case StationType::Airport:
767 break;
768 case StationType::Truck:
769 case StationType::Bus:
771 break;
772 case StationType::Dock:
774 break;
775 default: NOT_REACHED();
776 }
777
778 if (adding) {
779 UpdateStationAcceptance(this, false);
781 } else {
783 this->RecomputeCatchment();
784 }
785
786}
787
789
799CommandCost CheckBuildableTile(TileIndex tile, uint invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge = true)
800{
801 if (check_bridge && IsBridgeAbove(tile)) {
802 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
803 }
804
806 if (ret.Failed()) return ret;
807
808 auto [tileh, z] = GetTileSlopeZ(tile);
809
810 /* Prohibit building if
811 * 1) The tile is "steep" (i.e. stretches two height levels).
812 * 2) The tile is non-flat and the build_on_slopes switch is disabled.
813 */
814 if ((!allow_steep && IsSteepSlope(tileh)) ||
816 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
817 }
818
820 int flat_z = z + GetSlopeMaxZ(tileh);
821 if (tileh != SLOPE_FLAT) {
822 /* Forbid building if the tile faces a slope in a invalid direction. */
823 for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) {
824 if (HasBit(invalid_dirs, dir) && !CanBuildDepotByTileh(dir, tileh)) {
825 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
826 }
827 }
828 cost.AddCost(_price[PR_BUILD_FOUNDATION]);
829 }
830
831 /* The level of this tile must be equal to allowed_z. */
832 if (allowed_z < 0) {
833 /* First tile. */
834 allowed_z = flat_z;
835 } else if (allowed_z != flat_z) {
836 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
837 }
838
839 return cost;
840}
841
849{
851 int allowed_z = -1;
852
853 for (; tile_iter != INVALID_TILE; ++tile_iter) {
854 CommandCost ret = CheckBuildableTile(tile_iter, 0, allowed_z, true);
855 if (ret.Failed()) return ret;
856 cost.AddCost(ret);
857
858 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_iter);
859 if (ret.Failed()) return ret;
860 cost.AddCost(ret);
861 }
862
863 return cost;
864}
865
882static 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)
883{
885 uint invalid_dirs = 5 << axis;
886
887 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
888 bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
889
890 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false);
891 if (ret.Failed()) return ret;
892 cost.AddCost(ret);
893
894 if (slope_cb) {
895 /* Do slope check if requested. */
896 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
897 if (ret.Failed()) return ret;
898 }
899
900 /* if station is set, then we have special handling to allow building on top of already existing stations.
901 * so station points to StationID::Invalid() if we can build on any station.
902 * Or it points to a station if we're only allowed to build on exactly that station. */
903 if (station != nullptr && IsTileType(tile_cur, MP_STATION)) {
904 if (!IsRailStation(tile_cur)) {
905 return ClearTile_Station(tile_cur, DoCommandFlag::Auto); // get error message
906 } else {
907 StationID st = GetStationIndex(tile_cur);
908 if (*station == StationID::Invalid()) {
909 *station = st;
910 } else if (*station != st) {
911 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
912 }
913 }
914 } else {
915 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
916 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
917 /* Don't overbuild signals. */
918 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
919
920 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
921 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
922 TrackBits tracks = GetTrackBits(tile_cur);
923 Track track = RemoveFirstTrack(&tracks);
924 Track expected_track = HasBit(invalid_dirs, DIAGDIR_NE) ? TRACK_X : TRACK_Y;
925
926 /* The existing track must align with the desired station axis. */
927 if (tracks == TRACK_BIT_NONE && track == expected_track) {
928 /* Check for trains having a reservation for this tile. */
929 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
930 Train *v = GetTrainForReservation(tile_cur, track);
931 if (v != nullptr) {
932 affected_vehicles.push_back(v);
933 }
934 }
935 ret = Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile_cur, track);
936 if (ret.Failed()) return ret;
937 cost.AddCost(ret);
938 /* With DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) CmdLandscapeClear would fail since the rail still exists */
939 return cost;
940 }
941 }
942 }
943 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
944 if (ret.Failed()) return ret;
945 cost.AddCost(ret);
946 }
947
948 return cost;
949}
950
964CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlags flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt)
965{
967
968 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through);
969 if (ret.Failed()) return ret;
970 cost.AddCost(ret);
971
972 /* If station is set, then we have special handling to allow building on top of already existing stations.
973 * Station points to StationID::Invalid() if we can build on any station.
974 * Or it points to a station if we're only allowed to build on exactly that station. */
975 if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
976 if (!IsAnyRoadStop(cur_tile)) {
977 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
978 } else {
979 if (station_type != GetStationType(cur_tile) ||
980 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
981 return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
982 }
983 /* Drive-through station in the wrong direction. */
984 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
985 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
986 }
987 StationID st = GetStationIndex(cur_tile);
988 if (*station == StationID::Invalid()) {
989 *station = st;
990 } else if (*station != st) {
991 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
992 }
993 }
994 } else {
995 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
996 /* Road bits in the wrong direction. */
997 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
998 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
999 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1000 switch (CountBits(rb)) {
1001 case 1:
1002 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1003
1004 case 2:
1005 if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1006 return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
1007
1008 default: // 3 or 4
1009 return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
1010 }
1011 }
1012
1013 if (build_over_road) {
1014 /* There is a road, check if we can build road+tram stop over it. */
1015 RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
1016 if (road_rt != INVALID_ROADTYPE) {
1017 Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
1018 if (road_owner == OWNER_TOWN) {
1019 if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
1021 ret = CheckOwnership(road_owner);
1022 if (ret.Failed()) return ret;
1023 }
1024 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
1025
1026 if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1027
1028 if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
1029 ret = CheckOwnership(road_owner);
1030 if (ret.Failed()) return ret;
1031 }
1032
1033 cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
1034 } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
1035 cost.AddCost(RoadBuildCost(rt) * 2);
1036 }
1037
1038 /* There is a tram, check if we can build road+tram stop over it. */
1039 RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
1040 if (tram_rt != INVALID_ROADTYPE) {
1041 Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
1042 if (Company::IsValidID(tram_owner) &&
1044 /* Disallow breaking end-of-line of someone else
1045 * so trams can still reverse on this tile. */
1046 HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
1047 ret = CheckOwnership(tram_owner);
1048 if (ret.Failed()) return ret;
1049 }
1050 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
1051
1052 if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1053
1054 cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
1055 } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
1056 cost.AddCost(RoadBuildCost(rt) * 2);
1057 }
1058 } else if (rt == INVALID_ROADTYPE) {
1059 return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
1060 } else {
1061 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
1062 if (ret.Failed()) return ret;
1063 cost.AddCost(ret);
1064 cost.AddCost(RoadBuildCost(rt) * 2);
1065 }
1066 }
1067
1068 return cost;
1069}
1070
1078{
1079 TileArea cur_ta = st->train_station;
1080
1081 /* determine new size of train station region.. */
1082 int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
1083 int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
1084 new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
1085 new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
1086 new_ta.tile = TileXY(x, y);
1087
1088 /* make sure the final size is not too big. */
1090 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1091 }
1092
1093 return CommandCost();
1094}
1095
1096static inline uint8_t *CreateSingle(uint8_t *layout, int n)
1097{
1098 int i = n;
1099 do *layout++ = 0; while (--i);
1100 layout[((n - 1) >> 1) - n] = 2;
1101 return layout;
1102}
1103
1104static inline uint8_t *CreateMulti(uint8_t *layout, int n, uint8_t b)
1105{
1106 int i = n;
1107 do *layout++ = b; while (--i);
1108 if (n > 4) {
1109 layout[0 - n] = 0;
1110 layout[n - 1 - n] = 0;
1111 }
1112 return layout;
1113}
1114
1122void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const StationSpec *statspec)
1123{
1124 if (statspec != nullptr) {
1125 auto found = statspec->layouts.find(GetStationLayoutKey(numtracks, plat_len));
1126 if (found != std::end(statspec->layouts)) {
1127 /* Custom layout defined, copy to buffer. */
1128 std::copy(std::begin(found->second), std::end(found->second), layout);
1129 return;
1130 }
1131 }
1132
1133 if (plat_len == 1) {
1134 CreateSingle(layout, numtracks);
1135 } else {
1136 if (numtracks & 1) layout = CreateSingle(layout, plat_len);
1137 int n = numtracks >> 1;
1138
1139 while (--n >= 0) {
1140 layout = CreateMulti(layout, plat_len, 4);
1141 layout = CreateMulti(layout, plat_len, 6);
1142 }
1143 }
1144}
1145
1158template <class T, StringID error_message, class F>
1159CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1160{
1161 assert(*st == nullptr);
1162 bool check_surrounding = true;
1163
1164 if (existing_station != StationID::Invalid()) {
1165 if (adjacent && existing_station != station_to_join) {
1166 /* You can't build an adjacent station over the top of one that
1167 * already exists. */
1168 return CommandCost(error_message);
1169 } else {
1170 /* Extend the current station, and don't check whether it will
1171 * be near any other stations. */
1172 T *candidate = T::GetIfValid(existing_station);
1173 if (candidate != nullptr && filter(candidate)) *st = candidate;
1174 check_surrounding = (*st == nullptr);
1175 }
1176 } else {
1177 /* There's no station here. Don't check the tiles surrounding this
1178 * one if the company wanted to build an adjacent station. */
1179 if (adjacent) check_surrounding = false;
1180 }
1181
1182 if (check_surrounding) {
1183 /* Make sure there is no more than one other station around us that is owned by us. */
1184 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1185 if (ret.Failed()) return ret;
1186 }
1187
1188 /* Distant join */
1189 if (*st == nullptr && station_to_join != StationID::Invalid()) *st = T::GetIfValid(station_to_join);
1190
1191 return CommandCost();
1192}
1193
1203static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1204{
1205 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1206}
1207
1218CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1219{
1220 if (is_road) {
1221 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); });
1222 } else {
1223 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); });
1224 }
1225}
1226
1238
1250
1265static 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)
1266{
1268 bool length_price_ready = true;
1269 uint8_t tracknum = 0;
1270 int allowed_z = -1;
1271 for (TileIndex cur_tile : tile_area) {
1272 /* Clear the land below the station. */
1273 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1274 if (ret.Failed()) return ret;
1275
1276 /* Only add _price[PR_BUILD_STATION_RAIL_LENGTH] once for each valid plat_len. */
1277 if (tracknum == numtracks) {
1278 length_price_ready = true;
1279 tracknum = 0;
1280 } else {
1281 tracknum++;
1282 }
1283
1284 /* AddCost for new or rotated rail stations. */
1285 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1286 cost.AddCost(ret);
1287 cost.AddCost(_price[PR_BUILD_STATION_RAIL]);
1288 cost.AddCost(RailBuildCost(rt));
1289
1290 if (length_price_ready) {
1291 cost.AddCost(_price[PR_BUILD_STATION_RAIL_LENGTH]);
1292 length_price_ready = false;
1293 }
1294 }
1295 }
1296
1297 return cost;
1298}
1299
1307{
1308 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1309 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlag::Pylons : StationSpec::TileFlags{};
1310 return statspec->tileflags[gfx];
1311}
1312
1319{
1320 const auto flags = GetStationTileFlags(GetStationGfx(tile), statspec);
1324}
1325
1340CommandCost 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)
1341{
1342 /* Does the authority allow this? */
1343 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1344 if (ret.Failed()) return ret;
1345
1346 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1347
1348 /* Check if the given station class is valid */
1349 if (static_cast<uint>(spec_class) >= StationClass::GetClassCount()) return CMD_ERROR;
1350 const StationClass *cls = StationClass::Get(spec_class);
1351 if (IsWaypointClass(*cls)) return CMD_ERROR;
1352 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1353 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1354
1355 int w_org, h_org;
1356 if (axis == AXIS_X) {
1357 w_org = plat_len;
1358 h_org = numtracks;
1359 } else {
1360 h_org = plat_len;
1361 w_org = numtracks;
1362 }
1363
1364 bool reuse = (station_to_join != NEW_STATION);
1365 if (!reuse) station_to_join = StationID::Invalid();
1366 bool distant_join = (station_to_join != StationID::Invalid());
1367
1368 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1369
1371
1372 /* these values are those that will be stored in train_tile and station_platforms */
1373 TileArea new_location(tile_org, w_org, h_org);
1374
1375 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1376 StationID est = StationID::Invalid();
1377 std::vector<Train *> affected_vehicles;
1378 /* Add construction and clearing expenses. */
1379 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1380 if (cost.Failed()) return cost;
1381
1382 Station *st = nullptr;
1383 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1384 if (ret.Failed()) return ret;
1385
1386 ret = BuildStationPart(&st, flags, reuse, new_location, STATIONNAMING_RAIL);
1387 if (ret.Failed()) return ret;
1388
1389 if (st != nullptr && st->train_station.tile != INVALID_TILE) {
1390 ret = CanExpandRailStation(st, new_location);
1391 if (ret.Failed()) return ret;
1392 }
1393
1394 /* Check if we can allocate a custom stationspec to this station */
1395 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1396 int specindex = AllocateSpecToStation(statspec, st, flags.Test(DoCommandFlag::Execute));
1397 if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1398
1399 if (statspec != nullptr) {
1400 /* Perform NewStation checks */
1401
1402 /* Check if the station size is permitted */
1403 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1404 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1405
1406 /* Check if the station is buildable */
1408 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1410 }
1411 }
1412
1413 if (flags.Test(DoCommandFlag::Execute)) {
1414 st->train_station = new_location;
1415 st->AddFacility(StationFacility::Train, new_location.tile);
1416
1417 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
1418
1419 if (statspec != nullptr) {
1420 /* Include this station spec's animation trigger bitmask
1421 * in the station's cached copy. */
1422 st->cached_anim_triggers |= statspec->animation.triggers;
1423 }
1424
1425 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1426 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1427 Track track = AxisToTrack(axis);
1428
1429 std::vector<uint8_t> layouts(numtracks * plat_len);
1430 GetStationLayout(layouts.data(), numtracks, plat_len, statspec);
1431
1432 uint8_t numtracks_orig = numtracks;
1433
1434 Company *c = Company::Get(st->owner);
1435 size_t layout_idx = 0;
1436 TileIndex tile_track = tile_org;
1437 do {
1438 TileIndex tile = tile_track;
1439 int w = plat_len;
1440 do {
1441 uint8_t layout = layouts[layout_idx++];
1442 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1443 /* Check for trains having a reservation for this tile. */
1445 if (v != nullptr) {
1446 affected_vehicles.push_back(v);
1448 }
1449 }
1450
1451 /* Railtype can change when overbuilding. */
1452 if (IsRailStationTile(tile)) {
1453 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1455 }
1456
1457 /* Remove animation if overbuilding */
1458 DeleteAnimatedTile(tile);
1459 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1460 MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, rt);
1461 /* Free the spec if we overbuild something */
1462 DeallocateSpecFromStation(st, old_specindex);
1463
1464 SetCustomStationSpecIndex(tile, specindex);
1465 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1466 SetAnimationFrame(tile, 0);
1467
1468 if (statspec != nullptr) {
1469 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1470 uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
1471
1472 /* As the station is not yet completely finished, the station does not yet exist. */
1473 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1474 if (callback != CALLBACK_FAILED) {
1475 if (callback <= UINT8_MAX) {
1476 SetStationGfx(tile, (callback & ~1) + axis);
1477 } else {
1479 }
1480 }
1481
1482 /* Trigger station animation -- after building? */
1483 TriggerStationAnimation(st, tile, SAT_BUILT);
1484 }
1485
1486 SetRailStationTileFlags(tile, statspec);
1487
1488 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1490
1491 tile += tile_delta;
1492 } while (--w);
1493 AddTrackToSignalBuffer(tile_track, track, _current_company);
1494 YapfNotifyTrackLayoutChange(tile_track, track);
1495 tile_track += track_delta;
1496 } while (--numtracks);
1497
1498 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1499 /* Restore reservations of trains. */
1500 RestoreTrainReservation(affected_vehicles[i]);
1501 }
1502
1503 /* Check whether we need to expand the reservation of trains already on the station. */
1504 TileArea update_reservation_area;
1505 if (axis == AXIS_X) {
1506 update_reservation_area = TileArea(tile_org, 1, numtracks_orig);
1507 } else {
1508 update_reservation_area = TileArea(tile_org, numtracks_orig, 1);
1509 }
1510
1511 for (TileIndex tile : update_reservation_area) {
1512 /* Don't even try to make eye candy parts reserved. */
1513 if (IsStationTileBlocked(tile)) continue;
1514
1515 DiagDirection dir = AxisToDiagDir(axis);
1516 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1517 TileIndex platform_begin = tile;
1518 TileIndex platform_end = tile;
1519
1520 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1521 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1522 platform_begin = next_tile;
1523 }
1524 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1525 platform_end = next_tile;
1526 }
1527
1528 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1529 bool reservation = false;
1530 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1531 reservation = HasStationReservation(t);
1532 }
1533
1534 if (reservation) {
1535 SetRailStationPlatformReservation(platform_begin, dir, true);
1536 }
1537 }
1538
1539 st->MarkTilesDirty(false);
1540 st->AfterStationTileSetChange(true, StationType::Rail);
1541 }
1542
1543 return cost;
1544}
1545
1546static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1547{
1548restart:
1549
1550 /* too small? */
1551 if (ta.w != 0 && ta.h != 0) {
1552 /* check the left side, x = constant, y changes */
1553 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1554 /* the left side is unused? */
1555 if (++i == ta.h) {
1556 ta.tile += TileDiffXY(1, 0);
1557 ta.w--;
1558 goto restart;
1559 }
1560 }
1561
1562 /* check the right side, x = constant, y changes */
1563 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1564 /* the right side is unused? */
1565 if (++i == ta.h) {
1566 ta.w--;
1567 goto restart;
1568 }
1569 }
1570
1571 /* check the upper side, y = constant, x changes */
1572 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1573 /* the left side is unused? */
1574 if (++i == ta.w) {
1575 ta.tile += TileDiffXY(0, 1);
1576 ta.h--;
1577 goto restart;
1578 }
1579 }
1580
1581 /* check the lower side, y = constant, x changes */
1582 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1583 /* the left side is unused? */
1584 if (++i == ta.w) {
1585 ta.h--;
1586 goto restart;
1587 }
1588 }
1589 } else {
1590 ta.Clear();
1591 }
1592
1593 return ta;
1594}
1595
1596static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1597{
1598 return st->TileBelongsToRailStation(tile);
1599}
1600
1601static void MakeRailStationAreaSmaller(BaseStation *st)
1602{
1603 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1604}
1605
1606static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1607{
1608 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1609}
1610
1611static void MakeShipStationAreaSmaller(Station *st)
1612{
1613 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1614 UpdateStationDockingTiles(st);
1615}
1616
1617static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1618{
1619 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1620}
1621
1622void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1623{
1624 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1625}
1626
1637template <class T>
1638CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlags flags, Money removal_cost, bool keep_rail)
1639{
1640 /* Count of the number of tiles removed */
1641 int quantity = 0;
1643 /* Accumulator for the errors seen during clearing. If no errors happen,
1644 * and the quantity is 0 there is no station. Otherwise it will be one
1645 * of the other error that got accumulated. */
1646 CommandCost error;
1647
1648 /* Do the action for every tile into the area */
1649 for (TileIndex tile : ta) {
1650 /* Make sure the specified tile is a rail station */
1651 if (!HasStationTileRail(tile)) continue;
1652
1653 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1655 error.AddCost(ret);
1656 if (ret.Failed()) continue;
1657
1658 /* Check ownership of station */
1659 T *st = T::GetByTile(tile);
1660 if (st == nullptr) continue;
1661
1663 ret = CheckOwnership(st->owner);
1664 error.AddCost(ret);
1665 if (ret.Failed()) continue;
1666 }
1667
1668 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1669 quantity++;
1670
1671 if (keep_rail || IsStationTileBlocked(tile)) {
1672 /* Don't refund the 'steel' of the track when we keep the
1673 * rail, or when the tile didn't have any rail at all. */
1674 total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
1675 }
1676
1677 if (flags.Test(DoCommandFlag::Execute)) {
1678 /* read variables before the station tile is removed */
1679 uint specindex = GetCustomStationSpecIndex(tile);
1680 Track track = GetRailStationTrack(tile);
1681 Owner owner = GetTileOwner(tile);
1682 RailType rt = GetRailType(tile);
1683 Train *v = nullptr;
1684
1685 if (HasStationReservation(tile)) {
1686 v = GetTrainForReservation(tile, track);
1687 if (v != nullptr) FreeTrainReservation(v);
1688 }
1689
1690 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1691 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1692
1693 DoClearSquare(tile);
1694 DeleteNewGRFInspectWindow(GSF_STATIONS, tile.base());
1695 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
1696 Company::Get(owner)->infrastructure.station--;
1698
1699 st->rect.AfterRemoveTile(st, tile);
1700 AddTrackToSignalBuffer(tile, track, owner);
1701 YapfNotifyTrackLayoutChange(tile, track);
1702
1703 DeallocateSpecFromStation(st, specindex);
1704
1705 include(affected_stations, st);
1706
1707 if (v != nullptr) RestoreTrainReservation(v);
1708 }
1709 }
1710
1711 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1712
1713 for (T *st : affected_stations) {
1714
1715 /* now we need to make the "spanned" area of the railway station smaller
1716 * if we deleted something at the edges.
1717 * we also need to adjust train_tile. */
1718 MakeRailStationAreaSmaller(st);
1719 UpdateStationSignCoord(st);
1720
1721 /* if we deleted the whole station, delete the train facility. */
1722 if (st->train_station.tile == INVALID_TILE) {
1726 MarkCatchmentTilesDirty();
1727 st->UpdateVirtCoord();
1729 }
1730 }
1731
1732 total_cost.AddCost(quantity * removal_cost);
1733 return total_cost;
1734}
1735
1746{
1747 if (end == 0) end = start;
1748 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1749
1750 TileArea ta(start, end);
1751 std::vector<Station *> affected_stations;
1752
1753 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], keep_rail);
1754 if (ret.Failed()) return ret;
1755
1756 /* Do all station specific functions here. */
1757 for (Station *st : affected_stations) {
1758
1760 st->MarkTilesDirty(false);
1761 MarkCatchmentTilesDirty();
1762 st->RecomputeCatchment();
1763 }
1764
1765 /* Now apply the rail cost to the number that we deleted */
1766 return ret;
1767}
1768
1779{
1780 if (end == 0) end = start;
1781 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1782
1783 TileArea ta(start, end);
1784 std::vector<Waypoint *> affected_stations;
1785
1786 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], keep_rail);
1787}
1788
1789
1798template <class T>
1800{
1801 /* Current company owns the station? */
1803 CommandCost ret = CheckOwnership(st->owner);
1804 if (ret.Failed()) return ret;
1805 }
1806
1807 /* determine width and height of platforms */
1808 TileArea ta = st->train_station;
1809
1810 assert(ta.w != 0 && ta.h != 0);
1811
1813 /* clear all areas of the station */
1814 for (TileIndex tile : ta) {
1815 /* only remove tiles that are actually train station tiles */
1816 if (st->TileBelongsToRailStation(tile)) {
1817 std::vector<T*> affected_stations; // dummy
1818 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1819 if (ret.Failed()) return ret;
1820 cost.AddCost(ret);
1821 }
1822 }
1823
1824 return cost;
1825}
1826
1834{
1835 /* if there is flooding, remove platforms tile by tile */
1838 }
1839
1840 Station *st = Station::GetByTile(tile);
1841 CommandCost cost = RemoveRailStation(st, flags, _price[PR_CLEAR_STATION_RAIL]);
1842
1844
1845 return cost;
1846}
1847
1855{
1856 /* if there is flooding, remove waypoints tile by tile */
1859 }
1860
1861 return RemoveRailStation(Waypoint::GetByTile(tile), flags, _price[PR_CLEAR_WAYPOINT_RAIL]);
1862}
1863
1864
1870static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
1871{
1872 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1873
1874 if (*primary_stop == nullptr) {
1875 /* we have no roadstop of the type yet, so write a "primary stop" */
1876 return primary_stop;
1877 } else {
1878 /* there are stops already, so append to the end of the list */
1879 RoadStop *stop = *primary_stop;
1880 while (stop->next != nullptr) stop = stop->next;
1881 return &stop->next;
1882 }
1883}
1884
1885static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1886CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index = -1);
1887
1897static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1898{
1899 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1900}
1901
1915CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
1916{
1917 uint invalid_dirs = 0;
1918 if (is_drive_through) {
1919 SetBit(invalid_dirs, AxisToDiagDir(axis));
1920 SetBit(invalid_dirs, ReverseDiagDir(AxisToDiagDir(axis)));
1921 } else {
1922 SetBit(invalid_dirs, ddir);
1923 }
1924
1925 /* Check every tile in the area. */
1926 int allowed_z = -1;
1928 for (TileIndex cur_tile : tile_area) {
1929 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
1930 if (ret.Failed()) return ret;
1931
1932 bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
1933
1934 /* Only add costs if a stop doesn't already exist in the location */
1935 if (!is_preexisting_roadstop) {
1936 cost.AddCost(ret);
1937 cost.AddCost(unit_cost);
1938 }
1939 }
1940
1941 return cost;
1942}
1943
1960CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
1961 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
1962{
1963 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= RoadStopType::End) return CMD_ERROR;
1964 bool reuse = (station_to_join != NEW_STATION);
1965 if (!reuse) station_to_join = StationID::Invalid();
1966 bool distant_join = (station_to_join != StationID::Invalid());
1967
1968 /* Check if the given station class is valid */
1969 if (static_cast<uint>(spec_class) >= RoadStopClass::GetClassCount()) return CMD_ERROR;
1970 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
1971 if (IsWaypointClass(*cls)) return CMD_ERROR;
1972 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1973
1974 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
1975 if (roadstopspec != nullptr) {
1976 if (stop_type == RoadStopType::Truck && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
1977 if (stop_type == RoadStopType::Bus && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
1978 if (!is_drive_through && roadstopspec->flags.Test(RoadStopSpecFlag::DriveThroughOnly)) return CMD_ERROR;
1979 }
1980
1981 /* Check if the requested road stop is too big */
1982 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1983 /* Check for incorrect width / length. */
1984 if (width == 0 || length == 0) return CMD_ERROR;
1985 /* Check if the first tile and the last tile are valid */
1986 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
1987
1988 TileArea roadstop_area(tile, width, length);
1989
1990 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1991
1992 /* Trams only have drive through stops */
1993 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
1994
1995 Axis axis = DiagDirToAxis(ddir);
1996
1998 if (ret.Failed()) return ret;
1999
2000 bool is_truck_stop = stop_type != RoadStopType::Bus;
2001
2002 /* Total road stop cost. */
2003 Money unit_cost;
2004 if (roadstopspec != nullptr) {
2005 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS);
2006 } else {
2007 unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
2008 }
2009 StationID est = StationID::Invalid();
2010 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, axis, ddir, &est, rt, unit_cost);
2011 if (cost.Failed()) return cost;
2012
2013 Station *st = nullptr;
2014 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2015 if (ret.Failed()) return ret;
2016
2017 /* Check if this number of road stops can be allocated. */
2018 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);
2019
2020 ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD);
2021 if (ret.Failed()) return ret;
2022
2023 /* Check if we can allocate a custom stationspec to this station */
2024 int specindex = AllocateSpecToRoadStop(roadstopspec, st, flags.Test(DoCommandFlag::Execute));
2025 if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2026
2027 if (roadstopspec != nullptr) {
2028 /* Perform NewGRF checks */
2029
2030 /* Check if the road stop is buildable */
2031 if (roadstopspec->callback_mask.Test(RoadStopCallbackMask::Avail)) {
2032 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? StationType::Truck : StationType::Bus, 0);
2033 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2034 }
2035 }
2036
2037 if (flags.Test(DoCommandFlag::Execute)) {
2038 /* Check every tile in the area. */
2039 for (TileIndex cur_tile : roadstop_area) {
2040 /* Get existing road types and owners before any tile clearing */
2041 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
2042 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
2043 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
2044 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
2045
2046 if (IsTileType(cur_tile, MP_STATION) && IsStationRoadStop(cur_tile)) {
2047 RemoveRoadStop(cur_tile, flags, specindex);
2048 }
2049
2050 if (roadstopspec != nullptr) {
2051 /* Include this road stop spec's animation trigger bitmask
2052 * in the station's cached copy. */
2053 st->cached_roadstop_anim_triggers |= roadstopspec->animation.triggers;
2054 }
2055
2056 RoadStop *road_stop = new RoadStop(cur_tile);
2057 /* Insert into linked list of RoadStops. */
2058 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2059 *currstop = road_stop;
2060
2061 if (is_truck_stop) {
2062 st->truck_station.Add(cur_tile);
2063 } else {
2064 st->bus_station.Add(cur_tile);
2065 }
2066
2067 /* Initialize an empty station. */
2068 st->AddFacility(is_truck_stop ? StationFacility::TruckStop : StationFacility::BusStop, cur_tile);
2069
2070 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
2071
2072 RoadStopType rs_type = is_truck_stop ? RoadStopType::Truck : RoadStopType::Bus;
2073 if (is_drive_through) {
2074 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2075 * bits first. */
2076 if (IsNormalRoadTile(cur_tile)) {
2077 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
2078 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
2079 }
2080
2081 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2082 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2083
2084 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == RoadStopType::Bus ? StationType::Bus : StationType::Truck), road_rt, tram_rt, axis);
2085 road_stop->MakeDriveThrough();
2086 } else {
2087 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2088 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2089 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2090 }
2093 Company::Get(st->owner)->infrastructure.station++;
2094
2095 SetCustomRoadStopSpecIndex(cur_tile, specindex);
2096 if (roadstopspec != nullptr) {
2097 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2098 TriggerRoadStopAnimation(st, cur_tile, SAT_BUILT);
2099 }
2100
2101 MarkTileDirtyByTile(cur_tile);
2102 }
2103
2104 if (st != nullptr) {
2105 st->AfterStationTileSetChange(true, is_truck_stop ? StationType::Truck: StationType::Bus);
2106 }
2107 }
2108 return cost;
2109}
2110
2111
2112static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
2113{
2114 if (v->type == VEH_ROAD) {
2115 /* Okay... we are a road vehicle on a drive through road stop.
2116 * But that road stop has just been removed, so we need to make
2117 * sure we are in a valid state... however, vehicles can also
2118 * turn on road stop tiles, so only clear the 'road stop' state
2119 * bits and only when the state was 'in road stop', otherwise
2120 * we'll end up clearing the turn around bits. */
2123 }
2124
2125 return nullptr;
2126}
2127
2128
2136static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2137{
2138 Station *st = Station::GetByTile(tile);
2139
2141 CommandCost ret = CheckOwnership(st->owner);
2142 if (ret.Failed()) return ret;
2143 }
2144
2145 bool is_truck = IsTruckStop(tile);
2146
2147 RoadStop **primary_stop;
2148 RoadStop *cur_stop;
2149 if (is_truck) { // truck stop
2150 primary_stop = &st->truck_stops;
2151 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Truck);
2152 } else {
2153 primary_stop = &st->bus_stops;
2154 cur_stop = RoadStop::GetByTile(tile, RoadStopType::Bus);
2155 }
2156
2157 assert(cur_stop != nullptr);
2158
2159 /* don't do the check for drive-through road stops when company bankrupts */
2161 /* remove the 'going through road stop' status from all vehicles on that tile */
2162 if (flags.Test(DoCommandFlag::Execute)) FindVehicleOnPos(tile, nullptr, &ClearRoadStopStatusEnum);
2163 } else {
2165 if (ret.Failed()) return ret;
2166 }
2167
2168 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2169
2170 if (flags.Test(DoCommandFlag::Execute)) {
2171 if (*primary_stop == cur_stop) {
2172 /* removed the first stop in the list */
2173 *primary_stop = cur_stop->next;
2174 /* removed the only stop? */
2175 if (*primary_stop == nullptr) {
2178 }
2179 } else {
2180 /* tell the predecessor in the list to skip this stop */
2181 RoadStop *pred = *primary_stop;
2182 while (pred->next != cur_stop) pred = pred->next;
2183 pred->next = cur_stop->next;
2184 }
2185
2186 /* Update company infrastructure counts. */
2187 for (RoadTramType rtt : _roadtramtypes) {
2188 RoadType rt = GetRoadType(tile, rtt);
2190 }
2191
2192 Company::Get(st->owner)->infrastructure.station--;
2194
2195 uint specindex = GetCustomRoadStopSpecIndex(tile);
2196
2197 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2198
2199 if (IsDriveThroughStopTile(tile)) {
2200 /* Clears the tile for us */
2201 cur_stop->ClearDriveThrough();
2202 DeleteAnimatedTile(tile);
2203 } else {
2204 DoClearSquare(tile);
2205 }
2206
2207 delete cur_stop;
2208
2209 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2210 * this station, then look for any currently heading to the tile. */
2211 StationID station_id = st->index;
2213 [](const Vehicle *v) { return v->type == VEH_ROAD; },
2214 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2215 [station_id, tile](Vehicle *v) {
2216 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2217 v->SetDestTile(v->GetOrderStationLocation(station_id));
2218 }
2219 }
2220 );
2221
2222 st->rect.AfterRemoveTile(st, tile);
2223
2224 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
2225
2226 st->RemoveRoadStopTileData(tile);
2227 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2228
2229 /* Update the tile area of the truck/bus stop */
2230 if (is_truck) {
2231 st->truck_station.Clear();
2232 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2233 } else {
2234 st->bus_station.Clear();
2235 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2236 }
2237 }
2238
2239 Price category = is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS;
2240 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2241}
2242
2250CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index)
2251{
2252 Waypoint *wp = Waypoint::GetByTile(tile);
2253
2255 CommandCost ret = CheckOwnership(wp->owner);
2256 if (ret.Failed()) return ret;
2257 }
2258
2259 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2260 if (!flags.Test(DoCommandFlag::Bankrupt)) {
2262 if (ret.Failed()) return ret;
2263 }
2264
2265 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2266
2267 if (flags.Test(DoCommandFlag::Execute)) {
2268 /* Update company infrastructure counts. */
2269 for (RoadTramType rtt : _roadtramtypes) {
2270 RoadType rt = GetRoadType(tile, rtt);
2272 }
2273
2274 Company::Get(wp->owner)->infrastructure.station--;
2276
2277 uint specindex = GetCustomRoadStopSpecIndex(tile);
2278
2279 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2280
2281 DoClearSquare(tile);
2282
2283 wp->rect.AfterRemoveTile(wp, tile);
2284
2285 wp->RemoveRoadStopTileData(tile);
2286 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2287
2288 if (replacement_spec_index < 0) {
2289 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2290
2291 UpdateStationSignCoord(wp);
2292
2293 /* if we deleted the whole waypoint, delete the road facility. */
2297 wp->UpdateVirtCoord();
2299 }
2300 }
2301 }
2302
2303 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(PR_CLEAR_STATION_TRUCK) : _price[PR_CLEAR_STATION_TRUCK]);
2304}
2305
2314static CommandCost RemoveGenericRoadStop(DoCommandFlags flags, const TileArea &roadstop_area, bool road_waypoint, bool remove_road)
2315{
2317 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2318 bool had_success = false;
2319
2320 for (TileIndex cur_tile : roadstop_area) {
2321 /* Make sure the specified tile is a road stop of the correct type */
2322 if (!IsTileType(cur_tile, MP_STATION) || !IsAnyRoadStop(cur_tile) || IsRoadWaypoint(cur_tile) != road_waypoint) continue;
2323
2324 /* Save information on to-be-restored roads before the stop is removed. */
2325 RoadBits road_bits = ROAD_NONE;
2326 RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
2327 Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
2328 if (IsDriveThroughStopTile(cur_tile)) {
2329 for (RoadTramType rtt : _roadtramtypes) {
2330 road_type[rtt] = GetRoadType(cur_tile, rtt);
2331 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2332 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2333 /* If we don't want to preserve our roads then restore only roads of others. */
2334 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2335 }
2336 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2337 }
2338
2339 CommandCost ret;
2340 if (road_waypoint) {
2341 ret = RemoveRoadWaypointStop(cur_tile, flags);
2342 } else {
2343 ret = RemoveRoadStop(cur_tile, flags);
2344 }
2345 if (ret.Failed()) {
2346 last_error = ret;
2347 continue;
2348 }
2349 cost.AddCost(ret);
2350 had_success = true;
2351
2352 /* Restore roads. */
2353 if (flags.Test(DoCommandFlag::Execute) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
2354 MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2355 road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
2356
2357 /* Update company infrastructure counts. */
2358 int count = CountBits(road_bits);
2359 UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
2360 UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
2361 }
2362 }
2363
2364 return had_success ? cost : last_error;
2365}
2366
2377CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2378{
2379 if (stop_type >= RoadStopType::End) return CMD_ERROR;
2380 /* Check for incorrect width / height. */
2381 if (width == 0 || height == 0) return CMD_ERROR;
2382 /* Check if the first tile and the last tile are valid */
2383 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2384 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2385 if (remove_road && flags.Test(DoCommandFlag::Bankrupt)) return CMD_ERROR;
2386
2387 TileArea roadstop_area(tile, width, height);
2388
2389 return RemoveGenericRoadStop(flags, roadstop_area, false, remove_road);
2390}
2391
2400{
2401 if (end == 0) end = start;
2402 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2403
2404 TileArea roadstop_area(start, end);
2405
2406 return RemoveGenericRoadStop(flags, roadstop_area, true, false);
2407}
2408
2417uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2418{
2419 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2420 * So no need to go any further*/
2421 if (as->noise_level < 2) return as->noise_level;
2422
2423 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2424 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2425 * Basically, it says that the less tolerant a town is, the bigger the distance before
2426 * an actual decrease can be granted */
2427 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2428
2429 /* now, we want to have the distance segmented using the distance judged bareable by town
2430 * This will give us the coefficient of reduction the distance provides. */
2431 uint noise_reduction = distance / town_tolerance_distance;
2432
2433 /* If the noise reduction equals the airport noise itself, don't give it for free.
2434 * Otherwise, simply reduce the airport's level. */
2435 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2436}
2437
2448Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2449{
2450 assert(Town::GetNumItems() > 0);
2451
2452 Town *nearest = nullptr;
2453
2454 auto width = as->size_x;
2455 auto height = as->size_y;
2456 if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
2457
2458 uint perimeter_min_x = TileX(tile);
2459 uint perimeter_min_y = TileY(tile);
2460 uint perimeter_max_x = perimeter_min_x + width - 1;
2461 uint perimeter_max_y = perimeter_min_y + height - 1;
2462
2463 mindist = UINT_MAX - 1; // prevent overflow
2464
2465 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2466 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2467 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2468 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) {
2469 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2470 if (t == nullptr) continue;
2471
2472 uint dist = DistanceManhattan(t->xy, cur_tile);
2473 if (dist == mindist && t->index < nearest->index) nearest = t;
2474 if (dist < mindist) {
2475 nearest = t;
2476 mindist = dist;
2477 }
2478 }
2479 }
2480
2481 return nearest;
2482}
2483
2491static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2492{
2494}
2495
2496
2499{
2500 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2501
2502 for (const Station *st : Station::Iterate()) {
2503 if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
2504 uint dist;
2505 Town *nearest = AirportGetNearestTown(st, dist);
2506 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2507 }
2508 }
2509}
2510
2521CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2522{
2523 bool reuse = (station_to_join != NEW_STATION);
2524 if (!reuse) station_to_join = StationID::Invalid();
2525 bool distant_join = (station_to_join != StationID::Invalid());
2526
2527 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2528
2529 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2530
2532 if (ret.Failed()) return ret;
2533
2534 /* Check if a valid, buildable airport was chosen for construction */
2535 const AirportSpec *as = AirportSpec::Get(airport_type);
2536 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2537 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2538
2539 Direction rotation = as->layouts[layout].rotation;
2540 int w = as->size_x;
2541 int h = as->size_y;
2542 if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
2543 TileArea airport_area = TileArea(tile, w, h);
2544
2546 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2547 }
2548
2549 AirportTileTableIterator tile_iter(as->layouts[layout].tiles.data(), tile);
2550 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2551 if (cost.Failed()) return cost;
2552
2553 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2554 uint dist;
2555 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2556 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2557
2558 /* Check if local auth would allow a new airport */
2559 StringID authority_refuse_message = STR_NULL;
2560 Town *authority_refuse_town = nullptr;
2561
2563 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2564 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2565 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2566 authority_refuse_town = nearest;
2567 }
2568 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2569 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2570 uint num = 0;
2571 for (const Station *st : Station::Iterate()) {
2572 if (st->town == t && st->facilities.Test(StationFacility::Airport) && st->airport.type != AT_OILRIG) num++;
2573 }
2574 if (num >= 2) {
2575 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2576 authority_refuse_town = t;
2577 }
2578 }
2579
2580 if (authority_refuse_message != STR_NULL) {
2581 return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index);
2582 }
2583
2584 Station *st = nullptr;
2585 ret = FindJoiningStation(StationID::Invalid(), station_to_join, allow_adjacent, airport_area, &st);
2586 if (ret.Failed()) return ret;
2587
2588 /* Distant join */
2589 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2590
2591 ret = BuildStationPart(&st, flags, reuse, airport_area, GetAirport(airport_type)->flags.Test(AirportFTAClass::Flag::Airplanes) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT);
2592 if (ret.Failed()) return ret;
2593
2594 if (st != nullptr && st->airport.tile != INVALID_TILE) {
2595 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2596 }
2597
2598 for (AirportTileTableIterator iter(as->layouts[layout].tiles.data(), tile); iter != INVALID_TILE; ++iter) {
2599 cost.AddCost(_price[PR_BUILD_STATION_AIRPORT]);
2600 }
2601
2602 if (flags.Test(DoCommandFlag::Execute)) {
2603 /* Always add the noise, so there will be no need to recalculate when option toggles */
2604 nearest->noise_reached += newnoise_level;
2605
2607 st->airport.type = airport_type;
2608 st->airport.layout = layout;
2609 st->airport.blocks = {};
2610 st->airport.rotation = rotation;
2611
2612 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
2613
2614 for (AirportTileTableIterator iter(as->layouts[layout].tiles.data(), tile); iter != INVALID_TILE; ++iter) {
2615 Tile t(iter);
2616 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID);
2617 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2618 st->airport.Add(iter);
2619
2621 }
2622
2623 /* Only call the animation trigger after all tiles have been built */
2624 for (AirportTileTableIterator iter(as->layouts[layout].tiles.data(), tile); iter != INVALID_TILE; ++iter) {
2625 AirportTileAnimationTrigger(st, iter, AAT_BUILT);
2626 }
2627
2629
2630 Company::Get(st->owner)->infrastructure.airport++;
2631
2632 st->AfterStationTileSetChange(true, StationType::Airport);
2634
2637 }
2638 }
2639
2640 return cost;
2641}
2642
2650{
2651 Station *st = Station::GetByTile(tile);
2652
2654 CommandCost ret = CheckOwnership(st->owner);
2655 if (ret.Failed()) return ret;
2656 }
2657
2658 tile = st->airport.tile;
2659
2661
2662 for (const Aircraft *a : Aircraft::Iterate()) {
2663 if (!a->IsNormalAircraft()) continue;
2664 if (a->targetairport == st->index && a->state != FLYING) {
2665 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2666 }
2667 }
2668
2669 if (flags.Test(DoCommandFlag::Execute)) {
2670 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2671 TileIndex tile_cur = st->airport.GetHangarTile(i);
2672 OrderBackup::Reset(tile_cur, false);
2674 }
2675
2676 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2677 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2678 * need of recalculation */
2679 uint dist;
2680 Town *nearest = AirportGetNearestTown(st, dist);
2682
2685 }
2686 }
2687
2688 for (TileIndex tile_cur : st->airport) {
2689 if (!st->TileBelongsToAirport(tile_cur)) continue;
2690
2691 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2692 if (ret.Failed()) return ret;
2693
2694 cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
2695
2696 if (flags.Test(DoCommandFlag::Execute)) {
2697 DoClearSquare(tile_cur);
2698 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur.base());
2699 }
2700 }
2701
2702 if (flags.Test(DoCommandFlag::Execute)) {
2703 /* Clear the persistent storage. */
2704 delete st->airport.psa;
2705
2706 st->rect.AfterRemoveRect(st, st->airport);
2707
2708 st->airport.Clear();
2711
2713
2714 Company::Get(st->owner)->infrastructure.airport--;
2715
2716 st->AfterStationTileSetChange(false, StationType::Airport);
2717
2718 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
2719 }
2720
2721 return cost;
2722}
2723
2731{
2732 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2733 Station *st = Station::Get(station_id);
2734
2736
2737 CommandCost ret = CheckOwnership(st->owner);
2738 if (ret.Failed()) return ret;
2739
2740 if (flags.Test(DoCommandFlag::Execute)) {
2743 }
2744 return CommandCost();
2745}
2746
2753bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2754{
2755 for (const OrderList *orderlist : OrderList::Iterate()) {
2756 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2757 assert(v != nullptr);
2758 if ((v->owner == company) != include_company) continue;
2759
2760 for (const Order *order = orderlist->GetFirstOrder(); order != nullptr; order = order->next) {
2761 if (order->GetDestination() == station && (order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT))) {
2762 return true;
2763 }
2764 }
2765 }
2766 return false;
2767}
2768
2769static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
2770 {-1, 0},
2771 { 0, 0},
2772 { 0, 0},
2773 { 0, -1}
2774};
2775static const uint8_t _dock_w_chk[4] = { 2, 1, 2, 1 };
2776static const uint8_t _dock_h_chk[4] = { 1, 2, 1, 2 };
2777
2786CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
2787{
2788 bool reuse = (station_to_join != NEW_STATION);
2789 if (!reuse) station_to_join = StationID::Invalid();
2790 bool distant_join = (station_to_join != StationID::Invalid());
2791
2792 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2793
2795 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2796 direction = ReverseDiagDir(direction);
2797
2798 /* Docks cannot be placed on rapids */
2799 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2800
2802 if (ret.Failed()) return ret;
2803
2804 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
2805
2806 CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
2807 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
2808 if (ret.Failed()) return ret;
2809 cost.AddCost(ret);
2810
2811 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2812
2813 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2814 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2815 }
2816
2817 if (IsBridgeAbove(tile_cur)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
2818
2819 /* Get the water class of the water tile before it is cleared.*/
2820 WaterClass wc = GetWaterClass(tile_cur);
2821
2822 bool add_cost = !IsWaterTile(tile_cur);
2823 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
2824 if (ret.Failed()) return ret;
2825 if (add_cost) cost.AddCost(ret);
2826
2827 tile_cur += TileOffsByDiagDir(direction);
2828 if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) {
2829 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2830 }
2831
2832 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2833 _dock_w_chk[direction], _dock_h_chk[direction]);
2834
2835 /* middle */
2836 Station *st = nullptr;
2837 ret = FindJoiningStation(StationID::Invalid(), station_to_join, adjacent, dock_area, &st);
2838 if (ret.Failed()) return ret;
2839
2840 /* Distant join */
2841 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2842
2843 ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
2844 if (ret.Failed()) return ret;
2845
2846 if (flags.Test(DoCommandFlag::Execute)) {
2847 st->ship_station.Add(tile);
2848 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2849 st->ship_station.Add(flat_tile);
2851
2852 st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
2853
2854 /* If the water part of the dock is on a canal, update infrastructure counts.
2855 * This is needed as we've cleared that tile before.
2856 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2857 * See: MakeWaterKeepingClass() */
2858 if (wc == WATER_CLASS_CANAL && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WATER_CLASS_CANAL && IsTileOwner(flat_tile, _current_company))) {
2859 Company::Get(st->owner)->infrastructure.water++;
2860 }
2861 Company::Get(st->owner)->infrastructure.station += 2;
2862
2863 MakeDock(tile, st->owner, st->index, direction, wc);
2864 UpdateStationDockingTiles(st);
2865
2866 st->AfterStationTileSetChange(true, StationType::Dock);
2867 }
2868
2869 return cost;
2870}
2871
2872void RemoveDockingTile(TileIndex t)
2873{
2874 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2875 TileIndex tile = t + TileOffsByDiagDir(d);
2876 if (!IsValidTile(tile)) continue;
2877
2878 if (IsTileType(tile, MP_STATION)) {
2879 Station *st = Station::GetByTile(tile);
2880 if (st != nullptr) UpdateStationDockingTiles(st);
2881 } else if (IsTileType(tile, MP_INDUSTRY)) {
2883 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
2884 }
2885 }
2886}
2887
2894{
2895 assert(IsValidTile(tile));
2896
2897 /* Clear and maybe re-set docking tile */
2898 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2899 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
2900 if (!IsValidTile(docking_tile)) continue;
2901
2902 if (IsPossibleDockingTile(docking_tile)) {
2903 SetDockingTile(docking_tile, false);
2904 CheckForDockingTile(docking_tile);
2905 }
2906 }
2907}
2908
2915{
2916 assert(IsDockTile(t));
2917
2918 StationGfx gfx = GetStationGfx(t);
2919 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
2920
2921 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2922 TileIndex tile = t + TileOffsByDiagDir(d);
2923 if (!IsValidTile(tile)) continue;
2924 if (!IsDockTile(tile)) continue;
2925 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
2926 }
2927
2928 return INVALID_TILE;
2929}
2930
2938{
2939 Station *st = Station::GetByTile(tile);
2940 CommandCost ret = CheckOwnership(st->owner);
2941 if (ret.Failed()) return ret;
2942
2943 if (!IsDockTile(tile)) return CMD_ERROR;
2944
2945 TileIndex tile1 = FindDockLandPart(tile);
2946 if (tile1 == INVALID_TILE) return CMD_ERROR;
2947 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
2948
2949 ret = EnsureNoVehicleOnGround(tile1);
2950 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
2951 if (ret.Failed()) return ret;
2952
2953 if (flags.Test(DoCommandFlag::Execute)) {
2954 DoClearSquare(tile1);
2955 MarkTileDirtyByTile(tile1);
2956 MakeWaterKeepingClass(tile2, st->owner);
2957
2958 st->rect.AfterRemoveTile(st, tile1);
2959 st->rect.AfterRemoveTile(st, tile2);
2960
2961 MakeShipStationAreaSmaller(st);
2962 if (st->ship_station.tile == INVALID_TILE) {
2963 st->ship_station.Clear();
2964 st->docking_station.Clear();
2967 }
2968
2969 Company::Get(st->owner)->infrastructure.station -= 2;
2970
2971 st->AfterStationTileSetChange(false, StationType::Dock);
2972
2975
2976 for (Ship *s : Ship::Iterate()) {
2977 /* Find all ships going to our dock. */
2978 if (s->current_order.GetDestination() != st->index) {
2979 continue;
2980 }
2981
2982 /* Find ships that are marked as "loading" but are no longer on a
2983 * docking tile. Force them to leave the station (as they were loading
2984 * on the removed dock). */
2985 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
2986 s->LeaveStation();
2987 }
2988
2989 /* If we no longer have a dock, mark the order as invalid and send
2990 * the ship to the next order (or, if there is none, make it
2991 * wander the world). */
2992 if (s->current_order.IsType(OT_GOTO_STATION) && !st->facilities.Test(StationFacility::Dock)) {
2993 s->SetDestTile(s->GetOrderStationLocation(st->index));
2994 }
2995 }
2996 }
2997
2998 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
2999}
3000
3001#include "table/station_land.h"
3002
3010{
3011 const auto &layouts = _station_display_datas[to_underlying(st)];
3012 if (gfx >= layouts.size()) gfx &= 1;
3013 return layouts.data() + gfx;
3014}
3015
3025bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3026{
3027 bool snow_desert;
3028 switch (*ground) {
3029 case SPR_RAIL_TRACK_X:
3030 case SPR_MONO_TRACK_X:
3031 case SPR_MGLV_TRACK_X:
3032 snow_desert = false;
3033 *overlay_offset = RTO_X;
3034 break;
3035
3036 case SPR_RAIL_TRACK_Y:
3037 case SPR_MONO_TRACK_Y:
3038 case SPR_MGLV_TRACK_Y:
3039 snow_desert = false;
3040 *overlay_offset = RTO_Y;
3041 break;
3042
3043 case SPR_RAIL_TRACK_X_SNOW:
3044 case SPR_MONO_TRACK_X_SNOW:
3045 case SPR_MGLV_TRACK_X_SNOW:
3046 snow_desert = true;
3047 *overlay_offset = RTO_X;
3048 break;
3049
3050 case SPR_RAIL_TRACK_Y_SNOW:
3051 case SPR_MONO_TRACK_Y_SNOW:
3052 case SPR_MGLV_TRACK_Y_SNOW:
3053 snow_desert = true;
3054 *overlay_offset = RTO_Y;
3055 break;
3056
3057 default:
3058 return false;
3059 }
3060
3061 if (ti != nullptr) {
3062 /* Decide snow/desert from tile */
3064 case LandscapeType::Arctic:
3065 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3066 break;
3067
3068 case LandscapeType::Tropic:
3069 snow_desert = GetTropicZone(ti->tile) == TROPICZONE_DESERT;
3070 break;
3071
3072 default:
3073 break;
3074 }
3075 }
3076
3077 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3078 return true;
3079}
3080
3081static void DrawTile_Station(TileInfo *ti)
3082{
3083 const NewGRFSpriteLayout *layout = nullptr;
3084 DrawTileSpriteSpan tmp_rail_layout;
3085 const DrawTileSprites *t = nullptr;
3086 int32_t total_offset;
3087 const RailTypeInfo *rti = nullptr;
3088 uint32_t relocation = 0;
3089 uint32_t ground_relocation = 0;
3090 BaseStation *st = nullptr;
3091 const StationSpec *statspec = nullptr;
3092 uint tile_layout = 0;
3093
3094 if (HasStationRail(ti->tile)) {
3095 rti = GetRailTypeInfo(GetRailType(ti->tile));
3096 total_offset = rti->GetRailtypeSpriteOffset();
3097
3098 if (IsCustomStationSpecIndex(ti->tile)) {
3099 /* look for customization */
3100 st = BaseStation::GetByTile(ti->tile);
3101 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3102
3103 if (statspec != nullptr) {
3104 tile_layout = GetStationGfx(ti->tile);
3105
3107 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3108 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
3109 }
3110
3111 /* Ensure the chosen tile layout is valid for this custom station */
3112 if (!statspec->renderdata.empty()) {
3113 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3114 if (!layout->NeedsPreprocessing()) {
3115 t = layout;
3116 layout = nullptr;
3117 }
3118 }
3119 }
3120 }
3121 } else {
3122 total_offset = 0;
3123 }
3124
3125 StationGfx gfx = GetStationGfx(ti->tile);
3126 if (IsAirport(ti->tile)) {
3127 gfx = GetAirportGfx(ti->tile);
3128 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3129 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3130 if (ats->grf_prop.GetSpriteGroup() != nullptr && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3131 return;
3132 }
3133 /* No sprite group (or no valid one) found, meaning no graphics associated.
3134 * Use the substitute one instead */
3135 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3136 gfx = ats->grf_prop.subst_id;
3137 }
3138 switch (gfx) {
3139 case APT_RADAR_GRASS_FENCE_SW:
3140 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3141 break;
3142 case APT_GRASS_FENCE_NE_FLAG:
3143 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3144 break;
3145 case APT_RADAR_FENCE_SW:
3146 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3147 break;
3148 case APT_RADAR_FENCE_NE:
3149 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3150 break;
3151 case APT_GRASS_FENCE_NE_FLAG_2:
3152 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3153 break;
3154 }
3155 }
3156
3157 Owner owner = GetTileOwner(ti->tile);
3158
3159 PaletteID palette;
3160 if (Company::IsValidID(owner)) {
3161 palette = COMPANY_SPRITE_COLOUR(owner);
3162 } else {
3163 /* Some stations are not owner by a company, namely oil rigs */
3164 palette = PALETTE_TO_GREY;
3165 }
3166
3167 if (layout == nullptr && (t == nullptr || t->GetSequence().empty())) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3168
3169 /* don't show foundation for docks */
3170 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3171 if (statspec != nullptr && statspec->flags.Test(StationSpecFlag::CustomFoundations)) {
3172 /* Station has custom foundations.
3173 * Check whether the foundation continues beyond the tile's upper sides. */
3174 uint edge_info = 0;
3175 auto [slope, z] = GetFoundationPixelSlope(ti->tile);
3176 if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
3177 if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
3178 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, tile_layout, edge_info);
3179 if (image == 0) goto draw_default_foundation;
3180
3182 /* Station provides extended foundations. */
3183
3184 static const uint8_t foundation_parts[] = {
3185 0, 0, 0, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3186 0, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3187 0, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3188 7, 8, 9 // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3189 };
3190
3191 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
3192 } else {
3193 /* Draw simple foundations, built up from 8 possible foundation sprites. */
3194
3195 /* Each set bit represents one of the eight composite sprites to be drawn.
3196 * 'Invalid' entries will not drawn but are included for completeness. */
3197 static const uint8_t composite_foundation_parts[] = {
3198 /* Invalid (00000000), Invalid (11010001), Invalid (11100100), SLOPE_SW (11100000) */
3199 0x00, 0xD1, 0xE4, 0xE0,
3200 /* Invalid (11001010), SLOPE_EW (11001001), SLOPE_SE (11000100), SLOPE_WSE (11000000) */
3201 0xCA, 0xC9, 0xC4, 0xC0,
3202 /* Invalid (11010010), SLOPE_NW (10010001), SLOPE_NS (11100100), SLOPE_NWS (10100000) */
3203 0xD2, 0x91, 0xE4, 0xA0,
3204 /* SLOPE_NE (01001010), SLOPE_ENW (00001001), SLOPE_SEN (01000100) */
3205 0x4A, 0x09, 0x44
3206 };
3207
3208 uint8_t parts = composite_foundation_parts[ti->tileh];
3209
3210 /* If foundations continue beyond the tile's upper sides then
3211 * mask out the last two pieces. */
3212 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3213 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3214
3215 if (parts == 0) {
3216 /* We always have to draw at least one sprite to make sure there is a boundingbox and a sprite with the
3217 * correct offset for the childsprites.
3218 * So, draw the (completely empty) sprite of the default foundations. */
3219 goto draw_default_foundation;
3220 }
3221
3223 for (int i = 0; i < 8; i++) {
3224 if (HasBit(parts, i)) {
3225 AddSortableSpriteToDraw(image + i, PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
3226 }
3227 }
3229 }
3230
3231 OffsetGroundSprite(0, -8);
3233 } else {
3234draw_default_foundation:
3236 }
3237 }
3238
3239 bool draw_ground = false;
3240
3241 if (IsBuoy(ti->tile)) {
3242 DrawWaterClassGround(ti);
3243 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
3244 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3245 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3246 if (ti->tileh == SLOPE_FLAT) {
3247 DrawWaterClassGround(ti);
3248 } else {
3249 assert(IsDock(ti->tile));
3250 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3251 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WATER_CLASS_INVALID;
3252 if (wc == WATER_CLASS_SEA) {
3253 DrawShoreTile(ti->tileh);
3254 } else {
3255 DrawClearLandTile(ti, 3);
3256 }
3257 }
3258 } else if (IsRoadWaypointTile(ti->tile)) {
3260 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3261 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3262 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3263 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3264 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3265 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3266
3267 if (ti->tileh != SLOPE_FLAT) {
3269 }
3270
3271 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3272 } else {
3273 if (layout != nullptr) {
3274 /* Sprite layout which needs preprocessing */
3275 bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround);
3276 uint32_t var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3277 for (uint8_t var10 : SetBitIterator(var10_values)) {
3278 uint32_t var10_relocation = GetCustomStationRelocation(statspec, st, ti->tile, var10);
3279 layout->ProcessRegisters(var10, var10_relocation, separate_ground);
3280 }
3281 tmp_rail_layout.seq = layout->GetLayout(&tmp_rail_layout.ground);
3282 t = &tmp_rail_layout;
3283 total_offset = 0;
3284 } else if (statspec != nullptr) {
3285 /* Simple sprite layout */
3286 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3288 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3289 }
3290 ground_relocation += rti->fallback_railtype;
3291 }
3292
3293 draw_ground = true;
3294 }
3295
3296 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3297 SpriteID image = t->ground.sprite;
3298 PaletteID pal = t->ground.pal;
3299 RailTrackOffset overlay_offset;
3300 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3301 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
3302 DrawGroundSprite(image, PAL_NONE);
3303 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3304
3305 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3306 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
3307 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3308 }
3309 } else {
3310 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3311 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3312 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3313
3314 /* PBS debugging, draw reserved tracks darker */
3315 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3316 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
3317 }
3318 }
3319 }
3320
3322
3323 if (IsAnyRoadStop(ti->tile)) {
3324 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3325 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3326 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3327 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3328
3329 StationGfx view = GetStationGfx(ti->tile);
3330 StationType type = GetStationType(ti->tile);
3331
3332 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3333 RoadStopDrawModes stop_draw_mode{};
3334 if (stopspec != nullptr) {
3335 stop_draw_mode = stopspec->draw_mode;
3336 st = BaseStation::GetByTile(ti->tile);
3337 RoadStopResolverObject object(stopspec, st, ti->tile, INVALID_ROADTYPE, type, view);
3338 const SpriteGroup *group = object.Resolve();
3339 if (group != nullptr && group->type == SGT_TILELAYOUT) {
3340 if (stopspec->flags.Test(RoadStopSpecFlag::DrawModeRegister)) {
3341 stop_draw_mode = static_cast<RoadStopDrawMode>(GetRegister(0x100));
3342 }
3343 if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) {
3344 draw_ground = true;
3345 }
3346 t = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr);
3347 }
3348 }
3349
3350 /* Draw ground sprite */
3351 if (draw_ground) {
3352 SpriteID image = t->ground.sprite;
3353 PaletteID pal = t->ground.pal;
3354 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3355 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3356 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3357 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3358 }
3359 }
3360
3361 if (IsDriveThroughStopTile(ti->tile)) {
3362 if (type != StationType::RoadWaypoint && (stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Overlay))) {
3363 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
3364 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3365 }
3366 } else {
3367 /* Non-drivethrough road stops are only valid for roads. */
3368 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3369
3370 if ((stopspec == nullptr || stop_draw_mode.Test(RoadStopDrawMode::Road)) && road_rti->UsesOverlay()) {
3371 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
3372 DrawGroundSprite(ground + view, PAL_NONE);
3373 }
3374 }
3375
3376 if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
3377 /* Draw road, tram catenary */
3378 DrawRoadCatenary(ti);
3379 }
3380 }
3381
3382 if (IsRailWaypoint(ti->tile)) {
3383 /* Don't offset the waypoint graphics; they're always the same. */
3384 total_offset = 0;
3385 }
3386
3387 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
3388}
3389
3390void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3391{
3392 int32_t total_offset = 0;
3393 PaletteID pal = COMPANY_SPRITE_COLOUR(_local_company);
3394 const DrawTileSprites *t = GetStationTileLayout(st, image);
3395 const RailTypeInfo *railtype_info = nullptr;
3396
3397 if (railtype != INVALID_RAILTYPE) {
3398 railtype_info = GetRailTypeInfo(railtype);
3399 total_offset = railtype_info->GetRailtypeSpriteOffset();
3400 }
3401
3402 SpriteID img = t->ground.sprite;
3403 RailTrackOffset overlay_offset;
3404 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3405 SpriteID ground = GetCustomRailSprite(railtype_info, INVALID_TILE, RTSG_GROUND);
3406 DrawSprite(img, PAL_NONE, x, y);
3407 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3408 } else {
3409 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3410 }
3411
3412 if (roadtype != INVALID_ROADTYPE) {
3413 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3414 if (image >= 4) {
3415 /* Drive-through stop */
3416 uint sprite_offset = 5 - image;
3417
3418 /* Road underlay takes precedence over tram */
3419 if (roadtype_info->UsesOverlay()) {
3420 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_GROUND);
3421 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3422
3423 SpriteID overlay = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_OVERLAY);
3424 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3425 } else if (RoadTypeIsTram(roadtype)) {
3426 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3427 }
3428 } else {
3429 /* Bay stop */
3430 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3431 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_ROADSTOP);
3432 DrawSprite(ground + image, PAL_NONE, x, y);
3433 }
3434 }
3435 }
3436
3437 /* Default waypoint has no railtype specific sprites */
3438 DrawRailTileSeqInGUI(x, y, t, (st == StationType::RailWaypoint || st == StationType::RoadWaypoint) ? 0 : total_offset, 0, pal);
3439}
3440
3441static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)
3442{
3443 return GetTileMaxPixelZ(tile);
3444}
3445
3446static Foundation GetFoundation_Station(TileIndex, Slope tileh)
3447{
3448 return FlatteningFoundation(tileh);
3449}
3450
3451static void FillTileDescRoadStop(TileIndex tile, TileDesc &td)
3452{
3453 RoadType road_rt = GetRoadTypeRoad(tile);
3454 RoadType tram_rt = GetRoadTypeTram(tile);
3455 Owner road_owner = INVALID_OWNER;
3456 Owner tram_owner = INVALID_OWNER;
3457 if (road_rt != INVALID_ROADTYPE) {
3458 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3459 td.roadtype = rti->strings.name;
3460 td.road_speed = rti->max_speed / 2;
3461 road_owner = GetRoadOwner(tile, RTT_ROAD);
3462 }
3463
3464 if (tram_rt != INVALID_ROADTYPE) {
3465 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3466 td.tramtype = rti->strings.name;
3467 td.tram_speed = rti->max_speed / 2;
3468 tram_owner = GetRoadOwner(tile, RTT_TRAM);
3469 }
3470
3471 if (IsDriveThroughStopTile(tile)) {
3472 /* Is there a mix of owners? */
3473 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
3474 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
3475 uint i = 1;
3476 if (road_owner != INVALID_OWNER) {
3477 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3478 td.owner[i] = road_owner;
3479 i++;
3480 }
3481 if (tram_owner != INVALID_OWNER) {
3482 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3483 td.owner[i] = tram_owner;
3484 }
3485 }
3486 }
3487}
3488
3489void FillTileDescRailStation(TileIndex tile, TileDesc &td)
3490{
3491 const StationSpec *spec = GetStationSpec(tile);
3492
3493 if (spec != nullptr) {
3495 td.station_name = spec->name;
3496
3497 if (spec->grf_prop.HasGrfFile()) {
3498 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3499 td.grf = gc->GetName();
3500 }
3501 }
3502
3503 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3504 td.rail_speed = rti->max_speed;
3505 td.railtype = rti->strings.name;
3506}
3507
3508void FillTileDescAirport(TileIndex tile, TileDesc &td)
3509{
3510 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3512 td.airport_name = as->name;
3513
3515 td.airport_tile_name = ats->name;
3516
3517 if (as->grf_prop.HasGrfFile()) {
3518 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3519 td.grf = gc->GetName();
3520 } else if (ats->grf_prop.HasGrfFile()) {
3521 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3522 td.grf = gc->GetName();
3523 }
3524}
3525
3526static void GetTileDesc_Station(TileIndex tile, TileDesc &td)
3527{
3528 td.owner[0] = GetTileOwner(tile);
3530
3531 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3532 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3533 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3534
3535 StringID str;
3536 switch (GetStationType(tile)) {
3537 default: NOT_REACHED();
3538 case StationType::Rail: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3539 case StationType::Airport:
3540 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3541 break;
3542 case StationType::Truck: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3543 case StationType::Bus: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3544 case StationType::Oilrig: {
3545 const Industry *i = Station::GetByTile(tile)->industry;
3546 const IndustrySpec *is = GetIndustrySpec(i->type);
3547 td.owner[0] = i->owner;
3548 str = is->name;
3549 if (is->grf_prop.HasGrfFile()) td.grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3550 break;
3551 }
3552 case StationType::Dock: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3553 case StationType::Buoy: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3554 case StationType::RailWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3555 case StationType::RoadWaypoint: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3556 }
3557 td.str = str;
3558}
3559
3560
3561static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
3562{
3563 TrackBits trackbits = TRACK_BIT_NONE;
3564
3565 switch (mode) {
3566 case TRANSPORT_RAIL:
3567 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3568 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
3569 }
3570 break;
3571
3572 case TRANSPORT_WATER:
3573 /* buoy is coded as a station, it is always on open water */
3574 if (IsBuoy(tile)) {
3575 trackbits = TRACK_BIT_ALL;
3576 /* remove tracks that connect NE map edge */
3577 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
3578 /* remove tracks that connect NW map edge */
3579 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
3580 }
3581 break;
3582
3583 case TRANSPORT_ROAD:
3584 if (IsAnyRoadStop(tile)) {
3585 RoadTramType rtt = (RoadTramType)sub_mode;
3586 if (!HasTileRoadType(tile, rtt)) break;
3587
3588 if (IsBayRoadStopTile(tile)) {
3589 DiagDirection dir = GetBayRoadStopDir(tile);
3590 if (side != INVALID_DIAGDIR && dir != side) break;
3591 trackbits = DiagDirToDiagTrackBits(dir);
3592 } else {
3593 Axis axis = GetDriveThroughStopAxis(tile);
3594 if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
3595 trackbits = AxisToTrackBits(axis);
3596 }
3597 }
3598 break;
3599
3600 default:
3601 break;
3602 }
3603
3605}
3606
3607
3608static void TileLoop_Station(TileIndex tile)
3609{
3610 /* FIXME -- GetTileTrackStatus_Station -> animated stationtiles
3611 * hardcoded.....not good */
3612 switch (GetStationType(tile)) {
3613 case StationType::Airport:
3614 AirportTileAnimationTrigger(Station::GetByTile(tile), tile, AAT_TILELOOP);
3615 break;
3616
3617 case StationType::Dock:
3618 if (!IsTileFlat(tile)) break; // only handle water part
3619 [[fallthrough]];
3620
3621 case StationType::Oilrig: //(station part)
3622 case StationType::Buoy:
3623 TileLoop_Water(tile);
3624 break;
3625
3626 case StationType::RoadWaypoint: {
3628 case LandscapeType::Arctic:
3629 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3631 MarkTileDirtyByTile(tile);
3632 }
3633 break;
3634
3635 case LandscapeType::Tropic:
3638 MarkTileDirtyByTile(tile);
3639 }
3640 break;
3641
3642 default: break;
3643 }
3644
3645 HouseZonesBits new_zone = HZB_TOWN_EDGE;
3646 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3647 if (t != nullptr) {
3648 new_zone = GetTownRadiusGroup(t, tile);
3649 }
3650
3651 /* Adjust road ground type depending on 'new_zone' */
3652 Roadside new_rs = new_zone > HZB_TOWN_EDGE ? ROADSIDE_PAVED : ROADSIDE_GRASS;
3653 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3654
3655 if (new_rs != cur_rs) {
3656 SetRoadWaypointRoadside(tile, cur_rs == ROADSIDE_BARREN ? new_rs : ROADSIDE_BARREN);
3657 MarkTileDirtyByTile(tile);
3658 }
3659 break;
3660 }
3661
3662 default: break;
3663 }
3664}
3665
3666
3667static void AnimateTile_Station(TileIndex tile)
3668{
3669 if (HasStationRail(tile)) {
3670 AnimateStationTile(tile);
3671 return;
3672 }
3673
3674 if (IsAirport(tile)) {
3675 AnimateAirportTile(tile);
3676 return;
3677 }
3678
3679 if (IsAnyRoadStopTile(tile)) {
3680 AnimateRoadStopTile(tile);
3681 return;
3682 }
3683}
3684
3685
3686static bool ClickTile_Station(TileIndex tile)
3687{
3688 const BaseStation *bst = BaseStation::GetByTile(tile);
3689
3692 } else if (IsHangar(tile)) {
3693 const Station *st = Station::From(bst);
3695 } else {
3697 }
3698 return true;
3699}
3700
3701static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
3702{
3703 if (v->type == VEH_TRAIN) {
3704 StationID station_id = GetStationIndex(tile);
3705 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
3706 if (!IsRailStation(tile) || !v->IsFrontEngine()) return VETSB_CONTINUE;
3707
3708 int station_ahead;
3709 int station_length;
3710 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3711
3712 /* Stop whenever that amount of station ahead + the distance from the
3713 * begin of the platform to the stop location is longer than the length
3714 * of the platform. Station ahead 'includes' the current tile where the
3715 * vehicle is on, so we need to subtract that. */
3716 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return VETSB_CONTINUE;
3717
3719
3720 x &= 0xF;
3721 y &= 0xF;
3722
3723 if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
3724 if (y == TILE_SIZE / 2) {
3725 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
3726 stop &= TILE_SIZE - 1;
3727
3728 if (x == stop) {
3729 return VETSB_ENTERED_STATION | static_cast<VehicleEnterTileStatus>(station_id.base() << VETS_STATION_ID_OFFSET); // enter station
3730 } else if (x < stop) {
3732 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3733 if (spd < v->cur_speed) v->cur_speed = spd;
3734 }
3735 }
3736 } else if (v->type == VEH_ROAD) {
3738 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
3739 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3740 /* Attempt to allocate a parking bay in a road stop */
3742 }
3743 }
3744 }
3745
3746 return VETSB_CONTINUE;
3747}
3748
3754{
3755 /* Collect cargoes accepted since the last big tick. */
3756 CargoTypes cargoes = 0;
3757 for (CargoType cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
3758 if (HasBit(st->goods[cargo_type].status, GoodsEntry::GES_ACCEPTED_BIGTICK)) SetBit(cargoes, cargo_type);
3759 }
3760
3761 /* Anything to do? */
3762 if (cargoes == 0) return;
3763
3764 /* Loop over all houses in the catchment. */
3766 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3767 if (IsTileType(tile, MP_HOUSE)) {
3768 WatchedCargoCallback(tile, cargoes);
3769 }
3770 }
3771}
3772
3780{
3781 if (!st->IsInUse()) {
3782 if (++st->delete_ctr >= 8) delete st;
3783 return false;
3784 }
3785
3786 if (Station::IsExpected(st)) {
3788
3789 for (GoodsEntry &ge : Station::From(st)->goods) {
3791 }
3792 }
3793
3794
3796
3797 return true;
3798}
3799
3800static inline void byte_inc_sat(uint8_t *p)
3801{
3802 uint8_t b = *p + 1;
3803 if (b != 0) *p = b;
3804}
3805
3812static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3813{
3814 /* If truncating also punish the source stations' ratings to
3815 * decrease the flow of incoming cargo. */
3816
3817 if (!ge->HasData()) return;
3818
3819 StationCargoAmountMap waiting_per_source;
3820 ge->GetData().cargo.Truncate(amount, &waiting_per_source);
3821 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3822 Station *source_station = Station::GetIfValid(i->first);
3823 if (source_station == nullptr) continue;
3824
3825 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3826 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3827 }
3828}
3829
3830static void UpdateStationRating(Station *st)
3831{
3832 bool waiting_changed = false;
3833
3834 byte_inc_sat(&st->time_since_load);
3835 byte_inc_sat(&st->time_since_unload);
3836
3837 for (const CargoSpec *cs : CargoSpec::Iterate()) {
3838 GoodsEntry *ge = &st->goods[cs->Index()];
3839 /* Slowly increase the rating back to its original level in the case we
3840 * didn't deliver cargo yet to this station. This happens when a bribe
3841 * failed while you didn't moved that cargo yet to a station. */
3842 if (!ge->HasRating() && ge->rating < INITIAL_STATION_RATING) {
3843 ge->rating++;
3844 }
3845
3846 /* Only change the rating if we are moving this cargo */
3847 if (ge->HasRating()) {
3848 byte_inc_sat(&ge->time_since_pickup);
3851 ge->last_speed = 0;
3852 TruncateCargo(cs, ge);
3853 waiting_changed = true;
3854 continue;
3855 }
3856
3857 bool skip = false;
3858 int rating = 0;
3859 uint waiting = ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0;
3860
3861 /* num_dests is at least 1 if there is any cargo as
3862 * StationID::Invalid() is also a destination.
3863 */
3864 uint num_dests = ge->HasData() ? static_cast<uint>(ge->GetData().cargo.Packets()->MapSize()) : 0;
3865
3866 /* Average amount of cargo per next hop, but prefer solitary stations
3867 * with only one or two next hops. They are allowed to have more
3868 * cargo waiting per next hop.
3869 * With manual cargo distribution waiting_avg = waiting / 2 as then
3870 * StationID::Invalid() is the only destination.
3871 */
3872 uint waiting_avg = waiting / (num_dests + 1);
3873
3875 ge->rating = rating = MAX_STATION_RATING;
3876 skip = true;
3877 } else if (cs->callback_mask.Test(CargoCallbackMask::StationRatingCalc)) {
3878 /* Perform custom station rating. If it succeeds the speed, days in transit and
3879 * waiting cargo ratings must not be executed. */
3880
3881 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
3882 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
3883
3884 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
3885 | (ClampTo<uint16_t>(ge->max_waiting_cargo) << 8)
3886 | (ClampTo<uint8_t>(last_speed) << 24);
3887 /* Convert to the 'old' vehicle types */
3888 uint32_t var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
3889 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
3890 if (callback != CALLBACK_FAILED) {
3891 skip = true;
3892 rating = GB(callback, 0, 14);
3893
3894 /* Simulate a 15 bit signed value */
3895 if (HasBit(callback, 14)) rating -= 0x4000;
3896 }
3897 }
3898
3899 if (!skip) {
3900 int b = ge->last_speed - 85;
3901 if (b >= 0) rating += b >> 2;
3902
3903 uint8_t waittime = ge->time_since_pickup;
3904 if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
3905 if (waittime <= 21) rating += 25;
3906 if (waittime <= 12) rating += 25;
3907 if (waittime <= 6) rating += 45;
3908 if (waittime <= 3) rating += 35;
3909
3910 rating -= 90;
3911 if (ge->max_waiting_cargo <= 1500) rating += 55;
3912 if (ge->max_waiting_cargo <= 1000) rating += 35;
3913 if (ge->max_waiting_cargo <= 600) rating += 10;
3914 if (ge->max_waiting_cargo <= 300) rating += 20;
3915 if (ge->max_waiting_cargo <= 100) rating += 10;
3916 }
3917
3918 if (Company::IsValidID(st->owner) && st->town->statues.Test(st->owner)) rating += 26;
3919
3920 uint8_t age = ge->last_age;
3921 if (age < 3) rating += 10;
3922 if (age < 2) rating += 10;
3923 if (age < 1) rating += 13;
3924
3925 {
3926 int or_ = ge->rating; // old rating
3927
3928 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
3929 ge->rating = rating = or_ + Clamp(ClampTo<uint8_t>(rating) - or_, -2, 2);
3930
3931 /* if rating is <= 64 and more than 100 items waiting on average per destination,
3932 * remove some random amount of goods from the station */
3933 if (rating <= 64 && waiting_avg >= 100) {
3934 int dec = Random() & 0x1F;
3935 if (waiting_avg < 200) dec &= 7;
3936 waiting -= (dec + 1) * num_dests;
3937 waiting_changed = true;
3938 }
3939
3940 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
3941 if (rating <= 127 && waiting != 0) {
3942 uint32_t r = Random();
3943 if (rating <= (int)GB(r, 0, 7)) {
3944 /* Need to have int, otherwise it will just overflow etc. */
3945 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) - 1) * num_dests), 0);
3946 waiting_changed = true;
3947 }
3948 }
3949
3950 /* At some point we really must cap the cargo. Previously this
3951 * was a strict 4095, but now we'll have a less strict, but
3952 * increasingly aggressive truncation of the amount of cargo. */
3953 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
3954 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
3955 static const uint MAX_WAITING_CARGO = 1 << 15;
3956
3957 if (waiting > WAITING_CARGO_THRESHOLD) {
3958 uint difference = waiting - WAITING_CARGO_THRESHOLD;
3959 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
3960
3961 waiting = std::min(waiting, MAX_WAITING_CARGO);
3962 waiting_changed = true;
3963 }
3964
3965 /* We can't truncate cargo that's already reserved for loading.
3966 * Thus StoredCount() here. */
3967 if (waiting_changed && waiting < (ge->HasData() ? ge->GetData().cargo.AvailableCount() : 0)) {
3968 /* Feed back the exact own waiting cargo at this station for the
3969 * next rating calculation. */
3970 ge->max_waiting_cargo = 0;
3971
3972 TruncateCargo(cs, ge, ge->GetData().cargo.AvailableCount() - waiting);
3973 } else {
3974 /* If the average number per next hop is low, be more forgiving. */
3975 ge->max_waiting_cargo = waiting_avg;
3976 }
3977 }
3978 }
3979 }
3980
3981 StationID index = st->index;
3982 if (waiting_changed) {
3983 SetWindowDirty(WC_STATION_VIEW, index); // update whole window
3984 } else {
3985 SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
3986 }
3987}
3988
3998{
3999 GoodsEntry &ge = st->goods[c];
4000
4001 /* Reroute cargo in station. */
4002 if (ge.HasData()) ge.GetData().cargo.Reroute(UINT_MAX, &ge.GetData().cargo, avoid, avoid2, &ge);
4003
4004 /* Reroute cargo staged to be transferred. */
4005 for (Vehicle *v : st->loading_vehicles) {
4006 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4007 if (u->cargo_type != c) continue;
4008 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4009 }
4010 }
4011}
4012
4022{
4023 for (CargoType c = 0; c < NUM_CARGO; ++c) {
4024 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(c) != DT_MANUAL);
4025 GoodsEntry &ge = from->goods[c];
4027 if (lg == nullptr) continue;
4028 std::vector<NodeID> to_remove{};
4029 for (Edge &edge : (*lg)[ge.node].edges) {
4030 Station *to = Station::Get((*lg)[edge.dest_node].station);
4031 assert(to->goods[c].node == edge.dest_node);
4032 assert(TimerGameEconomy::date >= edge.LastUpdate());
4033 auto timeout = TimerGameEconomy::Date(LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3));
4034 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4035 bool updated = false;
4036
4037 if (auto_distributed) {
4038 /* Have all vehicles refresh their next hops before deciding to
4039 * remove the node. */
4040 std::vector<Vehicle *> vehicles;
4041 for (OrderList *l : OrderList::Iterate()) {
4042 bool found_from = false;
4043 bool found_to = false;
4044 for (Order *order = l->GetFirstOrder(); order != nullptr; order = order->next) {
4045 if (!order->IsType(OT_GOTO_STATION) && !order->IsType(OT_IMPLICIT)) continue;
4046 if (order->GetDestination() == from->index) {
4047 found_from = true;
4048 if (found_to) break;
4049 } else if (order->GetDestination() == to->index) {
4050 found_to = true;
4051 if (found_from) break;
4052 }
4053 }
4054 if (!found_to || !found_from) continue;
4055 vehicles.push_back(l->GetFirstSharedVehicle());
4056 }
4057
4058 auto iter = vehicles.begin();
4059 while (iter != vehicles.end()) {
4060 Vehicle *v = *iter;
4061 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4063 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4064 }
4065 if (edge.LastUpdate() == TimerGameEconomy::date) {
4066 updated = true;
4067 break;
4068 }
4069
4070 Vehicle *next_shared = v->NextShared();
4071 if (next_shared) {
4072 *iter = next_shared;
4073 ++iter;
4074 } else {
4075 iter = vehicles.erase(iter);
4076 }
4077
4078 if (iter == vehicles.end()) iter = vehicles.begin();
4079 }
4080 }
4081
4082 if (!updated) {
4083 /* If it's still considered dead remove it. */
4084 to_remove.emplace_back(to->goods[c].node);
4085 if (ge.HasData()) ge.GetData().flows.DeleteFlows(to->index);
4086 RerouteCargo(from, c, to->index, from->index);
4087 }
4088 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4089 edge.Restrict();
4090 if (ge.HasData()) ge.GetData().flows.RestrictFlows(to->index);
4091 RerouteCargo(from, c, to->index, from->index);
4092 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4093 edge.Release();
4094 }
4095 }
4096 /* Remove dead edges. */
4097 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4098
4099 assert(TimerGameEconomy::date >= lg->LastCompression());
4101 lg->Compress();
4102 }
4103 }
4104}
4105
4115void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateMode mode)
4116{
4117 GoodsEntry &ge1 = st->goods[cargo];
4118 Station *st2 = Station::Get(next_station_id);
4119 GoodsEntry &ge2 = st2->goods[cargo];
4120 LinkGraph *lg = nullptr;
4121 if (ge1.link_graph == LinkGraphID::Invalid()) {
4122 if (ge2.link_graph == LinkGraphID::Invalid()) {
4124 lg = new LinkGraph(cargo);
4126 ge2.link_graph = lg->index;
4127 ge2.node = lg->AddNode(st2);
4128 } else {
4129 Debug(misc, 0, "Can't allocate link graph");
4130 }
4131 } else {
4132 lg = LinkGraph::Get(ge2.link_graph);
4133 }
4134 if (lg) {
4135 ge1.link_graph = lg->index;
4136 ge1.node = lg->AddNode(st);
4137 }
4138 } else if (ge2.link_graph == LinkGraphID::Invalid()) {
4139 lg = LinkGraph::Get(ge1.link_graph);
4140 ge2.link_graph = lg->index;
4141 ge2.node = lg->AddNode(st2);
4142 } else {
4143 lg = LinkGraph::Get(ge1.link_graph);
4144 if (ge1.link_graph != ge2.link_graph) {
4146 if (lg->Size() < lg2->Size()) {
4148 lg2->Merge(lg); // Updates GoodsEntries of lg
4149 lg = lg2;
4150 } else {
4152 lg->Merge(lg2); // Updates GoodsEntries of lg2
4153 }
4154 }
4155 }
4156 if (lg != nullptr) {
4157 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, mode);
4158 }
4159}
4160
4167void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4168{
4169 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4170 if (v->refit_cap > 0) {
4171 /* The cargo count can indeed be higher than the refit_cap if
4172 * wagons have been auto-replaced and subsequently auto-
4173 * refitted to a higher capacity. The cargo gets redistributed
4174 * among the wagons in that case.
4175 * As usage is not such an important figure anyway we just
4176 * ignore the additional cargo then.*/
4177 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4178 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EUM_INCREASE);
4179 }
4180 }
4181}
4182
4183/* called for every station each tick */
4184static void StationHandleSmallTick(BaseStation *st)
4185{
4186 if (st->facilities.Test(StationFacility::Waypoint) || !st->IsInUse()) return;
4187
4188 uint8_t b = st->delete_ctr + 1;
4189 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4190 st->delete_ctr = b;
4191
4192 if (b == 0) UpdateStationRating(Station::From(st));
4193}
4194
4195void OnTick_Station()
4196{
4197 if (_game_mode == GM_EDITOR) return;
4198
4199 for (BaseStation *st : BaseStation::Iterate()) {
4200 StationHandleSmallTick(st);
4201
4202 /* Clean up the link graph about once a week. */
4205 };
4206
4207 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4209 /* Stop processing this station if it was deleted */
4210 if (!StationHandleBigTick(st)) continue;
4211 }
4212
4213 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4215 TriggerStationAnimation(st, st->xy, SAT_250_TICKS);
4216 TriggerRoadStopAnimation(st, st->xy, SAT_250_TICKS);
4217 if (Station::IsExpected(st)) AirportAnimationTrigger(Station::From(st), AAT_STATION_250_TICKS);
4218 }
4219 }
4220}
4221
4223static IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto)
4224{
4225 for (Station *st : Station::Iterate()) {
4226 for (GoodsEntry &ge : st->goods) {
4229 }
4230 }
4231});
4232
4233void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4234{
4235 ForAllStationsRadius(tile, radius, [&](Station *st) {
4236 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4237 for (GoodsEntry &ge : st->goods) {
4238 if (ge.status != 0) {
4239 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4240 }
4241 }
4242 }
4243 });
4244}
4245
4246static uint UpdateStationWaiting(Station *st, CargoType type, uint amount, Source source)
4247{
4248 /* We can't allocate a CargoPacket? Then don't do anything
4249 * at all; i.e. just discard the incoming cargo. */
4250 if (!CargoPacket::CanAllocateItem()) return 0;
4251
4252 GoodsEntry &ge = st->goods[type];
4253 amount += ge.amount_fract;
4254 ge.amount_fract = GB(amount, 0, 8);
4255
4256 amount >>= 8;
4257 /* No new "real" cargo item yet. */
4258 if (amount == 0) return 0;
4259
4260 StationID next = ge.GetVia(st->index);
4261 ge.GetOrCreateData().cargo.Append(new CargoPacket(st->index, amount, source), next);
4262 LinkGraph *lg = nullptr;
4263 if (ge.link_graph == LinkGraphID::Invalid()) {
4265 lg = new LinkGraph(type);
4267 ge.link_graph = lg->index;
4268 ge.node = lg->AddNode(st);
4269 } else {
4270 Debug(misc, 0, "Can't allocate link graph");
4271 }
4272 } else {
4273 lg = LinkGraph::Get(ge.link_graph);
4274 }
4275 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4276
4277 if (!ge.HasRating()) {
4280 }
4281
4283 TriggerStationAnimation(st, st->xy, SAT_NEW_CARGO, type);
4284 AirportAnimationTrigger(st, AAT_STATION_NEW_CARGO, type);
4285 TriggerRoadStopRandomisation(st, st->xy, RSRT_NEW_CARGO, type);
4286 TriggerRoadStopAnimation(st, st->xy, SAT_NEW_CARGO, type);
4287
4288
4290 st->MarkTilesDirty(true);
4291 return amount;
4292}
4293
4294static bool IsUniqueStationName(const std::string &name)
4295{
4296 for (const Station *st : Station::Iterate()) {
4297 if (!st->name.empty() && st->name == name) return false;
4298 }
4299
4300 return true;
4301}
4302
4310CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text)
4311{
4312 Station *st = Station::GetIfValid(station_id);
4313 if (st == nullptr) return CMD_ERROR;
4314
4315 CommandCost ret = CheckOwnership(st->owner);
4316 if (ret.Failed()) return ret;
4317
4318 bool reset = text.empty();
4319
4320 if (!reset) {
4322 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4323 }
4324
4325 if (flags.Test(DoCommandFlag::Execute)) {
4326 st->cached_name.clear();
4327 if (reset) {
4328 st->name.clear();
4329 } else {
4330 st->name = text;
4331 }
4332
4333 st->UpdateVirtCoord();
4335 }
4336
4337 return CommandCost();
4338}
4339
4340static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4341{
4342 for (Station *st : nearby) {
4343 if (st->TileIsInCatchment(tile)) stations.insert(st);
4344 }
4345}
4346
4352{
4353 if (this->tile != INVALID_TILE) {
4354 if (IsTileType(this->tile, MP_HOUSE)) {
4355 /* Town nearby stations need to be filtered per tile. */
4356 assert(this->w == 1 && this->h == 1);
4357 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4358 } else {
4359 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4360 this->stations.insert(st);
4361 return true;
4362 });
4363 }
4364 this->tile = INVALID_TILE;
4365 }
4366 return this->stations;
4367}
4368
4369
4370static bool CanMoveGoodsToStation(const Station *st, CargoType type)
4371{
4372 /* Is the station reserved exclusively for somebody else? */
4373 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4374
4375 /* Lowest possible rating, better not to give cargo anymore. */
4376 if (st->goods[type].rating == 0) return false;
4377
4378 /* Selectively servicing stations, and not this one. */
4379 if (_settings_game.order.selectgoods && !st->goods[type].HasVehicleEverTriedLoading()) return false;
4380
4382 /* Passengers are never served by just a truck stop. */
4383 if (st->facilities == StationFacility::TruckStop) return false;
4384 } else {
4385 /* Non-passengers are never served by just a bus stop. */
4386 if (st->facilities == StationFacility::BusStop) return false;
4387 }
4388 return true;
4389}
4390
4391uint MoveGoodsToStation(CargoType type, uint amount, Source source, const StationList &all_stations, Owner exclusivity)
4392{
4393 /* Return if nothing to do. Also the rounding below fails for 0. */
4394 if (all_stations.empty()) return 0;
4395 if (amount == 0) return 0;
4396
4397 Station *first_station = nullptr;
4398 typedef std::pair<Station *, uint> StationInfo;
4399 std::vector<StationInfo> used_stations;
4400
4401 for (Station *st : all_stations) {
4402 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4403 if (!CanMoveGoodsToStation(st, type)) continue;
4404
4405 /* Avoid allocating a vector if there is only one station to significantly
4406 * improve performance in this common case. */
4407 if (first_station == nullptr) {
4408 first_station = st;
4409 continue;
4410 }
4411 if (used_stations.empty()) {
4412 used_stations.reserve(2);
4413 used_stations.emplace_back(first_station, 0);
4414 }
4415 used_stations.emplace_back(st, 0);
4416 }
4417
4418 /* no stations around at all? */
4419 if (first_station == nullptr) return 0;
4420
4421 if (used_stations.empty()) {
4422 /* only one station around */
4423 amount *= first_station->goods[type].rating + 1;
4424 return UpdateStationWaiting(first_station, type, amount, source);
4425 }
4426
4427 ReferenceThroughBaseContainer<std::array<uint32_t, OWNER_END.base()>> company_best = {}; // best rating for each company, including OWNER_NONE
4428 ReferenceThroughBaseContainer<std::array<uint32_t, OWNER_END.base()>> company_sum = {}; // sum of ratings for each company
4429 uint best_rating = 0;
4430 uint best_sum = 0; // sum of best ratings for each company
4431
4432 for (auto &p : used_stations) {
4433 auto owner = p.first->owner;
4434 auto rating = p.first->goods[type].rating;
4435 if (rating > company_best[owner]) {
4436 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4437 company_best[owner] = rating;
4438 if (rating > best_rating) best_rating = rating;
4439 }
4440 company_sum[owner] += rating;
4441 }
4442
4443 /* From now we'll calculate with fractional cargo amounts.
4444 * First determine how much cargo we really have. */
4445 amount *= best_rating + 1;
4446
4447 uint moving = 0;
4448 for (auto &p : used_stations) {
4449 Owner owner = p.first->owner;
4450 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4451 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4452 p.second = amount * company_best[owner] * p.first->goods[type].rating / best_sum / company_sum[owner];
4453 moving += p.second;
4454 }
4455
4456 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4457 if (amount > moving) {
4458 std::stable_sort(used_stations.begin(), used_stations.end(), [type](const StationInfo &a, const StationInfo &b) {
4459 return b.first->goods[type].rating < a.first->goods[type].rating;
4460 });
4461
4462 assert(amount - moving <= used_stations.size());
4463 for (uint i = 0; i < amount - moving; i++) {
4464 used_stations[i].second++;
4465 }
4466 }
4467
4468 uint moved = 0;
4469 for (auto &p : used_stations) {
4470 moved += UpdateStationWaiting(p.first, type, p.second, source);
4471 }
4472
4473 return moved;
4474}
4475
4476void UpdateStationDockingTiles(Station *st)
4477{
4478 st->docking_station.Clear();
4479
4480 /* For neutral stations, start with the industry area instead of dock area */
4481 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4482
4483 if (area->tile == INVALID_TILE) return;
4484
4485 int x = TileX(area->tile);
4486 int y = TileY(area->tile);
4487
4488 /* Expand the area by a tile on each side while
4489 * making sure that we remain inside the map. */
4490 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4491 int x1 = std::max<int>(x - 1, 0);
4492
4493 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4494 int y1 = std::max<int>(y - 1, 0);
4495
4496 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4497 for (TileIndex tile : ta) {
4498 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4499 }
4500}
4501
4502void BuildOilRig(TileIndex tile)
4503{
4504 if (!Station::CanAllocateItem()) {
4505 Debug(misc, 0, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4506 return;
4507 }
4508
4509 Station *st = new Station(tile);
4510 _station_kdtree.Insert(st->index);
4511 st->town = ClosestTownFromTile(tile, UINT_MAX);
4512
4513 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
4514
4515 assert(IsTileType(tile, MP_INDUSTRY));
4516 /* Mark industry as associated both ways */
4517 st->industry = Industry::GetByTile(tile);
4518 st->industry->neutral_station = st;
4519 DeleteAnimatedTile(tile);
4520 MakeOilrig(tile, st->index, GetWaterClass(tile));
4521
4522 st->owner = OWNER_NONE;
4523 st->airport.type = AT_OILRIG;
4524 st->airport.Add(tile);
4525 st->ship_station.Add(tile);
4528 UpdateStationDockingTiles(st);
4529
4530 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
4531
4532 st->UpdateVirtCoord();
4533
4534 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4535 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4536 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4538 StationList nearby = std::move(st->industry->stations_near);
4539 st->industry->stations_near.clear();
4540 for (Station *near : nearby) {
4541 near->RecomputeCatchment(true);
4542 UpdateStationAcceptance(near, true);
4543 }
4544 }
4545
4546 st->RecomputeCatchment();
4547 UpdateStationAcceptance(st, false);
4548}
4549
4550void DeleteOilRig(TileIndex tile)
4551{
4552 Station *st = Station::GetByTile(tile);
4553
4554 MakeWaterKeepingClass(tile, OWNER_NONE);
4555
4556 /* The oil rig station is not supposed to be shared with anything else */
4557 [[maybe_unused]] static constexpr StationFacilities expected_facility{StationFacility::Airport, StationFacility::Dock};
4558 assert(st->facilities == expected_facility && st->airport.type == AT_OILRIG);
4559 if (st->industry != nullptr && st->industry->neutral_station == st) {
4560 /* Don't leave dangling neutral station pointer */
4561 st->industry->neutral_station = nullptr;
4562 }
4563 delete st;
4564}
4565
4566static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4567{
4568
4569 if (IsAnyRoadStopTile(tile)) {
4570 for (RoadTramType rtt : _roadtramtypes) {
4571 /* Update all roadtypes, no matter if they are present */
4572 if (GetRoadOwner(tile, rtt) == old_owner) {
4573 RoadType rt = GetRoadType(tile, rtt);
4574 if (rt != INVALID_ROADTYPE) {
4575 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4576 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4577 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4578 }
4579 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4580 }
4581 }
4582 }
4583
4584 if (!IsTileOwner(tile, old_owner)) return;
4585
4586 if (new_owner != INVALID_OWNER) {
4587 /* Update company infrastructure counts. Only do it here
4588 * if the new owner is valid as otherwise the clear
4589 * command will do it for us. No need to dirty windows
4590 * here, we'll redraw the whole screen anyway.*/
4591 Company *old_company = Company::Get(old_owner);
4592 Company *new_company = Company::Get(new_owner);
4593
4594 /* Update counts for underlying infrastructure. */
4595 switch (GetStationType(tile)) {
4596 case StationType::Rail:
4597 case StationType::RailWaypoint:
4598 if (!IsStationTileBlocked(tile)) {
4599 old_company->infrastructure.rail[GetRailType(tile)]--;
4600 new_company->infrastructure.rail[GetRailType(tile)]++;
4601 }
4602 break;
4603
4604 case StationType::Bus:
4605 case StationType::Truck:
4606 case StationType::RoadWaypoint:
4607 /* Road stops were already handled above. */
4608 break;
4609
4610 case StationType::Buoy:
4611 case StationType::Dock:
4612 if (GetWaterClass(tile) == WATER_CLASS_CANAL) {
4613 old_company->infrastructure.water--;
4614 new_company->infrastructure.water++;
4615 }
4616 break;
4617
4618 default:
4619 break;
4620 }
4621
4622 /* Update station tile count. */
4623 if (!IsBuoy(tile) && !IsAirport(tile)) {
4624 old_company->infrastructure.station--;
4625 new_company->infrastructure.station++;
4626 }
4627
4628 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4629 SetTileOwner(tile, new_owner);
4631 } else {
4632 if (IsDriveThroughStopTile(tile)) {
4633 /* Remove the drive-through road stop */
4634 if (IsRoadWaypoint(tile)) {
4636 } else {
4638 }
4639 assert(IsTileType(tile, MP_ROAD));
4640 /* Change owner of tile and all roadtypes */
4641 ChangeTileOwner(tile, old_owner, new_owner);
4642 } else {
4644 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4645 * Update owner of buoy if it was not removed (was in orders).
4646 * Do not update when owned by OWNER_WATER (sea and rivers). */
4647 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4648 }
4649 }
4650}
4651
4661{
4662 /* Water flooding can always clear road stops. */
4663 if (_current_company == OWNER_WATER) return CommandCost();
4664
4665 CommandCost ret;
4666
4667 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4668 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
4669 if (tram_owner != OWNER_NONE) {
4670 ret = CheckOwnership(tram_owner);
4671 if (ret.Failed()) return ret;
4672 }
4673 }
4674
4675 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4676 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
4677 if (road_owner == OWNER_TOWN) {
4678 ret = CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags);
4679 if (ret.Failed()) return ret;
4680 } else if (road_owner != OWNER_NONE) {
4681 ret = CheckOwnership(road_owner);
4682 if (ret.Failed()) return ret;
4683 }
4684 }
4685
4686 return CommandCost();
4687}
4688
4696{
4697 if (flags.Test(DoCommandFlag::Auto)) {
4698 switch (GetStationType(tile)) {
4699 default: break;
4700 case StationType::Rail: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4701 case StationType::RailWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4702 case StationType::Airport: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4703 case StationType::Truck: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4704 case StationType::Bus: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4705 case StationType::RoadWaypoint: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4706 case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4707 case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4708 case StationType::Oilrig:
4709 return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG);
4710 }
4711 }
4712
4713 switch (GetStationType(tile)) {
4714 case StationType::Rail: return RemoveRailStation(tile, flags);
4715 case StationType::RailWaypoint: return RemoveRailWaypoint(tile, flags);
4716 case StationType::Airport: return RemoveAirport(tile, flags);
4717 case StationType::Truck: [[fallthrough]];
4718 case StationType::Bus:
4719 if (IsDriveThroughStopTile(tile)) {
4720 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4721 if (remove_road.Failed()) return remove_road;
4722 }
4723 return RemoveRoadStop(tile, flags);
4724 case StationType::RoadWaypoint: {
4725 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4726 if (remove_road.Failed()) return remove_road;
4727 return RemoveRoadWaypointStop(tile, flags);
4728 }
4729 case StationType::Buoy: return RemoveBuoy(tile, flags);
4730 case StationType::Dock: return RemoveDock(tile, flags);
4731 default: break;
4732 }
4733
4734 return CMD_ERROR;
4735}
4736
4737static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
4738{
4740 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4741 * TTDP does not call it.
4742 */
4743 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4744 switch (GetStationType(tile)) {
4745 case StationType::RailWaypoint:
4746 case StationType::Rail: {
4747 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4748 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4749 }
4750
4751 case StationType::Airport:
4752 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4753
4754 case StationType::Truck:
4755 case StationType::Bus:
4756 case StationType::RoadWaypoint: {
4757 if (IsDriveThroughStopTile(tile)) {
4758 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4759 } else {
4760 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4761 }
4762 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4763 }
4764
4765 default: break;
4766 }
4767 }
4768 }
4769 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
4770}
4771
4778{
4779 uint32_t prev = 0;
4780 for (const auto &it : this->shares) {
4781 if (it.second == st) {
4782 return it.first - prev;
4783 } else {
4784 prev = it.first;
4785 }
4786 }
4787 return 0;
4788}
4789
4797{
4798 if (this->unrestricted == 0) return StationID::Invalid();
4799 assert(!this->shares.empty());
4800 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
4801 assert(it != this->shares.end() && it->first <= this->unrestricted);
4802 if (it->second != excluded && it->second != excluded2) return it->second;
4803
4804 /* We've hit one of the excluded stations.
4805 * Draw another share, from outside its range. */
4806
4807 uint end = it->first;
4808 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
4809 uint interval = end - begin;
4810 if (interval >= this->unrestricted) return StationID::Invalid(); // Only one station in the map.
4811 uint new_max = this->unrestricted - interval;
4812 uint rand = RandomRange(new_max);
4813 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
4814 this->shares.upper_bound(rand + interval);
4815 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
4816 if (it2->second != excluded && it2->second != excluded2) return it2->second;
4817
4818 /* We've hit the second excluded station.
4819 * Same as before, only a bit more complicated. */
4820
4821 uint end2 = it2->first;
4822 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
4823 uint interval2 = end2 - begin2;
4824 if (interval2 >= new_max) return StationID::Invalid(); // Only the two excluded stations in the map.
4825 new_max -= interval2;
4826 if (begin > begin2) {
4827 Swap(begin, begin2);
4828 Swap(end, end2);
4829 Swap(interval, interval2);
4830 }
4831 rand = RandomRange(new_max);
4832 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
4833 if (rand < begin) {
4834 it3 = this->shares.upper_bound(rand);
4835 } else if (rand < begin2 - interval) {
4836 it3 = this->shares.upper_bound(rand + interval);
4837 } else {
4838 it3 = this->shares.upper_bound(rand + interval + interval2);
4839 }
4840 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
4841 return it3->second;
4842}
4843
4850{
4851 assert(!this->shares.empty());
4852 SharesMap new_shares;
4853 uint i = 0;
4854 for (const auto &it : this->shares) {
4855 new_shares[++i] = it.second;
4856 if (it.first == this->unrestricted) this->unrestricted = i;
4857 }
4858 this->shares.swap(new_shares);
4859 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
4860}
4861
4869{
4870 /* We assert only before changing as afterwards the shares can actually
4871 * be empty. In that case the whole flow stat must be deleted then. */
4872 assert(!this->shares.empty());
4873
4874 uint removed_shares = 0;
4875 uint added_shares = 0;
4876 uint last_share = 0;
4877 SharesMap new_shares;
4878 for (const auto &it : this->shares) {
4879 if (it.second == st) {
4880 if (flow < 0) {
4881 uint share = it.first - last_share;
4882 if (flow == INT_MIN || (uint)(-flow) >= share) {
4883 removed_shares += share;
4884 if (it.first <= this->unrestricted) this->unrestricted -= share;
4885 if (flow != INT_MIN) flow += share;
4886 last_share = it.first;
4887 continue; // remove the whole share
4888 }
4889 removed_shares += (uint)(-flow);
4890 } else {
4891 added_shares += (uint)(flow);
4892 }
4893 if (it.first <= this->unrestricted) this->unrestricted += flow;
4894
4895 /* If we don't continue above the whole flow has been added or
4896 * removed. */
4897 flow = 0;
4898 }
4899 new_shares[it.first + added_shares - removed_shares] = it.second;
4900 last_share = it.first;
4901 }
4902 if (flow > 0) {
4903 new_shares[last_share + (uint)flow] = st;
4904 if (this->unrestricted < last_share) {
4905 this->ReleaseShare(st);
4906 } else {
4907 this->unrestricted += flow;
4908 }
4909 }
4910 this->shares.swap(new_shares);
4911}
4912
4919{
4920 assert(!this->shares.empty());
4921 uint flow = 0;
4922 uint last_share = 0;
4923 SharesMap new_shares;
4924 for (auto &it : this->shares) {
4925 if (flow == 0) {
4926 if (it.first > this->unrestricted) return; // Not present or already restricted.
4927 if (it.second == st) {
4928 flow = it.first - last_share;
4929 this->unrestricted -= flow;
4930 } else {
4931 new_shares[it.first] = it.second;
4932 }
4933 } else {
4934 new_shares[it.first - flow] = it.second;
4935 }
4936 last_share = it.first;
4937 }
4938 if (flow == 0) return;
4939 new_shares[last_share + flow] = st;
4940 this->shares.swap(new_shares);
4941 assert(!this->shares.empty());
4942}
4943
4950{
4951 assert(!this->shares.empty());
4952 uint flow = 0;
4953 uint next_share = 0;
4954 bool found = false;
4955 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
4956 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
4957 if (found) {
4958 flow = next_share - it->first;
4959 this->unrestricted += flow;
4960 break;
4961 } else {
4962 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
4963 if (it->second == st) found = true;
4964 }
4965 next_share = it->first;
4966 }
4967 if (flow == 0) return;
4968 SharesMap new_shares;
4969 new_shares[flow] = st;
4970 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
4971 if (it->second != st) {
4972 new_shares[flow + it->first] = it->second;
4973 } else {
4974 flow = 0;
4975 }
4976 }
4977 this->shares.swap(new_shares);
4978 assert(!this->shares.empty());
4979}
4980
4987{
4988 assert(runtime > 0);
4989 SharesMap new_shares;
4990 uint share = 0;
4991 for (auto i : this->shares) {
4992 share = std::max(share + 1, i.first * 30 / runtime);
4993 new_shares[share] = i.second;
4994 if (this->unrestricted == i.first) this->unrestricted = share;
4995 }
4996 this->shares.swap(new_shares);
4997}
4998
5005void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5006{
5007 FlowStatMap::iterator origin_it = this->find(origin);
5008 if (origin_it == this->end()) {
5009 this->emplace(origin, FlowStat(via, flow));
5010 } else {
5011 origin_it->second.ChangeShare(via, flow);
5012 assert(!origin_it->second.GetShares()->empty());
5013 }
5014}
5015
5024void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5025{
5026 FlowStatMap::iterator prev_it = this->find(origin);
5027 if (prev_it == this->end()) {
5028 FlowStat fs(via, flow);
5029 fs.AppendShare(StationID::Invalid(), flow);
5030 this->emplace(origin, fs);
5031 } else {
5032 prev_it->second.ChangeShare(via, flow);
5033 prev_it->second.ChangeShare(StationID::Invalid(), flow);
5034 assert(!prev_it->second.GetShares()->empty());
5035 }
5036}
5037
5043{
5044 for (auto &i : *this) {
5045 FlowStat &fs = i.second;
5046 uint local = fs.GetShare(StationID::Invalid());
5047 if (local > INT_MAX) { // make sure it fits in an int
5048 fs.ChangeShare(self, -INT_MAX);
5049 fs.ChangeShare(StationID::Invalid(), -INT_MAX);
5050 local -= INT_MAX;
5051 }
5052 fs.ChangeShare(self, -(int)local);
5053 fs.ChangeShare(StationID::Invalid(), -(int)local);
5054
5055 /* If the local share is used up there must be a share for some
5056 * remote station. */
5057 assert(!fs.GetShares()->empty());
5058 }
5059}
5060
5068{
5069 StationIDStack ret;
5070 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5071 FlowStat &s_flows = f_it->second;
5072 s_flows.ChangeShare(via, INT_MIN);
5073 if (s_flows.GetShares()->empty()) {
5074 ret.Push(f_it->first.base());
5075 this->erase(f_it++);
5076 } else {
5077 ++f_it;
5078 }
5079 }
5080 return ret;
5081}
5082
5088{
5089 for (auto &it : *this) {
5090 it.second.RestrictShare(via);
5091 }
5092}
5093
5099{
5100 for (auto &it : *this) {
5101 it.second.ReleaseShare(via);
5102 }
5103}
5104
5110{
5111 uint ret = 0;
5112 for (const auto &it : *this) {
5113 ret += (--(it.second.GetShares()->end()))->first;
5114 }
5115 return ret;
5116}
5117
5124{
5125 uint ret = 0;
5126 for (const auto &it : *this) {
5127 ret += it.second.GetShare(via);
5128 }
5129 return ret;
5130}
5131
5138{
5139 FlowStatMap::const_iterator i = this->find(from);
5140 if (i == this->end()) return 0;
5141 return (--(i->second.GetShares()->end()))->first;
5142}
5143
5151{
5152 FlowStatMap::const_iterator i = this->find(from);
5153 if (i == this->end()) return 0;
5154 return i->second.GetShare(via);
5155}
5156
5157extern const TileTypeProcs _tile_type_station_procs = {
5158 DrawTile_Station, // draw_tile_proc
5159 GetSlopePixelZ_Station, // get_slope_z_proc
5160 ClearTile_Station, // clear_tile_proc
5161 nullptr, // add_accepted_cargo_proc
5162 GetTileDesc_Station, // get_tile_desc_proc
5163 GetTileTrackStatus_Station, // get_tile_track_status_proc
5164 ClickTile_Station, // click_tile_proc
5165 AnimateTile_Station, // animate_tile_proc
5166 TileLoop_Station, // tile_loop_proc
5167 ChangeTileOwner_Station, // change_tile_owner_proc
5168 nullptr, // add_produced_cargo_proc
5169 VehicleEnter_Station, // vehicle_enter_tile_proc
5170 GetFoundation_Station, // get_foundation_proc
5171 TerraformTile_Station, // terraform_tile_proc
5172};
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:185
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 SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
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 t)
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
bool IsCargoInClass(CargoType c, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
@ Mail
Mail.
@ Liquid
Liquids (Oil, Water, Rubber)
@ Passengers
Passengers.
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 & 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.
constexpr Timpl & Reset(Tvalue_type value)
Reset the value-th bit.
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.
bool Failed() const
Did this command fail?
Enum-as-bit-set wrapper.
uint GetFlow() const
Get the sum of all flows from this FlowStatMap.
void PassOnFlow(StationID origin, StationID via, uint amount)
Pass on some flow, remembering it as invalid, for later subtraction from locally consumed flow.
void AddFlow(StationID origin, StationID via, uint amount)
Add some flow from "origin", going via "via".
uint GetFlowFrom(StationID from) const
Get the sum of flows from a specific station from this FlowStatMap.
void FinalizeLocalConsumption(StationID self)
Subtract invalid flows from locally consumed flow.
void ReleaseFlows(StationID via)
Release all flows at a station for specific cargo and destination.
StationIDStack DeleteFlows(StationID via)
Delete all flows at a station for specific cargo and destination.
uint GetFlowFromVia(StationID from, StationID via) const
Get the flow from a specific station via a specific other station.
uint GetFlowVia(StationID via) const
Get the sum of flows via a specific station from this FlowStatMap.
void RestrictFlows(StationID via)
Restrict all flows at a station for specific cargo and destination.
Flow statistics telling how much flow should be sent along a link.
static const SharesMap empty_sharesmap
Static instance of FlowStat::SharesMap.
void ScaleToMonthly(uint runtime)
Scale all shares from link graph's runtime to monthly values.
void RestrictShare(StationID st)
Restrict a flow by moving it to the end of the map and decreasing the amount of unrestricted flow.
uint GetShare(StationID st) const
Get flow for a station.
StationID GetVia() const
Get a station a package can be routed to.
const SharesMap * GetShares() const
Get the actual shares as a const pointer so that they can be iterated over.
SharesMap shares
Shares of flow to be sent via specified station (or consumed locally).
void ReleaseShare(StationID st)
Release ("unrestrict") a flow by moving it to the begin of the map and increasing the amount of unres...
void ChangeShare(StationID st, int flow)
Change share for specified station.
void AppendShare(StationID st, uint flow, bool restricted=false)
Add some flow to the end of the shares map.
void Invalidate()
Reduce all flows to minimum capacity so that they don't get in the way of link usage statistics too m...
uint unrestricted
Limit for unrestricted shares.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
void Insert(const T &element)
Insert a single element in the tree.
Definition kdtree.hpp:398
void Remove(const T &element)
Remove a single element from the tree, if it exists.
Definition kdtree.hpp:417
static LinkGraphSchedule instance
Static instance of LinkGraphSchedule.
void Unqueue(LinkGraph *lg)
Remove a link graph from the execution queue.
void Queue(LinkGraph *lg)
Queue a link graph for execution.
A connected component of a link graph.
Definition linkgraph.h:37
void Merge(LinkGraph *other)
Merge a link graph with another one.
Definition linkgraph.cpp:90
TimerGameEconomy::Date LastCompression() const
Get date of last compression.
Definition linkgraph.h:236
NodeID AddNode(const Station *st)
Add a node to the component and create empty edges associated with it.
NodeID Size() const
Get the current size of the component.
Definition linkgraph.h:230
static const uint MIN_TIMEOUT_DISTANCE
Minimum effective distance for timeout calculation.
Definition linkgraph.h:170
static constexpr TimerGameEconomy::Date STALE_LINK_DEPOT_TIMEOUT
Number of days before deleting links served only by vehicles stopped in depot.
Definition linkgraph.h:173
static constexpr TimerGameEconomy::Date COMPRESSION_INTERVAL
Minimum number of days between subsequent compressions of a LG.
Definition linkgraph.h:176
static void Run(Vehicle *v, bool allow_merge=true, bool is_full_loading=false)
Refresh all links the given vehicle will visit.
Definition refresh.cpp:26
size_t MapSize() const
Count the number of ranges with equal keys in this MultiMap.
Definition multimap.hpp:291
Struct containing information relating to NewGRF classes for stations and airports.
StringID name
Name of this class.
static NewGRFClass * Get(Tindex class_index)
Get a particular class.
uint GetSpecCount() const
Get the number of allocated specs within the class.
static uint GetClassCount()
Get the number of allocated classes.
const Tspec * GetSpec(uint index) const
Get a spec from the class at a given index.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:118
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:128
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:222
struct RailTypeInfo::@24 strings
Strings associated with the rail type.
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::@21 base_sprites
Struct containing the main sprites.
A sort-of mixin that adds 'at(pos)' and 'operator[](pos)' implementations for 'ConvertibleThroughBase...
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:133
StringID name
Name of this rail type.
Definition road.h:94
struct RoadTypeInfo::@27 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.
uint Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
Routes packets with station "avoid" as next hop to a different place.
uint AvailableCount() const
Returns sum of cargo still available for loading at the sation.
void Append(CargoPacket *cp, StationID next)
Appends the given cargo packet to the range of packets with the same next station.
uint Truncate(uint max_move=UINT_MAX, StationCargoAmountMap *cargo_per_source=nullptr)
Truncates where each destination loses roughly the same percentage of its cargo.
const StationList & GetStations()
Run a tile loop to find stations around a tile, on demand.
StationList stations
List of stations nearby.
static constexpr TimerGameTick::Ticks STATION_LINKGRAPH_TICKS
Cycle duration for cleaning dead links.
static constexpr TimerGameTick::Ticks STATION_ACCEPTANCE_TICKS
Cycle duration for updating station acceptance.
static constexpr TimerGameTick::Ticks STATION_RATING_TICKS
Cycle duration for updating station rating.
Base class for tile iterators.
Wrapper class to abstract away the way the tiles are stored.
Definition map_func.h:25
static Date date
Current date in days (day counter).
static constexpr TimerGame< struct Economy >::Date INVALID_DATE
Representation of an invalid date.
static Date date
Current date in days (day counter).
static TickCounter counter
Monotonic counter, in ticks, since start of game.
uint StoredCount() const
Returns sum of cargo on board the vehicle (ie not only reserved).
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:418
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.
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.
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_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:568
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
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:989
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:246
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
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.
EdgeUpdateMode
Special modes for updating links.
@ EUM_INCREASE
Increase capacity.
@ DT_MANUAL
Manual distribution. No link graph calculations are run.
bool CircularTileSearch(TileIndex *tile, uint size, TestTileOnSearchProc proc, void *user_data)
Function performing a search around a center tile and going outward, thus in circle.
Definition map.cpp:243
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 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.
constexpr void Swap(T &a, T &b)
Type safe swap operation.
uint8_t StationGfx
Copy from station_map.h.
StationGfx GetTranslatedAirportTileID(StationGfx gfx)
Do airporttile gfx ID translation for NewGRFs.
NewGRF handling of airport tiles.
@ AAT_BUILT
Triggered when the airport is built (for all tiles at the same time).
@ AAT_STATION_NEW_CARGO
Triggered when new cargo arrives at the station (for all tiles at the same time).
@ AAT_TILELOOP
Triggered in the periodic tile loop.
@ AAT_STATION_250_TICKS
Triggered every 250 ticks (for all tiles at the same time).
@ SAT_NEW_CARGO
Trigger station on new cargo arrival.
@ SAT_BUILT
Trigger tile when built.
@ SAT_250_TICKS
Trigger station every 250 ticks.
static const uint8_t ANIM_STATUS_NO_ANIMATION
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 WatchedCargoCallback(TileIndex tile, CargoTypes trigger_cargoes)
Run watched cargo accepted callback for a house.
Functions related to NewGRF houses.
SpriteID GetCustomRailSprite(const RailTypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of rail types.
NewGRF definitions and structures for road stops.
RoadStopDrawMode
Different draw modes to disallow rendering of some parts of the stop or road.
@ Overlay
Drive-through stops: Draw the road overlay, e.g. pavement.
@ WaypGround
Waypoints: Draw the sprite layout ground tile (on top of the road)
@ Road
Bay stops: Draw the road itself.
bool IsWaypointClass(const RoadStopClass &cls)
Test if a RoadStopClass is the waypoint class.
@ RSRT_NEW_CARGO
Trigger roadstop on arrival of new cargo.
RoadStopClassID
@ ROADSTOPTYPE_FREIGHT
This RoadStop is for freight (truck) stops.
@ ROADSTOPTYPE_ALL
This RoadStop is for both types of station road stops.
@ ROADSTOPTYPE_PASSENGER
This RoadStop is for passenger (bus) stops.
@ NoCatenary
Do not show catenary.
@ DriveThroughOnly
Stop is drive-through only.
@ DrawModeRegister
Read draw mode from register 0x100.
SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of road types.
uint32_t GetRegister(uint i)
Gets the value of a so-called newgrf "register".
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.
void TriggerStationRandomisation(Station *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger station randomisation.
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.
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.
@ SRT_NEW_CARGO
Trigger station on new cargo arrival.
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:330
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:206
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:368
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:328
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:27
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:34
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:153
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:235
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:220
@ 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:245
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:190
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:262
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.
RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance)
Returns the RoadBits on an arbitrary tile Special behaviour:
Definition road_map.cpp:33
void SetRoadOwner(Tile t, RoadTramType rtt, Owner o)
Set the owner of a specific road type.
Definition road_map.h:251
static debug_inline bool IsNormalRoadTile(Tile t)
Return whether a tile is a normal road tile.
Definition road_map.h:74
RoadBits GetRoadBits(Tile t, RoadTramType rtt)
Get the present road bits for a specific road type.
Definition road_map.h:128
bool MayHaveRoad(Tile t)
Test whether a tile can have road/tram types.
Definition road_map.h:33
DisallowedRoadDirections GetDisallowedRoadDirections(Tile t)
Gets the disallowed directions.
Definition road_map.h:301
bool HasTileRoadType(Tile t, RoadTramType rtt)
Check if a tile has a road or a tram road type.
Definition road_map.h:211
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:635
RoadBits GetAllRoadBits(Tile tile)
Get all set RoadBits on the given tile.
Definition road_map.h:141
Owner GetRoadOwner(Tile t, RoadTramType rtt)
Get the owner of a specific road type.
Definition road_map.h:234
Roadside
The possible road side decorations.
Definition road_map.h:477
@ ROADSIDE_PAVED
Road with paved sidewalks.
Definition road_map.h:480
@ ROADSIDE_BARREN
Road on barren land.
Definition road_map.h:478
@ ROADSIDE_GRASS
Road on grass.
Definition road_map.h:479
RoadBits
Enumeration for the road parts on a tile.
Definition road_type.h:52
@ ROAD_NONE
No road-part is build.
Definition road_type.h:53
@ ROAD_Y
Full road along the y-axis (north-west + south-east)
Definition road_type.h:59
@ ROAD_X
Full road along the x-axis (south-west + north-east)
Definition road_type.h:58
RoadType
The different roadtypes we support.
Definition road_type.h:25
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:30
@ DRD_NONE
None of the directions are disallowed.
Definition road_type.h:74
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:58
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:57
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:646
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:589
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
#define M(x)
Helper for creating a bitset of slopes.
Definition slope_type.h:84
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:1609
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1549
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1539
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
Definition sprites.h:1552
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.
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 CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlags flags, uint 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.
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.
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.
void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateMode mode)
Increase capacity for a link stat given by station cargo and next hop.
CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
Find a nearby waypoint that joins this waypoint.
CommandCost CmdBuildDock(DoCommandFlags flags, TileIndex tile, StationID station_to_join, bool adjacent)
Build a dock/haven.
static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCommandFlags flags)
Checks if an airport can be built at the given location and clear the area.
CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index=-1)
Remove a road waypoint.
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
Look for a station owned by the given company around the given tile area.
CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
Remove bus or truck stops.
bool HasStationInUse(StationID station, bool include_company, CompanyID company)
Tests whether the company's vehicles have this station in orders.
static int CountMapSquareAround(TileIndex tile, CMSAMatcher cmp)
Counts the numbers of tiles matching a specific type in the area around.
CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
Build a bus or truck stop.
static StationSpec::TileFlags GetStationTileFlags(StationGfx gfx, const StationSpec *statspec)
Get station tile flags for the given StationGfx.
const DrawTileSprites * GetStationTileLayout(StationType st, uint8_t gfx)
Get station tile layout for a station type and its station gfx.
CommandCost CmdRemoveFromRoadWaypoint(DoCommandFlags flags, TileIndex start, TileIndex end)
Remove road waypoints.
void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const StationSpec *statspec)
Create the station layout for the given number of tracks and platform length.
void RerouteCargo(Station *st, CargoType c, StationID avoid, StationID avoid2)
Reroute cargo of type c at station st or in any vehicles unloading there.
CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags)
Clear a single tile of a station.
static bool FindNearIndustryName(TileIndex tile, void *user_data)
Find a station action 0 property 24 station name, or reduce the free_names if needed.
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.
CommandCost CheckBuildableTile(TileIndex tile, uint 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.
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 IntervalTimer< TimerGameEconomy > _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto) { for(Station *st :Station::Iterate()) { for(GoodsEntry &ge :st->goods) { SB(ge.status, GoodsEntry::GES_LAST_MONTH, 1, GB(ge.status, GoodsEntry::GES_CURRENT_MONTH, 1));ClrBit(ge.status, GoodsEntry::GES_CURRENT_MONTH);} } })
Economy monthly loop for stations.
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 FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
Find a nearby station that joins this station.
Town * AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
Finds the town nearest to given airport.
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
Calculates cost of new road stops within the area.
static bool StationHandleBigTick(BaseStation *st)
This function is called for each station once every 250 ticks.
static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
Add news item for when a station changes which cargoes it accepts.
CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailType rt, Axis axis, uint8_t numtracks, uint8_t plat_len, StationClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
Build rail station.
static void DeleteStationIfEmpty(BaseStation *st)
This is called right after a station was deleted.
Command definitions related to stations.
Functions related to stations.
void ShowStationViewWindow(StationID station)
Opens StationViewWindow for given station.
Declarations for accessing the k-d tree of stations.
void ForAllStationsRadius(TileIndex center, uint radius, Func func)
Call a function on all stations whose sign is within a radius of a center tile.
Sprites to use and how to display them for station tiles.
void MakeAirport(Tile t, Owner o, StationID sid, uint8_t section, WaterClass wc)
Make the given tile an airport tile.
StationType GetStationType(Tile t)
Get the station type of this tile.
Definition station_map.h:44
StationGfx GetStationGfx(Tile t)
Get the station graphics of this tile.
Definition station_map.h:68
void SetStationGfx(Tile t, StationGfx gfx)
Set the station graphics of this tile.
Definition station_map.h:80
void MakeRoadStop(Tile t, Owner o, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, DiagDirection d)
Make the given tile a roadstop tile.
void SetCustomStationSpecIndex(Tile t, uint8_t specindex)
Set the custom station spec for this tile.
void SetStationTileHaveWires(Tile t, bool b)
Set the catenary wires state of the rail station.
bool IsAirport(Tile t)
Is this station tile an airport?
bool IsBayRoadStopTile(Tile t)
Is tile t a bay (non-drive through) road stop station?
uint GetCustomRoadStopSpecIndex(Tile t)
Get the custom road stop spec for this tile.
static const int GFX_DOCK_BASE_WATER_PART
The offset for the water parts.
Definition station_map.h:35
bool IsBuoy(Tile t)
Is tile t a buoy tile?
bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile)
Check if a tile is a valid continuation to a railstation tile.
bool IsRoadWaypoint(Tile t)
Is the station at t a road waypoint?
void MakeDriveThroughRoadStop(Tile t, Owner station, Owner road, Owner tram, StationID sid, StationType rst, RoadType road_rt, RoadType tram_rt, Axis a)
Make the given tile a drivethrough roadstop tile.
bool IsDriveThroughStopTile(Tile t)
Is tile t a drive through road stop station or waypoint?
static Roadside GetRoadWaypointRoadside(Tile tile)
Get the decorations of a road waypoint.
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
Track GetRailStationTrack(Tile t)
Get the rail track of a rail station tile.
static void ToggleRoadWaypointOnSnowOrDesert(Tile t)
Toggle the snow/desert state of a road waypoint tile.
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
bool HasStationTileRail(Tile t)
Has this station tile a rail? In other words, is this station tile a rail station or rail waypoint?
StationGfx GetAirportGfx(Tile t)
Get the station graphics of this airport tile.
uint GetCustomStationSpecIndex(Tile t)
Get the custom station spec for this tile.
bool IsRailWaypoint(Tile t)
Is this station tile a rail waypoint?
bool IsRailStation(Tile t)
Is this station tile a rail station?
Definition station_map.h:92
bool IsDockTile(Tile t)
Is tile t a dock tile?
static void SetRoadWaypointRoadside(Tile tile, Roadside s)
Set the decorations of a road waypoint.
void SetStationTileRandomBits(Tile t, uint8_t random_bits)
Set the random bits for a station tile.
bool IsAnyRoadStop(Tile t)
Is the station at t a road station?
void MakeOilrig(Tile t, StationID sid, WaterClass wc)
Make the given tile an oilrig tile.
DiagDirection GetDockDirection(Tile t)
Get the direction of a dock.
Axis GetRailStationAxis(Tile t)
Get the rail direction of a rail station.
static bool IsRoadWaypointOnSnowOrDesert(Tile t)
Check if a road waypoint tile has snow/desert.
bool IsRoadWaypointTile(Tile t)
Is this tile a station tile and a road waypoint?
bool IsStationTileBlocked(Tile t)
Is tile t a blocked tile?
bool IsTruckStop(Tile t)
Is the station at t a truck stop?
bool IsStationRoadStop(Tile t)
Is the station at t a road station?
bool IsCustomStationSpecIndex(Tile t)
Is there a custom rail station spec on this tile?
bool HasStationRail(Tile t)
Has this station tile a rail? In other words, is this station tile a rail station or rail waypoint?
Axis GetDriveThroughStopAxis(Tile t)
Gets the axis of the drive through stop.
void SetStationTileHavePylons(Tile t, bool b)
Set the catenary pylon state of the rail station.
bool IsOilRig(Tile t)
Is tile t part of an oilrig?
bool IsBuoyTile(Tile t)
Is tile t a buoy tile?
void MakeRailStation(Tile t, Owner o, StationID sid, Axis a, uint8_t section, RailType rt)
Make the given tile a rail station tile.
bool HasStationReservation(Tile t)
Get the reservation state of the rail station.
void MakeDock(Tile t, Owner o, StationID sid, DiagDirection d, WaterClass wc)
Make the given tile a dock tile.
DiagDirection GetBayRoadStopDir(Tile t)
Gets the direction the bay road stop entrance points towards.
bool IsDock(Tile t)
Is tile t a dock tile?
bool IsAnyRoadStopTile(Tile t)
Is tile t a road stop station?
void SetStationTileBlocked(Tile t, bool b)
Set the blocked state of the rail station.
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition station_map.h:56
void SetCustomRoadStopSpecIndex(Tile t, uint8_t specindex)
Set the custom road stop spec for this tile.
RoadStopType
Types of RoadStops.
@ Bus
A standard stop for buses.
@ Truck
A standard stop for trucks.
@ End
End of valid types.
@ Dock
Station with a dock.
@ Waypoint
Station is a waypoint.
@ TruckStop
Station with truck stops.
@ Train
Station with train station.
@ Airport
Station with an airport.
@ BusStop
Station with bus stops.
StationType
Station types.
std::set< Station *, StationCompare > StationList
List of stations.
static const uint MAX_LENGTH_STATION_NAME_CHARS
The maximum length of a station name in characters including '\0'.
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(const char *s)
Get the length of an UTF-8 encoded string in number of characters and thus not the number of bytes th...
Definition string.cpp:377
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:338
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:426
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
std::vector< AirportTileLayout > layouts
List of layouts composing the airport.
struct GRFFileProps grf_prop
Properties related to the grf file.
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.
StringID name
Tile Subname string, land information on this tile will give you "AirportName (TileSubname)".
AnimationInfo animation
Information about the animation.
static const AirportTileSpec * GetByTile(TileIndex tile)
Retrieve airport tile spec for the given airport tile.
GRFFileProps grf_prop
properties related the the grf file
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.
uint16_t triggers
The triggers that trigger animation.
uint8_t status
Status; 0: no looping, 1: looping, 0xFF: no animation.
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.
uint8_t cached_anim_triggers
NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
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.
uint8_t cached_roadstop_anim_triggers
NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing sh...
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 ...
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
bool IsInUse() const
Check whether the base station currently is in use; in use means that it is not scheduled for deletio...
Town * town
The town this station is associated with.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
virtual bool TileBelongsToRailStation(TileIndex tile) const =0
Check whether a specific tile belongs to this station.
TrackedViewportSign sign
NOSAVE: Dimensions of sign.
TimerGameCalendar::Date build_date
Date of construction.
std::string name
Custom name.
VehicleType type
Type of vehicle.
Class for storing amounts of cargo.
Definition cargo_type.h:113
Container for cargo from the same location and time.
Definition cargopacket.h:41
Specification of a cargo type.
Definition cargotype.h:74
CargoClasses classes
Classes of this cargo type.
Definition cargotype.h:81
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:108
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat station_rating
Fix station ratings at 100%.
Definition cheat_type.h:35
GUISettings gui
settings related to the GUI
uint32_t station
Count of company owned station tiles.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
uint32_t water
Count of company owned track bits for canals.
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
bool build_on_slopes
allow building on slopes
bool road_stop_on_town_road
allow building of drive-through road stops on town owned roads
bool road_stop_on_competitor_road
allow building of drive-through road stops on roads owned by competitors
uint8_t town_council_tolerance
minimum required town ratings to be allowed to demolish stuff
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:61
std::span< const DrawTileSeqStruct > seq
Child sprites,.
Definition sprite.h:62
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:46
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:47
bool station_noise_level
build new airports when the town noise level is still within accepted limits
const struct SpriteGroup * GetSpriteGroup(size_t index=0) const
Get the SpriteGroup at the specified index.
Information about GRF, used in the game and (part of it) in savegames.
const char * 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.
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.
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.
uint8_t status
Status of this cargo, see GoodsEntryStatus.
@ GES_ACCEPTANCE
Set when the station accepts the cargo currently for final deliveries.
@ GES_LAST_MONTH
Set when cargo was delivered for final delivery last month.
@ GES_RATING
This indicates whether a cargo has a rating at the station.
@ GES_CURRENT_MONTH
Set when cargo was delivered for final delivery this month.
@ GES_ACCEPTED_BIGTICK
Set when cargo was delivered for final delivery during the current STATION_ACCEPTANCE_TICKS interval.
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.
StringID name
Displayed name of the industry.
GRFFileProps grf_prop
properties related to the grf file
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:63
IndustryType type
type of industry.
Definition industry.h:99
ProducedCargoes produced
produced cargo slots
Definition industry.h:94
Owner owner
owner of the industry. Which SHOULD always be (imho) OWNER_NONE
Definition industry.h:100
static Industry * GetByTile(TileIndex tile)
Get the industry of the given tile.
Definition industry.h:235
TileArea location
Location of the industry.
Definition industry.h:91
Station * neutral_station
Associated neutral station.
Definition industry.h:93
StationList stations_near
NOSAVE: List of nearby stations.
Definition industry.h:107
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.
uint32_t PrepareLayout(uint32_t orig_offset, uint32_t newgrf_ground_offset, uint32_t newgrf_offset, uint constr_stage, bool separate_ground) const
Prepares a sprite layout before resolving action-1-2-3 chains.
void ProcessRegisters(uint8_t resolved_var10, uint32_t resolved_sprite, bool separate_ground) const
Evaluates the register modifiers and integrates them into the preprocessed sprite layout.
bool NeedsPreprocessing() const
Tests whether this spritelayout needs preprocessing by PrepareLayout() and ProcessRegisters(),...
std::span< DrawTileSeqStruct > GetLayout(PalSpriteID *ground) const
Returns the result spritelayout after preprocessing.
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:258
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:70
Order * next
Pointer to next order. If nullptr, end of list.
Definition order_base.h:59
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
Represents the covered area of e.g.
uint16_t w
The width of the area.
void Add(TileIndex to_add)
Add a single tile to a tile area; enlarge if needed.
Definition tilearea.cpp:43
void Clear()
Clears the 'tile area', i.e.
TileIndex tile
The base tile of the area.
uint16_t h
The height of the area.
OrthogonalTileArea & Expand(int rad)
Expand a tile area by rad tiles in each direction, keeping within map bounds.
Definition tilearea.cpp:123
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed)
Definition gfx_type.h:24
Coordinates of a point in 2D.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
static size_t GetNumItems()
Returns number of valid items in the pool.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(auto index)
Returns Titem with given index.
Road stop resolver.
Road stop specification.
Money GetBuildCost(Price category) const
Get the cost for building a road stop of this type.
VariableGRFFileProps grf_prop
Properties related the the grf file.
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.
bool Enter(RoadVehicle *rv)
Enter the road stop.
Definition roadstop.cpp:231
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp:264
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:129
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.
virtual const SpriteGroup * Resolve(ResolverObject &object) const
Base sprite group resolver.
Information to handle station action 0 property 24 correctly.
std::bitset< NUM_INDUSTRYTYPES > indtypes
Bit set indicating when an industry type has been found.
uint32_t free_names
Current bitset of free names (we can remove names).
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.
VariableGRFFileProps grf_prop
Properties related the the grf file.
StringID name
Name of this station.
uint8_t disallowed_platforms
Bitmask of number of platforms available for the station.
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:465
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:229
Tile description for the 'land area information' tool.
Definition tile_cmd.h:52
uint16_t rail_speed
Speed limit of rail (bridges and track)
Definition tile_cmd.h:65
StringID station_name
Type of station within the class.
Definition tile_cmd.h:59
StringID str
Description of the tile.
Definition tile_cmd.h:53
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:57
std::array< Owner, 4 > owner
Name of the owner(s)
Definition tile_cmd.h:55
StringID airport_class
Name of the airport class.
Definition tile_cmd.h:60
StringID airport_name
Name of the airport.
Definition tile_cmd.h:61
uint16_t tram_speed
Speed limit of tram (bridges and track)
Definition tile_cmd.h:69
StringID roadtype
Type of road on the tile.
Definition tile_cmd.h:66
StringID tramtype
Type of tram on the tile.
Definition tile_cmd.h:68
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:64
uint16_t road_speed
Speed limit of road (bridges and track)
Definition tile_cmd.h:67
const char * grf
newGRF used for the tile contents
Definition tile_cmd.h:63
std::array< StringID, 4 > owner_type
Type of each owner.
Definition tile_cmd.h:56
StringID airport_tile_name
Name of the airport tile.
Definition tile_cmd.h:62
StringID station_class
Class of station.
Definition tile_cmd.h:58
A pair-construct of a TileIndexDiff.
Definition map_type.h:31
Tile information, used while rendering the tile.
Definition tile_cmd.h:43
int z
Height.
Definition tile_cmd.h:48
int x
X position of the tile in unit coordinates.
Definition tile_cmd.h:44
Slope tileh
Slope of the tile.
Definition tile_cmd.h:46
TileIndex tile
Tile index.
Definition tile_cmd.h:47
int y
Y position of the tile in unit coordinates.
Definition tile_cmd.h:45
Action 2 sprite layout for houses, industry tiles, objects and airport tiles.
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:159
Town data structure.
Definition town.h:52
CompanyMask statues
which companies have a statue?
Definition town.h:68
TileIndex xy
town center tile
Definition town.h:53
uint16_t noise_reached
level of noise that all the airports are generating
Definition town.h:66
uint16_t MaxTownNoise() const
Calculate the max town noise.
Definition town.h:123
CompanyID exclusivity
which company has exclusivity
Definition town.h:73
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:74
void UpdatePosition(int center, int top, std::string_view str, std::string_view str_small={})
Update the position of the viewport sign.
bool kdtree_valid
Are the sign data valid for use with the _viewport_sign_kdtree?
'Train' is either a loco or a wagon.
Definition train.h:90
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.
VehicleEnterTileStatus
The returned bits of VehicleEnterTile.
Definition tile_cmd.h:21
@ VETSB_CANNOT_ENTER
The vehicle cannot enter the tile.
Definition tile_cmd.h:38
@ VETS_STATION_ID_OFFSET
Shift the VehicleEnterTileStatus this many bits to the right to get the station ID when VETS_ENTERED_...
Definition tile_cmd.h:31
@ VETSB_ENTERED_STATION
The vehicle entered a station.
Definition tile_cmd.h:36
@ VETSB_CONTINUE
Bit sets of the above specified bits.
Definition tile_cmd.h:35
bool IsTileFlat(TileIndex tile, int *h)
Check if a given tile is flat.
Definition tile_map.cpp:95
std::tuple< Slope, int > GetTileSlopeZ(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.cpp:55
int GetTileMaxZ(TileIndex t)
Get top height of the tile inside the map.
Definition tile_map.cpp:136
int GetTileZ(TileIndex tile)
Get bottom height of the tile.
Definition tile_map.cpp:116
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
void SetTileOwner(Tile tile, Owner owner)
Sets the owner of a tile.
Definition tile_map.h:198
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
uint8_t GetAnimationFrame(Tile t)
Get the current animation frame.
Definition tile_map.h:250
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
TropicZone GetTropicZone(Tile tile)
Get the tropic zone.
Definition tile_map.h:238
void SetAnimationFrame(Tile t, uint8_t frame)
Set a new animation frame.
Definition tile_map.h:262
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
static const uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:78
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:87
@ MP_TREES
Tile got trees.
Definition tile_type.h:52
@ MP_ROAD
A tile with road (or tram tracks)
Definition tile_type.h:50
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
@ MP_HOUSE
A house by a town.
Definition tile_type.h:51
@ MP_WATER
Water tile.
Definition tile_type.h:54
@ MP_INDUSTRY
Part of an industry.
Definition tile_type.h:56
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
Definition of Interval and OneShot timers.
Definition of the game-calendar-timer.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.
Base of the town class.
HouseZonesBits GetTownRadiusGroup(const Town *t, TileIndex tile)
Returns the bit corresponding to the town zone of the specified tile.
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.
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:67
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:99
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:543
void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
Find a vehicle from a specific location.
Definition vehicle.cpp:502
@ 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:601
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:769
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:673
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:779
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:589
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:1143
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3132
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:3224
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:3119
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3106
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:3241
@ 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.