OpenTTD Source 20241222-master-gc72542431a
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_internal.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 == INVALID_STATION) {
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 == INVALID_STATION) ? 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 */
179 if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_EXTRACTIVE) == 0) return false;
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 (IsValidCargoID(p.cargo) &&
185 (CargoSpec::Get(p.cargo)->classes & (CC_LIQUID | CC_PASSENGERS | CC_MAIL)) == 0) {
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 {
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 == LT_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 STATION_RAIL:
415 *ta = this->train_station;
416 return;
417
418 case STATION_AIRPORT:
419 *ta = this->airport;
420 return;
421
422 case STATION_TRUCK:
423 *ta = this->truck_station;
424 return;
425
426 case STATION_BUS:
427 *ta = this->bus_station;
428 return;
429
430 case STATION_DOCK:
431 case STATION_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 & FACIL_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 SetDParam(0, this->index);
452 SetDParam(1, this->facilities);
453 this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION, STR_VIEWPORT_STATION_TINY);
454
455 _viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeStation(this->index));
456
458}
459
465{
466 if (this->xy == new_xy) return;
467
468 _station_kdtree.Remove(this->index);
469
470 this->BaseStation::MoveSign(new_xy);
471
472 _station_kdtree.Insert(this->index);
473}
474
477{
478 for (BaseStation *st : BaseStation::Iterate()) {
479 st->UpdateVirtCoord();
480 }
481}
482
483void BaseStation::FillCachedName() const
484{
485 auto tmp_params = MakeParameters(this->index);
486 this->cached_name = GetStringWithArgs(Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params);
487}
488
489void ClearAllStationCachedNames()
490{
491 for (BaseStation *st : BaseStation::Iterate()) {
492 st->cached_name.clear();
493 }
494}
495
501CargoTypes GetAcceptanceMask(const Station *st)
502{
503 CargoTypes mask = 0;
504
505 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
506 if (HasBit(it->status, GoodsEntry::GES_ACCEPTANCE)) SetBit(mask, std::distance(std::begin(st->goods), it));
507 }
508 return mask;
509}
510
516CargoTypes GetEmptyMask(const Station *st)
517{
518 CargoTypes mask = 0;
519
520 for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
521 if (it->cargo.TotalCount() == 0) SetBit(mask, std::distance(std::begin(st->goods), it));
522 }
523 return mask;
524}
525
532static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
533{
534 SetDParam(0, st->index);
535 SetDParam(1, cargoes);
536 StringID msg = reject ? STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_LIST : STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST;
538}
539
547CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
548{
549 CargoArray produced{};
550 std::set<IndustryID> industries;
551 TileArea ta = TileArea(north_tile, w, h).Expand(rad);
552
553 /* Loop over all tiles to get the produced cargo of
554 * everything except industries */
555 for (TileIndex tile : ta) {
556 if (IsTileType(tile, MP_INDUSTRY)) industries.insert(GetIndustryIndex(tile));
557 AddProducedCargo(tile, produced);
558 }
559
560 /* Loop over the seen industries. They produce cargo for
561 * anything that is within 'rad' of any one of their tiles.
562 */
563 for (IndustryID industry : industries) {
564 const Industry *i = Industry::Get(industry);
565 /* Skip industry with neutral station */
566 if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) continue;
567
568 for (const auto &p : i->produced) {
569 if (IsValidCargoID(p.cargo)) produced[p.cargo]++;
570 }
571 }
572
573 return produced;
574}
575
585CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted)
586{
587 CargoArray acceptance{};
588 if (always_accepted != nullptr) *always_accepted = 0;
589
590 TileArea ta = TileArea(center_tile, w, h).Expand(rad);
591
592 for (TileIndex tile : ta) {
593 /* Ignore industry if it has a neutral station. */
595
596 AddAcceptedCargo(tile, acceptance, always_accepted);
597 }
598
599 return acceptance;
600}
601
607static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
608{
609 CargoArray acceptance{};
610 if (always_accepted != nullptr) *always_accepted = 0;
611
613 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
614 AddAcceptedCargo(tile, acceptance, always_accepted);
615 }
616
617 return acceptance;
618}
619
625void UpdateStationAcceptance(Station *st, bool show_msg)
626{
627 /* old accepted goods types */
628 CargoTypes old_acc = GetAcceptanceMask(st);
629
630 /* And retrieve the acceptance. */
631 CargoArray acceptance{};
632 if (!st->rect.IsEmpty()) {
633 acceptance = GetAcceptanceAroundStation(st, &st->always_accepted);
634 }
635
636 /* Adjust in case our station only accepts fewer kinds of goods */
637 for (CargoID i = 0; i < NUM_CARGO; i++) {
638 uint amt = acceptance[i];
639
640 /* Make sure the station can accept the goods type. */
641 bool is_passengers = IsCargoInClass(i, CC_PASSENGERS);
642 if ((!is_passengers && !(st->facilities & ~FACIL_BUS_STOP)) ||
643 (is_passengers && !(st->facilities & ~FACIL_TRUCK_STOP))) {
644 amt = 0;
645 }
646
647 GoodsEntry &ge = st->goods[i];
648 SB(ge.status, GoodsEntry::GES_ACCEPTANCE, 1, amt >= 8);
650 (*LinkGraph::Get(ge.link_graph))[ge.node].SetDemand(amt / 8);
651 }
652 }
653
654 /* Only show a message in case the acceptance was actually changed. */
655 CargoTypes new_acc = GetAcceptanceMask(st);
656 if (old_acc == new_acc) return;
657
658 /* show a message to report that the acceptance was changed? */
659 if (show_msg && st->owner == _local_company && st->IsInUse()) {
660 /* Combine old and new masks to get changes */
661 CargoTypes accepts = new_acc & ~old_acc;
662 CargoTypes rejects = ~new_acc & old_acc;
663
664 /* Show news message if there are any changes */
665 if (accepts != 0) ShowRejectOrAcceptNews(st, accepts, false);
666 if (rejects != 0) ShowRejectOrAcceptNews(st, rejects, true);
667 }
668
669 /* redraw the station view since acceptance changed */
671}
672
673static void UpdateStationSignCoord(BaseStation *st)
674{
675 const StationRect *r = &st->rect;
676
677 if (r->IsEmpty()) return; // no tiles belong to this station
678
679 /* clamp sign coord to be inside the station rect */
680 TileIndex new_xy = TileXY(ClampU(TileX(st->xy), r->left, r->right), ClampU(TileY(st->xy), r->top, r->bottom));
681 st->MoveSign(new_xy);
682
683 if (!Station::IsExpected(st)) return;
684 Station *full_station = Station::From(st);
685 for (const GoodsEntry &ge : full_station->goods) {
686 LinkGraphID lg = ge.link_graph;
687 if (!LinkGraph::IsValidID(lg)) continue;
688 (*LinkGraph::Get(lg))[ge.node].UpdateLocation(st->xy);
689 }
690}
691
701static CommandCost BuildStationPart(Station **st, DoCommandFlag flags, bool reuse, TileArea area, StationNaming name_class)
702{
703 /* Find a deleted station close to us */
704 if (*st == nullptr && reuse) *st = GetClosestDeletedStation(area.tile);
705
706 if (*st != nullptr) {
707 if ((*st)->owner != _current_company) {
708 return CommandCost(CMD_ERROR);
709 }
710
711 CommandCost ret = (*st)->rect.BeforeAddRect(area.tile, area.w, area.h, StationRect::ADD_TEST);
712 if (ret.Failed()) return ret;
713 } else {
714 /* allocate and initialize new station */
715 if (!Station::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_STATIONS_LOADING);
716
717 if (flags & DC_EXEC) {
718 *st = new Station(area.tile);
719 _station_kdtree.Insert((*st)->index);
720
721 (*st)->town = ClosestTownFromTile(area.tile, UINT_MAX);
722 (*st)->string_id = GenerateStationName(*st, area.tile, name_class);
723
725 SetBit((*st)->town->have_ratings, _current_company);
726 }
727 }
728 }
729 return CommandCost();
730}
731
739{
740 if (!st->IsInUse()) {
741 st->delete_ctr = 0;
743 }
744 /* station remains but it probably lost some parts - station sign should stay in the station boundaries */
745 UpdateStationSignCoord(st);
746}
747
754{
755 this->UpdateVirtCoord();
757
758 if (adding) {
759 this->RecomputeCatchment();
760 MarkCatchmentTilesDirty();
762 } else {
763 MarkCatchmentTilesDirty();
764 }
765
766 switch (type) {
767 case STATION_RAIL:
769 break;
770 case STATION_AIRPORT:
771 break;
772 case STATION_TRUCK:
773 case STATION_BUS:
775 break;
776 case STATION_DOCK:
778 break;
779 default: NOT_REACHED();
780 }
781
782 if (adding) {
783 UpdateStationAcceptance(this, false);
785 } else {
787 this->RecomputeCatchment();
788 }
789
790}
791
793
803CommandCost CheckBuildableTile(TileIndex tile, uint invalid_dirs, int &allowed_z, bool allow_steep, bool check_bridge = true)
804{
805 if (check_bridge && IsBridgeAbove(tile)) {
806 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
807 }
808
810 if (ret.Failed()) return ret;
811
812 auto [tileh, z] = GetTileSlopeZ(tile);
813
814 /* Prohibit building if
815 * 1) The tile is "steep" (i.e. stretches two height levels).
816 * 2) The tile is non-flat and the build_on_slopes switch is disabled.
817 */
818 if ((!allow_steep && IsSteepSlope(tileh)) ||
820 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
821 }
822
824 int flat_z = z + GetSlopeMaxZ(tileh);
825 if (tileh != SLOPE_FLAT) {
826 /* Forbid building if the tile faces a slope in a invalid direction. */
827 for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) {
828 if (HasBit(invalid_dirs, dir) && !CanBuildDepotByTileh(dir, tileh)) {
829 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
830 }
831 }
832 cost.AddCost(_price[PR_BUILD_FOUNDATION]);
833 }
834
835 /* The level of this tile must be equal to allowed_z. */
836 if (allowed_z < 0) {
837 /* First tile. */
838 allowed_z = flat_z;
839 } else if (allowed_z != flat_z) {
840 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
841 }
842
843 return cost;
844}
845
853{
855 int allowed_z = -1;
856
857 for (; tile_iter != INVALID_TILE; ++tile_iter) {
858 CommandCost ret = CheckBuildableTile(tile_iter, 0, allowed_z, true);
859 if (ret.Failed()) return ret;
860 cost.AddCost(ret);
861
862 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_iter);
863 if (ret.Failed()) return ret;
864 cost.AddCost(ret);
865 }
866
867 return cost;
868}
869
886static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_tile, int &allowed_z, DoCommandFlag 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)
887{
889 uint invalid_dirs = 5 << axis;
890
891 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
892 bool slope_cb = statspec != nullptr && HasBit(statspec->callback_mask, CBM_STATION_SLOPE_CHECK);
893
894 CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false);
895 if (ret.Failed()) return ret;
896 cost.AddCost(ret);
897
898 if (slope_cb) {
899 /* Do slope check if requested. */
900 ret = PerformStationTileSlopeCheck(north_tile, tile_cur, statspec, axis, plat_len, numtracks);
901 if (ret.Failed()) return ret;
902 }
903
904 /* if station is set, then we have special handling to allow building on top of already existing stations.
905 * so station points to INVALID_STATION if we can build on any station.
906 * Or it points to a station if we're only allowed to build on exactly that station. */
907 if (station != nullptr && IsTileType(tile_cur, MP_STATION)) {
908 if (!IsRailStation(tile_cur)) {
909 return ClearTile_Station(tile_cur, DC_AUTO); // get error message
910 } else {
911 StationID st = GetStationIndex(tile_cur);
912 if (*station == INVALID_STATION) {
913 *station = st;
914 } else if (*station != st) {
915 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
916 }
917 }
918 } else {
919 /* If we are building a station with a valid railtype, we may be able to overbuild an existing rail tile. */
920 if (rt != INVALID_RAILTYPE && IsPlainRailTile(tile_cur)) {
921 /* Don't overbuild signals. */
922 if (HasSignals(tile_cur)) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
923
924 /* The current rail type must have power on the to-be-built type (e.g. convert normal rail to electrified rail). */
925 if (HasPowerOnRail(GetRailType(tile_cur), rt)) {
926 TrackBits tracks = GetTrackBits(tile_cur);
927 Track track = RemoveFirstTrack(&tracks);
928 Track expected_track = HasBit(invalid_dirs, DIAGDIR_NE) ? TRACK_X : TRACK_Y;
929
930 /* The existing track must align with the desired station axis. */
931 if (tracks == TRACK_BIT_NONE && track == expected_track) {
932 /* Check for trains having a reservation for this tile. */
933 if (HasBit(GetRailReservationTrackBits(tile_cur), track)) {
934 Train *v = GetTrainForReservation(tile_cur, track);
935 if (v != nullptr) {
936 affected_vehicles.push_back(v);
937 }
938 }
939 ret = Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile_cur, track);
940 if (ret.Failed()) return ret;
941 cost.AddCost(ret);
942 /* With flags & ~DC_EXEC CmdLandscapeClear would fail since the rail still exists */
943 return cost;
944 }
945 }
946 }
947 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
948 if (ret.Failed()) return ret;
949 cost.AddCost(ret);
950 }
951
952 return cost;
953}
954
968CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt)
969{
971
972 CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through);
973 if (ret.Failed()) return ret;
974 cost.AddCost(ret);
975
976 /* If station is set, then we have special handling to allow building on top of already existing stations.
977 * Station points to INVALID_STATION if we can build on any station.
978 * Or it points to a station if we're only allowed to build on exactly that station. */
979 if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
980 if (!IsAnyRoadStop(cur_tile)) {
981 return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
982 } else {
983 if (station_type != GetStationType(cur_tile) ||
984 is_drive_through != IsDriveThroughStopTile(cur_tile)) {
985 return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
986 }
987 /* Drive-through station in the wrong direction. */
988 if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
989 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
990 }
991 StationID st = GetStationIndex(cur_tile);
992 if (*station == INVALID_STATION) {
993 *station = st;
994 } else if (*station != st) {
995 return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
996 }
997 }
998 } else {
999 bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
1000 /* Road bits in the wrong direction. */
1001 RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
1002 if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
1003 /* Someone was pedantic and *NEEDED* three fracking different error messages. */
1004 switch (CountBits(rb)) {
1005 case 1:
1006 return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1007
1008 case 2:
1009 if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
1010 return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
1011
1012 default: // 3 or 4
1013 return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
1014 }
1015 }
1016
1017 if (build_over_road) {
1018 /* There is a road, check if we can build road+tram stop over it. */
1019 RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
1020 if (road_rt != INVALID_ROADTYPE) {
1021 Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
1022 if (road_owner == OWNER_TOWN) {
1023 if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
1025 ret = CheckOwnership(road_owner);
1026 if (ret.Failed()) return ret;
1027 }
1028 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
1029
1030 if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1031
1032 if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
1033 ret = CheckOwnership(road_owner);
1034 if (ret.Failed()) return ret;
1035 }
1036
1037 cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
1038 } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
1039 cost.AddCost(RoadBuildCost(rt) * 2);
1040 }
1041
1042 /* There is a tram, check if we can build road+tram stop over it. */
1043 RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
1044 if (tram_rt != INVALID_ROADTYPE) {
1045 Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
1046 if (Company::IsValidID(tram_owner) &&
1048 /* Disallow breaking end-of-line of someone else
1049 * so trams can still reverse on this tile. */
1050 HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
1051 ret = CheckOwnership(tram_owner);
1052 if (ret.Failed()) return ret;
1053 }
1054 uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
1055
1056 if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
1057
1058 cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
1059 } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
1060 cost.AddCost(RoadBuildCost(rt) * 2);
1061 }
1062 } else if (rt == INVALID_ROADTYPE) {
1063 return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
1064 } else {
1065 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
1066 if (ret.Failed()) return ret;
1067 cost.AddCost(ret);
1068 cost.AddCost(RoadBuildCost(rt) * 2);
1069 }
1070 }
1071
1072 return cost;
1073}
1074
1082{
1083 TileArea cur_ta = st->train_station;
1084
1085 /* determine new size of train station region.. */
1086 int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
1087 int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
1088 new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
1089 new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
1090 new_ta.tile = TileXY(x, y);
1091
1092 /* make sure the final size is not too big. */
1094 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1095 }
1096
1097 return CommandCost();
1098}
1099
1100static inline uint8_t *CreateSingle(uint8_t *layout, int n)
1101{
1102 int i = n;
1103 do *layout++ = 0; while (--i);
1104 layout[((n - 1) >> 1) - n] = 2;
1105 return layout;
1106}
1107
1108static inline uint8_t *CreateMulti(uint8_t *layout, int n, uint8_t b)
1109{
1110 int i = n;
1111 do *layout++ = b; while (--i);
1112 if (n > 4) {
1113 layout[0 - n] = 0;
1114 layout[n - 1 - n] = 0;
1115 }
1116 return layout;
1117}
1118
1126void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const StationSpec *statspec)
1127{
1128 if (statspec != nullptr) {
1129 auto found = statspec->layouts.find(GetStationLayoutKey(numtracks, plat_len));
1130 if (found != std::end(statspec->layouts)) {
1131 /* Custom layout defined, copy to buffer. */
1132 std::copy(std::begin(found->second), std::end(found->second), layout);
1133 return;
1134 }
1135 }
1136
1137 if (plat_len == 1) {
1138 CreateSingle(layout, numtracks);
1139 } else {
1140 if (numtracks & 1) layout = CreateSingle(layout, plat_len);
1141 int n = numtracks >> 1;
1142
1143 while (--n >= 0) {
1144 layout = CreateMulti(layout, plat_len, 4);
1145 layout = CreateMulti(layout, plat_len, 6);
1146 }
1147 }
1148}
1149
1162template <class T, StringID error_message, class F>
1163CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
1164{
1165 assert(*st == nullptr);
1166 bool check_surrounding = true;
1167
1168 if (existing_station != INVALID_STATION) {
1169 if (adjacent && existing_station != station_to_join) {
1170 /* You can't build an adjacent station over the top of one that
1171 * already exists. */
1172 return CommandCost(error_message);
1173 } else {
1174 /* Extend the current station, and don't check whether it will
1175 * be near any other stations. */
1176 T *candidate = T::GetIfValid(existing_station);
1177 if (candidate != nullptr && filter(candidate)) *st = candidate;
1178 check_surrounding = (*st == nullptr);
1179 }
1180 } else {
1181 /* There's no station here. Don't check the tiles surrounding this
1182 * one if the company wanted to build an adjacent station. */
1183 if (adjacent) check_surrounding = false;
1184 }
1185
1186 if (check_surrounding) {
1187 /* Make sure there is no more than one other station around us that is owned by us. */
1188 CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
1189 if (ret.Failed()) return ret;
1190 }
1191
1192 /* Distant join */
1193 if (*st == nullptr && station_to_join != INVALID_STATION) *st = T::GetIfValid(station_to_join);
1194
1195 return CommandCost();
1196}
1197
1207static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1208{
1209 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1210}
1211
1222CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
1223{
1224 if (is_road) {
1225 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); });
1226 } else {
1227 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); });
1228 }
1229}
1230
1242
1254
1269static CommandCost CalculateRailStationCost(TileArea tile_area, DoCommandFlag 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)
1270{
1272 bool length_price_ready = true;
1273 uint8_t tracknum = 0;
1274 int allowed_z = -1;
1275 for (TileIndex cur_tile : tile_area) {
1276 /* Clear the land below the station. */
1277 CommandCost ret = CheckFlatLandRailStation(cur_tile, tile_area.tile, allowed_z, flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1278 if (ret.Failed()) return ret;
1279
1280 /* Only add _price[PR_BUILD_STATION_RAIL_LENGTH] once for each valid plat_len. */
1281 if (tracknum == numtracks) {
1282 length_price_ready = true;
1283 tracknum = 0;
1284 } else {
1285 tracknum++;
1286 }
1287
1288 /* AddCost for new or rotated rail stations. */
1289 if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) {
1290 cost.AddCost(ret);
1291 cost.AddCost(_price[PR_BUILD_STATION_RAIL]);
1292 cost.AddCost(RailBuildCost(rt));
1293
1294 if (length_price_ready) {
1295 cost.AddCost(_price[PR_BUILD_STATION_RAIL_LENGTH]);
1296 length_price_ready = false;
1297 }
1298 }
1299 }
1300
1301 return cost;
1302}
1303
1311{
1312 /* Default stations do not draw pylons under roofs (gfx >= 4) */
1313 if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlags::Pylons : StationSpec::TileFlags::None;
1314 return statspec->tileflags[gfx];
1315}
1316
1329
1344CommandCost CmdBuildRailStation(DoCommandFlag 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)
1345{
1346 /* Does the authority allow this? */
1347 CommandCost ret = CheckIfAuthorityAllowsNewStation(tile_org, flags);
1348 if (ret.Failed()) return ret;
1349
1350 if (!ValParamRailType(rt) || !IsValidAxis(axis)) return CMD_ERROR;
1351
1352 /* Check if the given station class is valid */
1353 if (static_cast<uint>(spec_class) >= StationClass::GetClassCount()) return CMD_ERROR;
1354 const StationClass *cls = StationClass::Get(spec_class);
1355 if (IsWaypointClass(*cls)) return CMD_ERROR;
1356 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1357 if (plat_len == 0 || numtracks == 0) return CMD_ERROR;
1358
1359 int w_org, h_org;
1360 if (axis == AXIS_X) {
1361 w_org = plat_len;
1362 h_org = numtracks;
1363 } else {
1364 h_org = plat_len;
1365 w_org = numtracks;
1366 }
1367
1368 bool reuse = (station_to_join != NEW_STATION);
1369 if (!reuse) station_to_join = INVALID_STATION;
1370 bool distant_join = (station_to_join != INVALID_STATION);
1371
1372 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1373
1375
1376 /* these values are those that will be stored in train_tile and station_platforms */
1377 TileArea new_location(tile_org, w_org, h_org);
1378
1379 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
1380 StationID est = INVALID_STATION;
1381 std::vector<Train *> affected_vehicles;
1382 /* Add construction and clearing expenses. */
1383 CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
1384 if (cost.Failed()) return cost;
1385
1386 Station *st = nullptr;
1387 ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st);
1388 if (ret.Failed()) return ret;
1389
1390 ret = BuildStationPart(&st, flags, reuse, new_location, STATIONNAMING_RAIL);
1391 if (ret.Failed()) return ret;
1392
1393 if (st != nullptr && st->train_station.tile != INVALID_TILE) {
1394 ret = CanExpandRailStation(st, new_location);
1395 if (ret.Failed()) return ret;
1396 }
1397
1398 /* Check if we can allocate a custom stationspec to this station */
1399 const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
1400 int specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0);
1401 if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
1402
1403 if (statspec != nullptr) {
1404 /* Perform NewStation checks */
1405
1406 /* Check if the station size is permitted */
1407 if (HasBit(statspec->disallowed_platforms, std::min(numtracks - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_NUMBER_TRACKS);
1408 if (HasBit(statspec->disallowed_lengths, std::min(plat_len - 1, 7))) return CommandCost(STR_ERROR_STATION_DISALLOWED_LENGTH);
1409
1410 /* Check if the station is buildable */
1411 if (HasBit(statspec->callback_mask, CBM_STATION_AVAIL)) {
1412 uint16_t cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
1414 }
1415 }
1416
1417 if (flags & DC_EXEC) {
1418 st->train_station = new_location;
1419 st->AddFacility(FACIL_TRAIN, new_location.tile);
1420
1421 st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
1422
1423 if (statspec != nullptr) {
1424 /* Include this station spec's animation trigger bitmask
1425 * in the station's cached copy. */
1426 st->cached_anim_triggers |= statspec->animation.triggers;
1427 }
1428
1429 TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
1430 TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
1431 Track track = AxisToTrack(axis);
1432
1433 std::vector<uint8_t> layouts(numtracks * plat_len);
1434 GetStationLayout(layouts.data(), numtracks, plat_len, statspec);
1435
1436 uint8_t numtracks_orig = numtracks;
1437
1438 Company *c = Company::Get(st->owner);
1439 size_t layout_idx = 0;
1440 TileIndex tile_track = tile_org;
1441 do {
1442 TileIndex tile = tile_track;
1443 int w = plat_len;
1444 do {
1445 uint8_t layout = layouts[layout_idx++];
1446 if (IsRailStationTile(tile) && HasStationReservation(tile)) {
1447 /* Check for trains having a reservation for this tile. */
1449 if (v != nullptr) {
1450 affected_vehicles.push_back(v);
1452 }
1453 }
1454
1455 /* Railtype can change when overbuilding. */
1456 if (IsRailStationTile(tile)) {
1457 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]--;
1459 }
1460
1461 /* Remove animation if overbuilding */
1462 DeleteAnimatedTile(tile);
1463 uint8_t old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
1464 MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, rt);
1465 /* Free the spec if we overbuild something */
1466 DeallocateSpecFromStation(st, old_specindex);
1467
1468 SetCustomStationSpecIndex(tile, specindex);
1469 SetStationTileRandomBits(tile, GB(Random(), 0, 4));
1470 SetAnimationFrame(tile, 0);
1471
1472 if (statspec != nullptr) {
1473 /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
1474 uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
1475
1476 /* As the station is not yet completely finished, the station does not yet exist. */
1477 uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile);
1478 if (callback != CALLBACK_FAILED) {
1479 if (callback <= UINT8_MAX) {
1480 SetStationGfx(tile, (callback & ~1) + axis);
1481 } else {
1483 }
1484 }
1485
1486 /* Trigger station animation -- after building? */
1487 TriggerStationAnimation(st, tile, SAT_BUILT);
1488 }
1489
1490 SetRailStationTileFlags(tile, statspec);
1491
1492 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++;
1494
1495 tile += tile_delta;
1496 } while (--w);
1497 AddTrackToSignalBuffer(tile_track, track, _current_company);
1498 YapfNotifyTrackLayoutChange(tile_track, track);
1499 tile_track += track_delta;
1500 } while (--numtracks);
1501
1502 for (uint i = 0; i < affected_vehicles.size(); ++i) {
1503 /* Restore reservations of trains. */
1504 RestoreTrainReservation(affected_vehicles[i]);
1505 }
1506
1507 /* Check whether we need to expand the reservation of trains already on the station. */
1508 TileArea update_reservation_area;
1509 if (axis == AXIS_X) {
1510 update_reservation_area = TileArea(tile_org, 1, numtracks_orig);
1511 } else {
1512 update_reservation_area = TileArea(tile_org, numtracks_orig, 1);
1513 }
1514
1515 for (TileIndex tile : update_reservation_area) {
1516 /* Don't even try to make eye candy parts reserved. */
1517 if (IsStationTileBlocked(tile)) continue;
1518
1519 DiagDirection dir = AxisToDiagDir(axis);
1520 TileIndexDiff tile_offset = TileOffsByDiagDir(dir);
1521 TileIndex platform_begin = tile;
1522 TileIndex platform_end = tile;
1523
1524 /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
1525 for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
1526 platform_begin = next_tile;
1527 }
1528 for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
1529 platform_end = next_tile;
1530 }
1531
1532 /* If there is at least on reservation on the platform, we reserve the whole platform. */
1533 bool reservation = false;
1534 for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
1535 reservation = HasStationReservation(t);
1536 }
1537
1538 if (reservation) {
1539 SetRailStationPlatformReservation(platform_begin, dir, true);
1540 }
1541 }
1542
1543 st->MarkTilesDirty(false);
1544 st->AfterStationTileSetChange(true, STATION_RAIL);
1545 }
1546
1547 return cost;
1548}
1549
1550static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
1551{
1552restart:
1553
1554 /* too small? */
1555 if (ta.w != 0 && ta.h != 0) {
1556 /* check the left side, x = constant, y changes */
1557 for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
1558 /* the left side is unused? */
1559 if (++i == ta.h) {
1560 ta.tile += TileDiffXY(1, 0);
1561 ta.w--;
1562 goto restart;
1563 }
1564 }
1565
1566 /* check the right side, x = constant, y changes */
1567 for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
1568 /* the right side is unused? */
1569 if (++i == ta.h) {
1570 ta.w--;
1571 goto restart;
1572 }
1573 }
1574
1575 /* check the upper side, y = constant, x changes */
1576 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
1577 /* the left side is unused? */
1578 if (++i == ta.w) {
1579 ta.tile += TileDiffXY(0, 1);
1580 ta.h--;
1581 goto restart;
1582 }
1583 }
1584
1585 /* check the lower side, y = constant, x changes */
1586 for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
1587 /* the left side is unused? */
1588 if (++i == ta.w) {
1589 ta.h--;
1590 goto restart;
1591 }
1592 }
1593 } else {
1594 ta.Clear();
1595 }
1596
1597 return ta;
1598}
1599
1600static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
1601{
1602 return st->TileBelongsToRailStation(tile);
1603}
1604
1605static void MakeRailStationAreaSmaller(BaseStation *st)
1606{
1607 st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
1608}
1609
1610static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
1611{
1612 return IsDockTile(tile) && GetStationIndex(tile) == st->index;
1613}
1614
1615static void MakeShipStationAreaSmaller(Station *st)
1616{
1617 st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
1618 UpdateStationDockingTiles(st);
1619}
1620
1621static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
1622{
1623 return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
1624}
1625
1626void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
1627{
1628 road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
1629}
1630
1641template <class T>
1642CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlag flags, Money removal_cost, bool keep_rail)
1643{
1644 /* Count of the number of tiles removed */
1645 int quantity = 0;
1647 /* Accumulator for the errors seen during clearing. If no errors happen,
1648 * and the quantity is 0 there is no station. Otherwise it will be one
1649 * of the other error that got accumulated. */
1650 CommandCost error;
1651
1652 /* Do the action for every tile into the area */
1653 for (TileIndex tile : ta) {
1654 /* Make sure the specified tile is a rail station */
1655 if (!HasStationTileRail(tile)) continue;
1656
1657 /* If there is a vehicle on ground, do not allow to remove (flood) the tile */
1659 error.AddCost(ret);
1660 if (ret.Failed()) continue;
1661
1662 /* Check ownership of station */
1663 T *st = T::GetByTile(tile);
1664 if (st == nullptr) continue;
1665
1667 ret = CheckOwnership(st->owner);
1668 error.AddCost(ret);
1669 if (ret.Failed()) continue;
1670 }
1671
1672 /* If we reached here, the tile is valid so increase the quantity of tiles we will remove */
1673 quantity++;
1674
1675 if (keep_rail || IsStationTileBlocked(tile)) {
1676 /* Don't refund the 'steel' of the track when we keep the
1677 * rail, or when the tile didn't have any rail at all. */
1678 total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
1679 }
1680
1681 if (flags & DC_EXEC) {
1682 /* read variables before the station tile is removed */
1683 uint specindex = GetCustomStationSpecIndex(tile);
1684 Track track = GetRailStationTrack(tile);
1685 Owner owner = GetTileOwner(tile);
1686 RailType rt = GetRailType(tile);
1687 Train *v = nullptr;
1688
1689 if (HasStationReservation(tile)) {
1690 v = GetTrainForReservation(tile, track);
1691 if (v != nullptr) FreeTrainReservation(v);
1692 }
1693
1694 bool build_rail = keep_rail && !IsStationTileBlocked(tile);
1695 if (!build_rail && !IsStationTileBlocked(tile)) Company::Get(owner)->infrastructure.rail[rt]--;
1696
1697 DoClearSquare(tile);
1698 DeleteNewGRFInspectWindow(GSF_STATIONS, tile.base());
1699 if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt);
1700 Company::Get(owner)->infrastructure.station--;
1702
1703 st->rect.AfterRemoveTile(st, tile);
1704 AddTrackToSignalBuffer(tile, track, owner);
1705 YapfNotifyTrackLayoutChange(tile, track);
1706
1707 DeallocateSpecFromStation(st, specindex);
1708
1709 include(affected_stations, st);
1710
1711 if (v != nullptr) RestoreTrainReservation(v);
1712 }
1713 }
1714
1715 if (quantity == 0) return error.Failed() ? error : CommandCost(STR_ERROR_THERE_IS_NO_STATION);
1716
1717 for (T *st : affected_stations) {
1718
1719 /* now we need to make the "spanned" area of the railway station smaller
1720 * if we deleted something at the edges.
1721 * we also need to adjust train_tile. */
1722 MakeRailStationAreaSmaller(st);
1723 UpdateStationSignCoord(st);
1724
1725 /* if we deleted the whole station, delete the train facility. */
1726 if (st->train_station.tile == INVALID_TILE) {
1727 st->facilities &= ~FACIL_TRAIN;
1730 MarkCatchmentTilesDirty();
1731 st->UpdateVirtCoord();
1733 }
1734 }
1735
1736 total_cost.AddCost(quantity * removal_cost);
1737 return total_cost;
1738}
1739
1750{
1751 if (end == 0) end = start;
1752 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1753
1754 TileArea ta(start, end);
1755 std::vector<Station *> affected_stations;
1756
1757 CommandCost ret = RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_STATION_RAIL], keep_rail);
1758 if (ret.Failed()) return ret;
1759
1760 /* Do all station specific functions here. */
1761 for (Station *st : affected_stations) {
1762
1764 st->MarkTilesDirty(false);
1765 MarkCatchmentTilesDirty();
1766 st->RecomputeCatchment();
1767 }
1768
1769 /* Now apply the rail cost to the number that we deleted */
1770 return ret;
1771}
1772
1783{
1784 if (end == 0) end = start;
1785 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
1786
1787 TileArea ta(start, end);
1788 std::vector<Waypoint *> affected_stations;
1789
1790 return RemoveFromRailBaseStation(ta, affected_stations, flags, _price[PR_CLEAR_WAYPOINT_RAIL], keep_rail);
1791}
1792
1793
1802template <class T>
1804{
1805 /* Current company owns the station? */
1807 CommandCost ret = CheckOwnership(st->owner);
1808 if (ret.Failed()) return ret;
1809 }
1810
1811 /* determine width and height of platforms */
1812 TileArea ta = st->train_station;
1813
1814 assert(ta.w != 0 && ta.h != 0);
1815
1817 /* clear all areas of the station */
1818 for (TileIndex tile : ta) {
1819 /* only remove tiles that are actually train station tiles */
1820 if (st->TileBelongsToRailStation(tile)) {
1821 std::vector<T*> affected_stations; // dummy
1822 CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
1823 if (ret.Failed()) return ret;
1824 cost.AddCost(ret);
1825 }
1826 }
1827
1828 return cost;
1829}
1830
1838{
1839 /* if there is flooding, remove platforms tile by tile */
1841 return Command<CMD_REMOVE_FROM_RAIL_STATION>::Do(DC_EXEC, tile, 0, false);
1842 }
1843
1844 Station *st = Station::GetByTile(tile);
1845 CommandCost cost = RemoveRailStation(st, flags, _price[PR_CLEAR_STATION_RAIL]);
1846
1847 if (flags & DC_EXEC) st->RecomputeCatchment();
1848
1849 return cost;
1850}
1851
1859{
1860 /* if there is flooding, remove waypoints tile by tile */
1863 }
1864
1865 return RemoveRailStation(Waypoint::GetByTile(tile), flags, _price[PR_CLEAR_WAYPOINT_RAIL]);
1866}
1867
1868
1874static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
1875{
1876 RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
1877
1878 if (*primary_stop == nullptr) {
1879 /* we have no roadstop of the type yet, so write a "primary stop" */
1880 return primary_stop;
1881 } else {
1882 /* there are stops already, so append to the end of the list */
1883 RoadStop *stop = *primary_stop;
1884 while (stop->next != nullptr) stop = stop->next;
1885 return &stop->next;
1886 }
1887}
1888
1889static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index = -1);
1890CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index = -1);
1891
1901static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
1902{
1903 return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
1904}
1905
1919CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlag flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
1920{
1921 uint invalid_dirs = 0;
1922 if (is_drive_through) {
1923 SetBit(invalid_dirs, AxisToDiagDir(axis));
1924 SetBit(invalid_dirs, ReverseDiagDir(AxisToDiagDir(axis)));
1925 } else {
1926 SetBit(invalid_dirs, ddir);
1927 }
1928
1929 /* Check every tile in the area. */
1930 int allowed_z = -1;
1932 for (TileIndex cur_tile : tile_area) {
1933 CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
1934 if (ret.Failed()) return ret;
1935
1936 bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
1937
1938 /* Only add costs if a stop doesn't already exist in the location */
1939 if (!is_preexisting_roadstop) {
1940 cost.AddCost(ret);
1941 cost.AddCost(unit_cost);
1942 }
1943 }
1944
1945 return cost;
1946}
1947
1964CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through,
1965 DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
1966{
1967 if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= ROADSTOP_END) return CMD_ERROR;
1968 bool reuse = (station_to_join != NEW_STATION);
1969 if (!reuse) station_to_join = INVALID_STATION;
1970 bool distant_join = (station_to_join != INVALID_STATION);
1971
1972 /* Check if the given station class is valid */
1973 if (static_cast<uint>(spec_class) >= RoadStopClass::GetClassCount()) return CMD_ERROR;
1974 const RoadStopClass *cls = RoadStopClass::Get(spec_class);
1975 if (IsWaypointClass(*cls)) return CMD_ERROR;
1976 if (spec_index >= cls->GetSpecCount()) return CMD_ERROR;
1977
1978 const RoadStopSpec *roadstopspec = cls->GetSpec(spec_index);
1979 if (roadstopspec != nullptr) {
1980 if (stop_type == ROADSTOP_TRUCK && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
1981 if (stop_type == ROADSTOP_BUS && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
1982 if (!is_drive_through && HasBit(roadstopspec->flags, RSF_DRIVE_THROUGH_ONLY)) return CMD_ERROR;
1983 }
1984
1985 /* Check if the requested road stop is too big */
1986 if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
1987 /* Check for incorrect width / length. */
1988 if (width == 0 || length == 0) return CMD_ERROR;
1989 /* Check if the first tile and the last tile are valid */
1990 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, length - 1) == INVALID_TILE) return CMD_ERROR;
1991
1992 TileArea roadstop_area(tile, width, length);
1993
1994 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
1995
1996 /* Trams only have drive through stops */
1997 if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
1998
1999 Axis axis = DiagDirToAxis(ddir);
2000
2002 if (ret.Failed()) return ret;
2003
2004 bool is_truck_stop = stop_type != ROADSTOP_BUS;
2005
2006 /* Total road stop cost. */
2007 Money unit_cost;
2008 if (roadstopspec != nullptr) {
2009 unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS);
2010 } else {
2011 unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
2012 }
2013 StationID est = INVALID_STATION;
2014 CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? STATION_TRUCK : STATION_BUS, axis, ddir, &est, rt, unit_cost);
2015 if (cost.Failed()) return cost;
2016
2017 Station *st = nullptr;
2018 ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st);
2019 if (ret.Failed()) return ret;
2020
2021 /* Check if this number of road stops can be allocated. */
2022 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);
2023
2024 ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD);
2025 if (ret.Failed()) return ret;
2026
2027 /* Check if we can allocate a custom stationspec to this station */
2028 int specindex = AllocateSpecToRoadStop(roadstopspec, st, (flags & DC_EXEC) != 0);
2029 if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
2030
2031 if (roadstopspec != nullptr) {
2032 /* Perform NewGRF checks */
2033
2034 /* Check if the road stop is buildable */
2035 if (HasBit(roadstopspec->callback_mask, CBM_ROAD_STOP_AVAIL)) {
2036 uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? STATION_TRUCK : STATION_BUS, 0);
2037 if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
2038 }
2039 }
2040
2041 if (flags & DC_EXEC) {
2042 /* Check every tile in the area. */
2043 for (TileIndex cur_tile : roadstop_area) {
2044 /* Get existing road types and owners before any tile clearing */
2045 RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
2046 RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
2047 Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
2048 Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
2049
2050 if (IsTileType(cur_tile, MP_STATION) && IsStationRoadStop(cur_tile)) {
2051 RemoveRoadStop(cur_tile, flags, specindex);
2052 }
2053
2054 if (roadstopspec != nullptr) {
2055 /* Include this road stop spec's animation trigger bitmask
2056 * in the station's cached copy. */
2057 st->cached_roadstop_anim_triggers |= roadstopspec->animation.triggers;
2058 }
2059
2060 RoadStop *road_stop = new RoadStop(cur_tile);
2061 /* Insert into linked list of RoadStops. */
2062 RoadStop **currstop = FindRoadStopSpot(is_truck_stop, st);
2063 *currstop = road_stop;
2064
2065 if (is_truck_stop) {
2066 st->truck_station.Add(cur_tile);
2067 } else {
2068 st->bus_station.Add(cur_tile);
2069 }
2070
2071 /* Initialize an empty station. */
2072 st->AddFacility(is_truck_stop ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, cur_tile);
2073
2074 st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
2075
2076 RoadStopType rs_type = is_truck_stop ? ROADSTOP_TRUCK : ROADSTOP_BUS;
2077 if (is_drive_through) {
2078 /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
2079 * bits first. */
2080 if (IsNormalRoadTile(cur_tile)) {
2081 UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
2082 UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
2083 }
2084
2085 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2086 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2087
2088 MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), road_rt, tram_rt, axis);
2089 road_stop->MakeDriveThrough();
2090 } else {
2091 if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
2092 if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
2093 MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
2094 }
2097 Company::Get(st->owner)->infrastructure.station++;
2098
2099 SetCustomRoadStopSpecIndex(cur_tile, specindex);
2100 if (roadstopspec != nullptr) {
2101 st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
2102 TriggerRoadStopAnimation(st, cur_tile, SAT_BUILT);
2103 }
2104
2105 MarkTileDirtyByTile(cur_tile);
2106 }
2107
2108 if (st != nullptr) {
2109 st->AfterStationTileSetChange(true, is_truck_stop ? STATION_TRUCK: STATION_BUS);
2110 }
2111 }
2112 return cost;
2113}
2114
2115
2116static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
2117{
2118 if (v->type == VEH_ROAD) {
2119 /* Okay... we are a road vehicle on a drive through road stop.
2120 * But that road stop has just been removed, so we need to make
2121 * sure we are in a valid state... however, vehicles can also
2122 * turn on road stop tiles, so only clear the 'road stop' state
2123 * bits and only when the state was 'in road stop', otherwise
2124 * we'll end up clearing the turn around bits. */
2127 }
2128
2129 return nullptr;
2130}
2131
2132
2140static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index)
2141{
2142 Station *st = Station::GetByTile(tile);
2143
2145 CommandCost ret = CheckOwnership(st->owner);
2146 if (ret.Failed()) return ret;
2147 }
2148
2149 bool is_truck = IsTruckStop(tile);
2150
2151 RoadStop **primary_stop;
2152 RoadStop *cur_stop;
2153 if (is_truck) { // truck stop
2154 primary_stop = &st->truck_stops;
2155 cur_stop = RoadStop::GetByTile(tile, ROADSTOP_TRUCK);
2156 } else {
2157 primary_stop = &st->bus_stops;
2158 cur_stop = RoadStop::GetByTile(tile, ROADSTOP_BUS);
2159 }
2160
2161 assert(cur_stop != nullptr);
2162
2163 /* don't do the check for drive-through road stops when company bankrupts */
2164 if (IsDriveThroughStopTile(tile) && (flags & DC_BANKRUPT)) {
2165 /* remove the 'going through road stop' status from all vehicles on that tile */
2166 if (flags & DC_EXEC) FindVehicleOnPos(tile, nullptr, &ClearRoadStopStatusEnum);
2167 } else {
2169 if (ret.Failed()) return ret;
2170 }
2171
2172 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2173
2174 if (flags & DC_EXEC) {
2175 if (*primary_stop == cur_stop) {
2176 /* removed the first stop in the list */
2177 *primary_stop = cur_stop->next;
2178 /* removed the only stop? */
2179 if (*primary_stop == nullptr) {
2180 st->facilities &= (is_truck ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP);
2182 }
2183 } else {
2184 /* tell the predecessor in the list to skip this stop */
2185 RoadStop *pred = *primary_stop;
2186 while (pred->next != cur_stop) pred = pred->next;
2187 pred->next = cur_stop->next;
2188 }
2189
2190 /* Update company infrastructure counts. */
2191 for (RoadTramType rtt : _roadtramtypes) {
2192 RoadType rt = GetRoadType(tile, rtt);
2194 }
2195
2196 Company::Get(st->owner)->infrastructure.station--;
2198
2199 DeleteAnimatedTile(tile);
2200
2201 uint specindex = GetCustomRoadStopSpecIndex(tile);
2202
2203 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2204
2205 if (IsDriveThroughStopTile(tile)) {
2206 /* Clears the tile for us */
2207 cur_stop->ClearDriveThrough();
2208 } else {
2209 DoClearSquare(tile);
2210 }
2211
2212 delete cur_stop;
2213
2214 /* Make sure no vehicle is going to the old roadstop. Narrow the search to any road vehicles with an order to
2215 * this station, then look for any currently heading to the tile. */
2216 StationID station_id = st->index;
2218 [](const Vehicle *v) { return v->type == VEH_ROAD; },
2219 [station_id](const Order *order) { return order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id; },
2220 [station_id, tile](Vehicle *v) {
2221 if (v->current_order.IsType(OT_GOTO_STATION) && v->dest_tile == tile) {
2222 v->SetDestTile(v->GetOrderStationLocation(station_id));
2223 }
2224 }
2225 );
2226
2227 st->rect.AfterRemoveTile(st, tile);
2228
2229 if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? STATION_TRUCK: STATION_BUS);
2230
2231 st->RemoveRoadStopTileData(tile);
2232 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
2233
2234 /* Update the tile area of the truck/bus stop */
2235 if (is_truck) {
2236 st->truck_station.Clear();
2237 for (const RoadStop *rs = st->truck_stops; rs != nullptr; rs = rs->next) st->truck_station.Add(rs->xy);
2238 } else {
2239 st->bus_station.Clear();
2240 for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
2241 }
2242 }
2243
2244 Price category = is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS;
2245 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
2246}
2247
2255CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index)
2256{
2257 Waypoint *wp = Waypoint::GetByTile(tile);
2258
2260 CommandCost ret = CheckOwnership(wp->owner);
2261 if (ret.Failed()) return ret;
2262 }
2263
2264 /* Ignore vehicles when the company goes bankrupt. The road will remain, any vehicles going to the waypoint will be removed. */
2265 if (!(flags & DC_BANKRUPT)) {
2267 if (ret.Failed()) return ret;
2268 }
2269
2270 const RoadStopSpec *spec = GetRoadStopSpec(tile);
2271
2272 if (flags & DC_EXEC) {
2273 /* Update company infrastructure counts. */
2274 for (RoadTramType rtt : _roadtramtypes) {
2275 RoadType rt = GetRoadType(tile, rtt);
2277 }
2278
2279 Company::Get(wp->owner)->infrastructure.station--;
2281
2282 DeleteAnimatedTile(tile);
2283
2284 uint specindex = GetCustomRoadStopSpecIndex(tile);
2285
2286 DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
2287
2288 DoClearSquare(tile);
2289
2290 wp->rect.AfterRemoveTile(wp, tile);
2291
2292 wp->RemoveRoadStopTileData(tile);
2293 if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
2294
2295 if (replacement_spec_index < 0) {
2296 MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
2297
2298 UpdateStationSignCoord(wp);
2299
2300 /* if we deleted the whole waypoint, delete the road facility. */
2304 wp->UpdateVirtCoord();
2306 }
2307 }
2308 }
2309
2310 return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(PR_CLEAR_STATION_TRUCK) : _price[PR_CLEAR_STATION_TRUCK]);
2311}
2312
2321static CommandCost RemoveGenericRoadStop(DoCommandFlag flags, const TileArea &roadstop_area, StationType station_type, bool remove_road)
2322{
2324 CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
2325 bool had_success = false;
2326
2327 for (TileIndex cur_tile : roadstop_area) {
2328 /* Make sure the specified tile is a road stop of the correct type */
2329 if (!IsTileType(cur_tile, MP_STATION) || !IsAnyRoadStop(cur_tile) || GetStationType(cur_tile) != station_type) continue;
2330
2331 /* Save information on to-be-restored roads before the stop is removed. */
2332 RoadBits road_bits = ROAD_NONE;
2333 RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
2334 Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
2335 if (IsDriveThroughStopTile(cur_tile)) {
2336 for (RoadTramType rtt : _roadtramtypes) {
2337 road_type[rtt] = GetRoadType(cur_tile, rtt);
2338 if (road_type[rtt] == INVALID_ROADTYPE) continue;
2339 road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
2340 /* If we don't want to preserve our roads then restore only roads of others. */
2341 if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
2342 }
2343 road_bits = AxisToRoadBits(GetDriveThroughStopAxis(cur_tile));
2344 }
2345
2346 CommandCost ret;
2347 if (station_type == STATION_ROADWAYPOINT) {
2348 ret = RemoveRoadWaypointStop(cur_tile, flags);
2349 } else {
2350 ret = RemoveRoadStop(cur_tile, flags);
2351 }
2352 if (ret.Failed()) {
2353 last_error = ret;
2354 continue;
2355 }
2356 cost.AddCost(ret);
2357 had_success = true;
2358
2359 /* Restore roads. */
2360 if ((flags & DC_EXEC) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
2361 MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
2362 road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
2363
2364 /* Update company infrastructure counts. */
2365 int count = CountBits(road_bits);
2366 UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
2367 UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
2368 }
2369 }
2370
2371 return had_success ? cost : last_error;
2372}
2373
2384CommandCost CmdRemoveRoadStop(DoCommandFlag flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
2385{
2386 if (stop_type >= ROADSTOP_END) return CMD_ERROR;
2387 /* Check for incorrect width / height. */
2388 if (width == 0 || height == 0) return CMD_ERROR;
2389 /* Check if the first tile and the last tile are valid */
2390 if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
2391 /* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
2392 if (remove_road && (flags & DC_BANKRUPT)) return CMD_ERROR;
2393
2394 TileArea roadstop_area(tile, width, height);
2395
2396 return RemoveGenericRoadStop(flags, roadstop_area, stop_type == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK, remove_road);
2397}
2398
2407{
2408 if (end == 0) end = start;
2409 if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
2410
2411 TileArea roadstop_area(start, end);
2412
2413 return RemoveGenericRoadStop(flags, roadstop_area, STATION_ROADWAYPOINT, false);
2414}
2415
2424uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
2425{
2426 /* 0 cannot be accounted, and 1 is the lowest that can be reduced from town.
2427 * So no need to go any further*/
2428 if (as->noise_level < 2) return as->noise_level;
2429
2430 /* The steps for measuring noise reduction are based on the "magical" (and arbitrary) 8 base distance
2431 * adding the town_council_tolerance 4 times, as a way to graduate, depending of the tolerance.
2432 * Basically, it says that the less tolerant a town is, the bigger the distance before
2433 * an actual decrease can be granted */
2434 uint8_t town_tolerance_distance = 8 + (_settings_game.difficulty.town_council_tolerance * 4);
2435
2436 /* now, we want to have the distance segmented using the distance judged bareable by town
2437 * This will give us the coefficient of reduction the distance provides. */
2438 uint noise_reduction = distance / town_tolerance_distance;
2439
2440 /* If the noise reduction equals the airport noise itself, don't give it for free.
2441 * Otherwise, simply reduce the airport's level. */
2442 return noise_reduction >= as->noise_level ? 1 : as->noise_level - noise_reduction;
2443}
2444
2455Town *AirportGetNearestTown(const AirportSpec *as, Direction rotation, TileIndex tile, TileIterator &&it, uint &mindist)
2456{
2457 assert(Town::GetNumItems() > 0);
2458
2459 Town *nearest = nullptr;
2460
2461 auto width = as->size_x;
2462 auto height = as->size_y;
2463 if (rotation == DIR_E || rotation == DIR_W) std::swap(width, height);
2464
2465 uint perimeter_min_x = TileX(tile);
2466 uint perimeter_min_y = TileY(tile);
2467 uint perimeter_max_x = perimeter_min_x + width - 1;
2468 uint perimeter_max_y = perimeter_min_y + height - 1;
2469
2470 mindist = UINT_MAX - 1; // prevent overflow
2471
2472 for (TileIndex cur_tile = *it; cur_tile != INVALID_TILE; cur_tile = ++it) {
2473 assert(IsInsideBS(TileX(cur_tile), perimeter_min_x, width));
2474 assert(IsInsideBS(TileY(cur_tile), perimeter_min_y, height));
2475 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) {
2476 Town *t = CalcClosestTownFromTile(cur_tile, mindist + 1);
2477 if (t == nullptr) continue;
2478
2479 uint dist = DistanceManhattan(t->xy, cur_tile);
2480 if (dist == mindist && t->index < nearest->index) nearest = t;
2481 if (dist < mindist) {
2482 nearest = t;
2483 mindist = dist;
2484 }
2485 }
2486 }
2487
2488 return nearest;
2489}
2490
2498static Town *AirportGetNearestTown(const Station *st, uint &mindist)
2499{
2501}
2502
2503
2506{
2507 for (Town *t : Town::Iterate()) t->noise_reached = 0;
2508
2509 for (const Station *st : Station::Iterate()) {
2510 if (st->airport.tile != INVALID_TILE && st->airport.type != AT_OILRIG) {
2511 uint dist;
2512 Town *nearest = AirportGetNearestTown(st, dist);
2513 nearest->noise_reached += GetAirportNoiseLevelForDistance(st->airport.GetSpec(), dist);
2514 }
2515 }
2516}
2517
2528CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
2529{
2530 bool reuse = (station_to_join != NEW_STATION);
2531 if (!reuse) station_to_join = INVALID_STATION;
2532 bool distant_join = (station_to_join != INVALID_STATION);
2533
2534 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2535
2536 if (airport_type >= NUM_AIRPORTS) return CMD_ERROR;
2537
2539 if (ret.Failed()) return ret;
2540
2541 /* Check if a valid, buildable airport was chosen for construction */
2542 const AirportSpec *as = AirportSpec::Get(airport_type);
2543 if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
2544 if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
2545
2546 Direction rotation = as->layouts[layout].rotation;
2547 int w = as->size_x;
2548 int h = as->size_y;
2549 if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
2550 TileArea airport_area = TileArea(tile, w, h);
2551
2553 return CommandCost(STR_ERROR_STATION_TOO_SPREAD_OUT);
2554 }
2555
2556 AirportTileTableIterator tile_iter(as->layouts[layout].tiles.data(), tile);
2557 CommandCost cost = CheckFlatLandAirport(tile_iter, flags);
2558 if (cost.Failed()) return cost;
2559
2560 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. */
2561 uint dist;
2562 Town *nearest = AirportGetNearestTown(as, rotation, tile, std::move(tile_iter), dist);
2563 uint newnoise_level = GetAirportNoiseLevelForDistance(as, dist);
2564
2565 /* Check if local auth would allow a new airport */
2566 StringID authority_refuse_message = STR_NULL;
2567 Town *authority_refuse_town = nullptr;
2568
2570 /* do not allow to build a new airport if this raise the town noise over the maximum allowed by town */
2571 if ((nearest->noise_reached + newnoise_level) > nearest->MaxTownNoise()) {
2572 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE;
2573 authority_refuse_town = nearest;
2574 }
2575 } else if (_settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) {
2576 Town *t = ClosestTownFromTile(tile, UINT_MAX);
2577 uint num = 0;
2578 for (const Station *st : Station::Iterate()) {
2579 if (st->town == t && (st->facilities & FACIL_AIRPORT) && st->airport.type != AT_OILRIG) num++;
2580 }
2581 if (num >= 2) {
2582 authority_refuse_message = STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT;
2583 authority_refuse_town = t;
2584 }
2585 }
2586
2587 if (authority_refuse_message != STR_NULL) {
2588 SetDParam(0, authority_refuse_town->index);
2589 return CommandCost(authority_refuse_message);
2590 }
2591
2592 Station *st = nullptr;
2593 ret = FindJoiningStation(INVALID_STATION, station_to_join, allow_adjacent, airport_area, &st);
2594 if (ret.Failed()) return ret;
2595
2596 /* Distant join */
2597 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2598
2599 ret = BuildStationPart(&st, flags, reuse, airport_area, (GetAirport(airport_type)->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT);
2600 if (ret.Failed()) return ret;
2601
2602 if (st != nullptr && st->airport.tile != INVALID_TILE) {
2603 return CommandCost(STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT);
2604 }
2605
2606 for (AirportTileTableIterator iter(as->layouts[layout].tiles.data(), tile); iter != INVALID_TILE; ++iter) {
2607 cost.AddCost(_price[PR_BUILD_STATION_AIRPORT]);
2608 }
2609
2610 if (flags & DC_EXEC) {
2611 /* Always add the noise, so there will be no need to recalculate when option toggles */
2612 nearest->noise_reached += newnoise_level;
2613
2614 st->AddFacility(FACIL_AIRPORT, tile);
2615 st->airport.type = airport_type;
2616 st->airport.layout = layout;
2617 st->airport.flags = 0;
2618 st->airport.rotation = rotation;
2619
2620 st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY);
2621
2622 for (AirportTileTableIterator iter(as->layouts[layout].tiles.data(), tile); iter != INVALID_TILE; ++iter) {
2623 Tile t(iter);
2624 MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID);
2625 SetStationTileRandomBits(t, GB(Random(), 0, 4));
2626 st->airport.Add(iter);
2627
2629 }
2630
2631 /* Only call the animation trigger after all tiles have been built */
2632 for (AirportTileTableIterator iter(as->layouts[layout].tiles.data(), tile); iter != INVALID_TILE; ++iter) {
2633 AirportTileAnimationTrigger(st, iter, AAT_BUILT);
2634 }
2635
2637
2638 Company::Get(st->owner)->infrastructure.airport++;
2639
2640 st->AfterStationTileSetChange(true, STATION_AIRPORT);
2642
2645 }
2646 }
2647
2648 return cost;
2649}
2650
2658{
2659 Station *st = Station::GetByTile(tile);
2660
2662 CommandCost ret = CheckOwnership(st->owner);
2663 if (ret.Failed()) return ret;
2664 }
2665
2666 tile = st->airport.tile;
2667
2669
2670 for (const Aircraft *a : Aircraft::Iterate()) {
2671 if (!a->IsNormalAircraft()) continue;
2672 if (a->targetairport == st->index && a->state != FLYING) {
2673 return CommandCost(STR_ERROR_AIRCRAFT_IN_THE_WAY);
2674 }
2675 }
2676
2677 if (flags & DC_EXEC) {
2678 for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
2679 TileIndex tile_cur = st->airport.GetHangarTile(i);
2680 OrderBackup::Reset(tile_cur, false);
2682 }
2683
2684 /* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
2685 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
2686 * need of recalculation */
2687 uint dist;
2688 Town *nearest = AirportGetNearestTown(st, dist);
2690
2693 }
2694 }
2695
2696 for (TileIndex tile_cur : st->airport) {
2697 if (!st->TileBelongsToAirport(tile_cur)) continue;
2698
2699 CommandCost ret = EnsureNoVehicleOnGround(tile_cur);
2700 if (ret.Failed()) return ret;
2701
2702 cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
2703
2704 if (flags & DC_EXEC) {
2705 DeleteAnimatedTile(tile_cur);
2706 DoClearSquare(tile_cur);
2707 DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur.base());
2708 }
2709 }
2710
2711 if (flags & DC_EXEC) {
2712 /* Clear the persistent storage. */
2713 delete st->airport.psa;
2714
2715 st->rect.AfterRemoveRect(st, st->airport);
2716
2717 st->airport.Clear();
2718 st->facilities &= ~FACIL_AIRPORT;
2720
2722
2723 Company::Get(st->owner)->infrastructure.airport--;
2724
2725 st->AfterStationTileSetChange(false, STATION_AIRPORT);
2726
2727 DeleteNewGRFInspectWindow(GSF_AIRPORTS, st->index);
2728 }
2729
2730 return cost;
2731}
2732
2740{
2741 if (!Station::IsValidID(station_id)) return CMD_ERROR;
2742 Station *st = Station::Get(station_id);
2743
2744 if (!(st->facilities & FACIL_AIRPORT) || st->owner == OWNER_NONE) return CMD_ERROR;
2745
2746 CommandCost ret = CheckOwnership(st->owner);
2747 if (ret.Failed()) return ret;
2748
2749 if (flags & DC_EXEC) {
2752 }
2753 return CommandCost();
2754}
2755
2762bool HasStationInUse(StationID station, bool include_company, CompanyID company)
2763{
2764 for (const OrderList *orderlist : OrderList::Iterate()) {
2765 const Vehicle *v = orderlist->GetFirstSharedVehicle();
2766 assert(v != nullptr);
2767 if ((v->owner == company) != include_company) continue;
2768
2769 for (const Order *order = orderlist->GetFirstOrder(); order != nullptr; order = order->next) {
2770 if (order->GetDestination() == station && (order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT))) {
2771 return true;
2772 }
2773 }
2774 }
2775 return false;
2776}
2777
2778static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
2779 {-1, 0},
2780 { 0, 0},
2781 { 0, 0},
2782 { 0, -1}
2783};
2784static const uint8_t _dock_w_chk[4] = { 2, 1, 2, 1 };
2785static const uint8_t _dock_h_chk[4] = { 1, 2, 1, 2 };
2786
2795CommandCost CmdBuildDock(DoCommandFlag flags, TileIndex tile, StationID station_to_join, bool adjacent)
2796{
2797 bool reuse = (station_to_join != NEW_STATION);
2798 if (!reuse) station_to_join = INVALID_STATION;
2799 bool distant_join = (station_to_join != INVALID_STATION);
2800
2801 if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
2802
2804 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2805 direction = ReverseDiagDir(direction);
2806
2807 /* Docks cannot be placed on rapids */
2808 if (HasTileWaterGround(tile)) return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2809
2811 if (ret.Failed()) return ret;
2812
2813 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
2814
2815 CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
2816 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
2817 if (ret.Failed()) return ret;
2818 cost.AddCost(ret);
2819
2820 TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
2821
2822 if (!HasTileWaterGround(tile_cur) || !IsTileFlat(tile_cur)) {
2823 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2824 }
2825
2826 if (IsBridgeAbove(tile_cur)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
2827
2828 /* Get the water class of the water tile before it is cleared.*/
2829 WaterClass wc = GetWaterClass(tile_cur);
2830
2831 bool add_cost = !IsWaterTile(tile_cur);
2832 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_cur);
2833 if (ret.Failed()) return ret;
2834 if (add_cost) cost.AddCost(ret);
2835
2836 tile_cur += TileOffsByDiagDir(direction);
2837 if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) {
2838 return CommandCost(STR_ERROR_SITE_UNSUITABLE);
2839 }
2840
2841 TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
2842 _dock_w_chk[direction], _dock_h_chk[direction]);
2843
2844 /* middle */
2845 Station *st = nullptr;
2846 ret = FindJoiningStation(INVALID_STATION, station_to_join, adjacent, dock_area, &st);
2847 if (ret.Failed()) return ret;
2848
2849 /* Distant join */
2850 if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
2851
2852 ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
2853 if (ret.Failed()) return ret;
2854
2855 if (flags & DC_EXEC) {
2856 st->ship_station.Add(tile);
2857 TileIndex flat_tile = tile + TileOffsByDiagDir(direction);
2858 st->ship_station.Add(flat_tile);
2859 st->AddFacility(FACIL_DOCK, tile);
2860
2861 st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
2862
2863 /* If the water part of the dock is on a canal, update infrastructure counts.
2864 * This is needed as we've cleared that tile before.
2865 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
2866 * See: MakeWaterKeepingClass() */
2867 if (wc == WATER_CLASS_CANAL && !(HasTileWaterClass(flat_tile) && GetWaterClass(flat_tile) == WATER_CLASS_CANAL && IsTileOwner(flat_tile, _current_company))) {
2868 Company::Get(st->owner)->infrastructure.water++;
2869 }
2870 Company::Get(st->owner)->infrastructure.station += 2;
2871
2872 MakeDock(tile, st->owner, st->index, direction, wc);
2873 UpdateStationDockingTiles(st);
2874
2875 st->AfterStationTileSetChange(true, STATION_DOCK);
2876 }
2877
2878 return cost;
2879}
2880
2881void RemoveDockingTile(TileIndex t)
2882{
2883 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2884 TileIndex tile = t + TileOffsByDiagDir(d);
2885 if (!IsValidTile(tile)) continue;
2886
2887 if (IsTileType(tile, MP_STATION)) {
2888 Station *st = Station::GetByTile(tile);
2889 if (st != nullptr) UpdateStationDockingTiles(st);
2890 } else if (IsTileType(tile, MP_INDUSTRY)) {
2892 if (neutral != nullptr) UpdateStationDockingTiles(neutral);
2893 }
2894 }
2895}
2896
2903{
2904 assert(IsValidTile(tile));
2905
2906 /* Clear and maybe re-set docking tile */
2907 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2908 TileIndex docking_tile = tile + TileOffsByDiagDir(d);
2909 if (!IsValidTile(docking_tile)) continue;
2910
2911 if (IsPossibleDockingTile(docking_tile)) {
2912 SetDockingTile(docking_tile, false);
2913 CheckForDockingTile(docking_tile);
2914 }
2915 }
2916}
2917
2924{
2925 assert(IsDockTile(t));
2926
2927 StationGfx gfx = GetStationGfx(t);
2928 if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
2929
2930 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
2931 TileIndex tile = t + TileOffsByDiagDir(d);
2932 if (!IsValidTile(tile)) continue;
2933 if (!IsDockTile(tile)) continue;
2934 if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
2935 }
2936
2937 return INVALID_TILE;
2938}
2939
2947{
2948 Station *st = Station::GetByTile(tile);
2949 CommandCost ret = CheckOwnership(st->owner);
2950 if (ret.Failed()) return ret;
2951
2952 if (!IsDockTile(tile)) return CMD_ERROR;
2953
2954 TileIndex tile1 = FindDockLandPart(tile);
2955 if (tile1 == INVALID_TILE) return CMD_ERROR;
2956 TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
2957
2958 ret = EnsureNoVehicleOnGround(tile1);
2959 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
2960 if (ret.Failed()) return ret;
2961
2962 if (flags & DC_EXEC) {
2963 DoClearSquare(tile1);
2964 MarkTileDirtyByTile(tile1);
2965 MakeWaterKeepingClass(tile2, st->owner);
2966
2967 st->rect.AfterRemoveTile(st, tile1);
2968 st->rect.AfterRemoveTile(st, tile2);
2969
2970 MakeShipStationAreaSmaller(st);
2971 if (st->ship_station.tile == INVALID_TILE) {
2972 st->ship_station.Clear();
2973 st->docking_station.Clear();
2974 st->facilities &= ~FACIL_DOCK;
2976 }
2977
2978 Company::Get(st->owner)->infrastructure.station -= 2;
2979
2980 st->AfterStationTileSetChange(false, STATION_DOCK);
2981
2984
2985 for (Ship *s : Ship::Iterate()) {
2986 /* Find all ships going to our dock. */
2987 if (s->current_order.GetDestination() != st->index) {
2988 continue;
2989 }
2990
2991 /* Find ships that are marked as "loading" but are no longer on a
2992 * docking tile. Force them to leave the station (as they were loading
2993 * on the removed dock). */
2994 if (s->current_order.IsType(OT_LOADING) && !(IsDockingTile(s->tile) && IsShipDestinationTile(s->tile, st->index))) {
2995 s->LeaveStation();
2996 }
2997
2998 /* If we no longer have a dock, mark the order as invalid and send
2999 * the ship to the next order (or, if there is none, make it
3000 * wander the world). */
3001 if (s->current_order.IsType(OT_GOTO_STATION) && !(st->facilities & FACIL_DOCK)) {
3002 s->SetDestTile(s->GetOrderStationLocation(st->index));
3003 }
3004 }
3005 }
3006
3007 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
3008}
3009
3010#include "table/station_land.h"
3011
3019{
3020 const auto &layouts = _station_display_datas[st];
3021 if (gfx >= layouts.size()) gfx &= 1;
3022 return layouts.data() + gfx;
3023}
3024
3034bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset)
3035{
3036 bool snow_desert;
3037 switch (*ground) {
3038 case SPR_RAIL_TRACK_X:
3039 case SPR_MONO_TRACK_X:
3040 case SPR_MGLV_TRACK_X:
3041 snow_desert = false;
3042 *overlay_offset = RTO_X;
3043 break;
3044
3045 case SPR_RAIL_TRACK_Y:
3046 case SPR_MONO_TRACK_Y:
3047 case SPR_MGLV_TRACK_Y:
3048 snow_desert = false;
3049 *overlay_offset = RTO_Y;
3050 break;
3051
3052 case SPR_RAIL_TRACK_X_SNOW:
3053 case SPR_MONO_TRACK_X_SNOW:
3054 case SPR_MGLV_TRACK_X_SNOW:
3055 snow_desert = true;
3056 *overlay_offset = RTO_X;
3057 break;
3058
3059 case SPR_RAIL_TRACK_Y_SNOW:
3060 case SPR_MONO_TRACK_Y_SNOW:
3061 case SPR_MGLV_TRACK_Y_SNOW:
3062 snow_desert = true;
3063 *overlay_offset = RTO_Y;
3064 break;
3065
3066 default:
3067 return false;
3068 }
3069
3070 if (ti != nullptr) {
3071 /* Decide snow/desert from tile */
3073 case LT_ARCTIC:
3074 snow_desert = (uint)ti->z > GetSnowLine() * TILE_HEIGHT;
3075 break;
3076
3077 case LT_TROPIC:
3078 snow_desert = GetTropicZone(ti->tile) == TROPICZONE_DESERT;
3079 break;
3080
3081 default:
3082 break;
3083 }
3084 }
3085
3086 *ground = snow_desert ? SPR_FLAT_SNOW_DESERT_TILE : SPR_FLAT_GRASS_TILE;
3087 return true;
3088}
3089
3090static void DrawTile_Station(TileInfo *ti)
3091{
3092 const NewGRFSpriteLayout *layout = nullptr;
3093 DrawTileSprites tmp_rail_layout;
3094 const DrawTileSprites *t = nullptr;
3095 int32_t total_offset;
3096 const RailTypeInfo *rti = nullptr;
3097 uint32_t relocation = 0;
3098 uint32_t ground_relocation = 0;
3099 BaseStation *st = nullptr;
3100 const StationSpec *statspec = nullptr;
3101 uint tile_layout = 0;
3102
3103 if (HasStationRail(ti->tile)) {
3104 rti = GetRailTypeInfo(GetRailType(ti->tile));
3105 total_offset = rti->GetRailtypeSpriteOffset();
3106
3107 if (IsCustomStationSpecIndex(ti->tile)) {
3108 /* look for customization */
3109 st = BaseStation::GetByTile(ti->tile);
3110 statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
3111
3112 if (statspec != nullptr) {
3113 tile_layout = GetStationGfx(ti->tile);
3114
3116 uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile);
3117 if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
3118 }
3119
3120 /* Ensure the chosen tile layout is valid for this custom station */
3121 if (!statspec->renderdata.empty()) {
3122 layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
3123 if (!layout->NeedsPreprocessing()) {
3124 t = layout;
3125 layout = nullptr;
3126 }
3127 }
3128 }
3129 }
3130 } else {
3131 total_offset = 0;
3132 }
3133
3134 StationGfx gfx = GetStationGfx(ti->tile);
3135 if (IsAirport(ti->tile)) {
3136 gfx = GetAirportGfx(ti->tile);
3137 if (gfx >= NEW_AIRPORTTILE_OFFSET) {
3138 const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
3139 if (ats->grf_prop.spritegroup[0] != nullptr && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), ats)) {
3140 return;
3141 }
3142 /* No sprite group (or no valid one) found, meaning no graphics associated.
3143 * Use the substitute one instead */
3144 assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
3145 gfx = ats->grf_prop.subst_id;
3146 }
3147 switch (gfx) {
3148 case APT_RADAR_GRASS_FENCE_SW:
3149 t = &_station_display_datas_airport_radar_grass_fence_sw[GetAnimationFrame(ti->tile)];
3150 break;
3151 case APT_GRASS_FENCE_NE_FLAG:
3152 t = &_station_display_datas_airport_flag_grass_fence_ne[GetAnimationFrame(ti->tile)];
3153 break;
3154 case APT_RADAR_FENCE_SW:
3155 t = &_station_display_datas_airport_radar_fence_sw[GetAnimationFrame(ti->tile)];
3156 break;
3157 case APT_RADAR_FENCE_NE:
3158 t = &_station_display_datas_airport_radar_fence_ne[GetAnimationFrame(ti->tile)];
3159 break;
3160 case APT_GRASS_FENCE_NE_FLAG_2:
3161 t = &_station_display_datas_airport_flag_grass_fence_ne_2[GetAnimationFrame(ti->tile)];
3162 break;
3163 }
3164 }
3165
3166 Owner owner = GetTileOwner(ti->tile);
3167
3168 PaletteID palette;
3169 if (Company::IsValidID(owner)) {
3170 palette = COMPANY_SPRITE_COLOUR(owner);
3171 } else {
3172 /* Some stations are not owner by a company, namely oil rigs */
3173 palette = PALETTE_TO_GREY;
3174 }
3175
3176 if (layout == nullptr && (t == nullptr || t->seq == nullptr)) t = GetStationTileLayout(GetStationType(ti->tile), gfx);
3177
3178 /* don't show foundation for docks */
3179 if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
3180 if (statspec != nullptr && HasBit(statspec->flags, SSF_CUSTOM_FOUNDATIONS)) {
3181 /* Station has custom foundations.
3182 * Check whether the foundation continues beyond the tile's upper sides. */
3183 uint edge_info = 0;
3184 auto [slope, z] = GetFoundationPixelSlope(ti->tile);
3185 if (!HasFoundationNW(ti->tile, slope, z)) SetBit(edge_info, 0);
3186 if (!HasFoundationNE(ti->tile, slope, z)) SetBit(edge_info, 1);
3187 SpriteID image = GetCustomStationFoundationRelocation(statspec, st, ti->tile, tile_layout, edge_info);
3188 if (image == 0) goto draw_default_foundation;
3189
3190 if (HasBit(statspec->flags, SSF_EXTENDED_FOUNDATIONS)) {
3191 /* Station provides extended foundations. */
3192
3193 static const uint8_t foundation_parts[] = {
3194 0, 0, 0, 0, // Invalid, Invalid, Invalid, SLOPE_SW
3195 0, 1, 2, 3, // Invalid, SLOPE_EW, SLOPE_SE, SLOPE_WSE
3196 0, 4, 5, 6, // Invalid, SLOPE_NW, SLOPE_NS, SLOPE_NWS
3197 7, 8, 9 // SLOPE_NE, SLOPE_ENW, SLOPE_SEN
3198 };
3199
3200 AddSortableSpriteToDraw(image + foundation_parts[ti->tileh], PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
3201 } else {
3202 /* Draw simple foundations, built up from 8 possible foundation sprites. */
3203
3204 /* Each set bit represents one of the eight composite sprites to be drawn.
3205 * 'Invalid' entries will not drawn but are included for completeness. */
3206 static const uint8_t composite_foundation_parts[] = {
3207 /* Invalid (00000000), Invalid (11010001), Invalid (11100100), SLOPE_SW (11100000) */
3208 0x00, 0xD1, 0xE4, 0xE0,
3209 /* Invalid (11001010), SLOPE_EW (11001001), SLOPE_SE (11000100), SLOPE_WSE (11000000) */
3210 0xCA, 0xC9, 0xC4, 0xC0,
3211 /* Invalid (11010010), SLOPE_NW (10010001), SLOPE_NS (11100100), SLOPE_NWS (10100000) */
3212 0xD2, 0x91, 0xE4, 0xA0,
3213 /* SLOPE_NE (01001010), SLOPE_ENW (00001001), SLOPE_SEN (01000100) */
3214 0x4A, 0x09, 0x44
3215 };
3216
3217 uint8_t parts = composite_foundation_parts[ti->tileh];
3218
3219 /* If foundations continue beyond the tile's upper sides then
3220 * mask out the last two pieces. */
3221 if (HasBit(edge_info, 0)) ClrBit(parts, 6);
3222 if (HasBit(edge_info, 1)) ClrBit(parts, 7);
3223
3224 if (parts == 0) {
3225 /* We always have to draw at least one sprite to make sure there is a boundingbox and a sprite with the
3226 * correct offset for the childsprites.
3227 * So, draw the (completely empty) sprite of the default foundations. */
3228 goto draw_default_foundation;
3229 }
3230
3232 for (int i = 0; i < 8; i++) {
3233 if (HasBit(parts, i)) {
3234 AddSortableSpriteToDraw(image + i, PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
3235 }
3236 }
3238 }
3239
3240 OffsetGroundSprite(0, -8);
3242 } else {
3243draw_default_foundation:
3245 }
3246 }
3247
3248 bool draw_ground = false;
3249
3250 if (IsBuoy(ti->tile)) {
3251 DrawWaterClassGround(ti);
3252 SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
3253 if (sprite != 0) total_offset = sprite - SPR_IMG_BUOY;
3254 } else if (IsDock(ti->tile) || (IsOilRig(ti->tile) && IsTileOnWater(ti->tile))) {
3255 if (ti->tileh == SLOPE_FLAT) {
3256 DrawWaterClassGround(ti);
3257 } else {
3258 assert(IsDock(ti->tile));
3259 TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
3260 WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WATER_CLASS_INVALID;
3261 if (wc == WATER_CLASS_SEA) {
3262 DrawShoreTile(ti->tileh);
3263 } else {
3264 DrawClearLandTile(ti, 3);
3265 }
3266 }
3267 } else if (IsRoadWaypointTile(ti->tile)) {
3269 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3270 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3271 RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3272 RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
3273 const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
3274 const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
3275
3276 if (ti->tileh != SLOPE_FLAT) {
3278 }
3279
3280 DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
3281 } else {
3282 if (layout != nullptr) {
3283 /* Sprite layout which needs preprocessing */
3284 bool separate_ground = HasBit(statspec->flags, SSF_SEPARATE_GROUND);
3285 uint32_t var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, 0, separate_ground);
3286 for (uint8_t var10 : SetBitIterator(var10_values)) {
3287 uint32_t var10_relocation = GetCustomStationRelocation(statspec, st, ti->tile, var10);
3288 layout->ProcessRegisters(var10, var10_relocation, separate_ground);
3289 }
3290 tmp_rail_layout.seq = layout->GetLayout(&tmp_rail_layout.ground);
3291 t = &tmp_rail_layout;
3292 total_offset = 0;
3293 } else if (statspec != nullptr) {
3294 /* Simple sprite layout */
3295 ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
3296 if (HasBit(statspec->flags, SSF_SEPARATE_GROUND)) {
3297 ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
3298 }
3299 ground_relocation += rti->fallback_railtype;
3300 }
3301
3302 draw_ground = true;
3303 }
3304
3305 if (draw_ground && !IsAnyRoadStop(ti->tile)) {
3306 SpriteID image = t->ground.sprite;
3307 PaletteID pal = t->ground.pal;
3308 RailTrackOffset overlay_offset;
3309 if (rti != nullptr && rti->UsesOverlay() && SplitGroundSpriteForOverlay(ti, &image, &overlay_offset)) {
3310 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
3311 DrawGroundSprite(image, PAL_NONE);
3312 DrawGroundSprite(ground + overlay_offset, PAL_NONE);
3313
3314 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
3315 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
3316 DrawGroundSprite(overlay + overlay_offset, PALETTE_CRASH);
3317 }
3318 } else {
3319 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3320 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3321 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3322
3323 /* PBS debugging, draw reserved tracks darker */
3324 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
3325 DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
3326 }
3327 }
3328 }
3329
3331
3332 if (IsAnyRoadStop(ti->tile)) {
3333 RoadType road_rt = GetRoadTypeRoad(ti->tile);
3334 RoadType tram_rt = GetRoadTypeTram(ti->tile);
3335 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
3336 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
3337
3338 StationGfx view = GetStationGfx(ti->tile);
3339 StationType type = GetStationType(ti->tile);
3340
3341 const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
3342 RoadStopDrawMode stop_draw_mode{};
3343 if (stopspec != nullptr) {
3344 stop_draw_mode = stopspec->draw_mode;
3345 st = BaseStation::GetByTile(ti->tile);
3346 RoadStopResolverObject object(stopspec, st, ti->tile, INVALID_ROADTYPE, type, view);
3347 const SpriteGroup *group = object.Resolve();
3348 if (group != nullptr && group->type == SGT_TILELAYOUT) {
3349 if (HasBit(stopspec->flags, RSF_DRAW_MODE_REGISTER)) {
3350 stop_draw_mode = static_cast<RoadStopDrawMode>(GetRegister(0x100));
3351 }
3352 if (type == STATION_ROADWAYPOINT && (stop_draw_mode & ROADSTOP_DRAW_MODE_WAYP_GROUND)) {
3353 draw_ground = true;
3354 }
3355 t = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr);
3356 }
3357 }
3358
3359 /* Draw ground sprite */
3360 if (draw_ground) {
3361 SpriteID image = t->ground.sprite;
3362 PaletteID pal = t->ground.pal;
3363 image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
3364 if (GB(image, 0, SPRITE_WIDTH) != 0) {
3365 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
3366 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
3367 }
3368 }
3369
3370 if (IsDriveThroughStopTile(ti->tile)) {
3371 if (type != STATION_ROADWAYPOINT && (stopspec == nullptr || (stop_draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) != 0)) {
3372 uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
3373 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
3374 }
3375 } else {
3376 /* Non-drivethrough road stops are only valid for roads. */
3377 assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
3378
3379 if ((stopspec == nullptr || (stop_draw_mode & ROADSTOP_DRAW_MODE_ROAD) != 0) && road_rti->UsesOverlay()) {
3380 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
3381 DrawGroundSprite(ground + view, PAL_NONE);
3382 }
3383 }
3384
3385 if (stopspec == nullptr || !HasBit(stopspec->flags, RSF_NO_CATENARY)) {
3386 /* Draw road, tram catenary */
3387 DrawRoadCatenary(ti);
3388 }
3389 }
3390
3391 if (IsRailWaypoint(ti->tile)) {
3392 /* Don't offset the waypoint graphics; they're always the same. */
3393 total_offset = 0;
3394 }
3395
3396 DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
3397}
3398
3399void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
3400{
3401 int32_t total_offset = 0;
3402 PaletteID pal = COMPANY_SPRITE_COLOUR(_local_company);
3403 const DrawTileSprites *t = GetStationTileLayout(st, image);
3404 const RailTypeInfo *railtype_info = nullptr;
3405
3406 if (railtype != INVALID_RAILTYPE) {
3407 railtype_info = GetRailTypeInfo(railtype);
3408 total_offset = railtype_info->GetRailtypeSpriteOffset();
3409 }
3410
3411 SpriteID img = t->ground.sprite;
3412 RailTrackOffset overlay_offset;
3413 if (railtype_info != nullptr && railtype_info->UsesOverlay() && SplitGroundSpriteForOverlay(nullptr, &img, &overlay_offset)) {
3414 SpriteID ground = GetCustomRailSprite(railtype_info, INVALID_TILE, RTSG_GROUND);
3415 DrawSprite(img, PAL_NONE, x, y);
3416 DrawSprite(ground + overlay_offset, PAL_NONE, x, y);
3417 } else {
3418 DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
3419 }
3420
3421 if (roadtype != INVALID_ROADTYPE) {
3422 const RoadTypeInfo *roadtype_info = GetRoadTypeInfo(roadtype);
3423 if (image >= 4) {
3424 /* Drive-through stop */
3425 uint sprite_offset = 5 - image;
3426
3427 /* Road underlay takes precedence over tram */
3428 if (roadtype_info->UsesOverlay()) {
3429 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_GROUND);
3430 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
3431
3432 SpriteID overlay = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_OVERLAY);
3433 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
3434 } else if (RoadTypeIsTram(roadtype)) {
3435 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
3436 }
3437 } else {
3438 /* Bay stop */
3439 if (RoadTypeIsRoad(roadtype) && roadtype_info->UsesOverlay()) {
3440 SpriteID ground = GetCustomRoadSprite(roadtype_info, INVALID_TILE, ROTSG_ROADSTOP);
3441 DrawSprite(ground + image, PAL_NONE, x, y);
3442 }
3443 }
3444 }
3445
3446 /* Default waypoint has no railtype specific sprites */
3447 DrawRailTileSeqInGUI(x, y, t, (st == STATION_WAYPOINT || st == STATION_ROADWAYPOINT) ? 0 : total_offset, 0, pal);
3448}
3449
3450static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)
3451{
3452 return GetTileMaxPixelZ(tile);
3453}
3454
3455static Foundation GetFoundation_Station(TileIndex, Slope tileh)
3456{
3457 return FlatteningFoundation(tileh);
3458}
3459
3460static void FillTileDescRoadStop(TileIndex tile, TileDesc *td)
3461{
3462 RoadType road_rt = GetRoadTypeRoad(tile);
3463 RoadType tram_rt = GetRoadTypeTram(tile);
3464 Owner road_owner = INVALID_OWNER;
3465 Owner tram_owner = INVALID_OWNER;
3466 if (road_rt != INVALID_ROADTYPE) {
3467 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
3468 td->roadtype = rti->strings.name;
3469 td->road_speed = rti->max_speed / 2;
3470 road_owner = GetRoadOwner(tile, RTT_ROAD);
3471 }
3472
3473 if (tram_rt != INVALID_ROADTYPE) {
3474 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
3475 td->tramtype = rti->strings.name;
3476 td->tram_speed = rti->max_speed / 2;
3477 tram_owner = GetRoadOwner(tile, RTT_TRAM);
3478 }
3479
3480 if (IsDriveThroughStopTile(tile)) {
3481 /* Is there a mix of owners? */
3482 if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
3483 (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
3484 uint i = 1;
3485 if (road_owner != INVALID_OWNER) {
3486 td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
3487 td->owner[i] = road_owner;
3488 i++;
3489 }
3490 if (tram_owner != INVALID_OWNER) {
3491 td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
3492 td->owner[i] = tram_owner;
3493 }
3494 }
3495 }
3496}
3497
3498void FillTileDescRailStation(TileIndex tile, TileDesc *td)
3499{
3500 const StationSpec *spec = GetStationSpec(tile);
3501
3502 if (spec != nullptr) {
3504 td->station_name = spec->name;
3505
3506 if (spec->grf_prop.HasGrfFile()) {
3507 const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grfid);
3508 td->grf = gc->GetName();
3509 }
3510 }
3511
3512 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
3513 td->rail_speed = rti->max_speed;
3514 td->railtype = rti->strings.name;
3515}
3516
3517void FillTileDescAirport(TileIndex tile, TileDesc *td)
3518{
3519 const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
3521 td->airport_name = as->name;
3522
3524 td->airport_tile_name = ats->name;
3525
3526 if (as->grf_prop.HasGrfFile()) {
3527 const GRFConfig *gc = GetGRFConfig(as->grf_prop.grfid);
3528 td->grf = gc->GetName();
3529 } else if (ats->grf_prop.HasGrfFile()) {
3530 const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grfid);
3531 td->grf = gc->GetName();
3532 }
3533}
3534
3535static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
3536{
3537 td->owner[0] = GetTileOwner(tile);
3539
3540 if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
3541 if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
3542 if (IsAirport(tile)) FillTileDescAirport(tile, td);
3543
3544 StringID str;
3545 switch (GetStationType(tile)) {
3546 default: NOT_REACHED();
3547 case STATION_RAIL: str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
3548 case STATION_AIRPORT:
3549 str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
3550 break;
3551 case STATION_TRUCK: str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
3552 case STATION_BUS: str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
3553 case STATION_OILRIG: {
3554 const Industry *i = Station::GetByTile(tile)->industry;
3555 const IndustrySpec *is = GetIndustrySpec(i->type);
3556 td->owner[0] = i->owner;
3557 str = is->name;
3558 if (is->grf_prop.HasGrfFile()) td->grf = GetGRFConfig(is->grf_prop.grfid)->GetName();
3559 break;
3560 }
3561 case STATION_DOCK: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
3562 case STATION_BUOY: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
3563 case STATION_WAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3564 case STATION_ROADWAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
3565 }
3566 td->str = str;
3567}
3568
3569
3570static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
3571{
3572 TrackBits trackbits = TRACK_BIT_NONE;
3573
3574 switch (mode) {
3575 case TRANSPORT_RAIL:
3576 if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
3577 trackbits = TrackToTrackBits(GetRailStationTrack(tile));
3578 }
3579 break;
3580
3581 case TRANSPORT_WATER:
3582 /* buoy is coded as a station, it is always on open water */
3583 if (IsBuoy(tile)) {
3584 trackbits = TRACK_BIT_ALL;
3585 /* remove tracks that connect NE map edge */
3586 if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
3587 /* remove tracks that connect NW map edge */
3588 if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
3589 }
3590 break;
3591
3592 case TRANSPORT_ROAD:
3593 if (IsAnyRoadStop(tile)) {
3594 RoadTramType rtt = (RoadTramType)sub_mode;
3595 if (!HasTileRoadType(tile, rtt)) break;
3596
3597 if (IsBayRoadStopTile(tile)) {
3598 DiagDirection dir = GetBayRoadStopDir(tile);
3599 if (side != INVALID_DIAGDIR && dir != side) break;
3600 trackbits = DiagDirToDiagTrackBits(dir);
3601 } else {
3602 Axis axis = GetDriveThroughStopAxis(tile);
3603 if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
3604 trackbits = AxisToTrackBits(axis);
3605 }
3606 }
3607 break;
3608
3609 default:
3610 break;
3611 }
3612
3614}
3615
3616
3617static void TileLoop_Station(TileIndex tile)
3618{
3619 /* FIXME -- GetTileTrackStatus_Station -> animated stationtiles
3620 * hardcoded.....not good */
3621 switch (GetStationType(tile)) {
3622 case STATION_AIRPORT:
3623 AirportTileAnimationTrigger(Station::GetByTile(tile), tile, AAT_TILELOOP);
3624 break;
3625
3626 case STATION_DOCK:
3627 if (!IsTileFlat(tile)) break; // only handle water part
3628 [[fallthrough]];
3629
3630 case STATION_OILRIG: //(station part)
3631 case STATION_BUOY:
3632 TileLoop_Water(tile);
3633 break;
3634
3635 case STATION_ROADWAYPOINT: {
3637 case LT_ARCTIC:
3638 if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
3640 MarkTileDirtyByTile(tile);
3641 }
3642 break;
3643
3644 case LT_TROPIC:
3647 MarkTileDirtyByTile(tile);
3648 }
3649 break;
3650
3651 default: break;
3652 }
3653
3654 HouseZonesBits new_zone = HZB_TOWN_EDGE;
3655 const Town *t = ClosestTownFromTile(tile, UINT_MAX);
3656 if (t != nullptr) {
3657 new_zone = GetTownRadiusGroup(t, tile);
3658 }
3659
3660 /* Adjust road ground type depending on 'new_zone' */
3661 Roadside new_rs = new_zone > HZB_TOWN_EDGE ? ROADSIDE_PAVED : ROADSIDE_GRASS;
3662 Roadside cur_rs = GetRoadWaypointRoadside(tile);
3663
3664 if (new_rs != cur_rs) {
3665 SetRoadWaypointRoadside(tile, cur_rs == ROADSIDE_BARREN ? new_rs : ROADSIDE_BARREN);
3666 MarkTileDirtyByTile(tile);
3667 }
3668 break;
3669 }
3670
3671 default: break;
3672 }
3673}
3674
3675
3676static void AnimateTile_Station(TileIndex tile)
3677{
3678 if (HasStationRail(tile)) {
3679 AnimateStationTile(tile);
3680 return;
3681 }
3682
3683 if (IsAirport(tile)) {
3684 AnimateAirportTile(tile);
3685 return;
3686 }
3687
3688 if (IsAnyRoadStopTile(tile)) {
3689 AnimateRoadStopTile(tile);
3690 return;
3691 }
3692}
3693
3694
3695static bool ClickTile_Station(TileIndex tile)
3696{
3697 const BaseStation *bst = BaseStation::GetByTile(tile);
3698
3699 if (bst->facilities & FACIL_WAYPOINT) {
3701 } else if (IsHangar(tile)) {
3702 const Station *st = Station::From(bst);
3704 } else {
3706 }
3707 return true;
3708}
3709
3710static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
3711{
3712 if (v->type == VEH_TRAIN) {
3713 StationID station_id = GetStationIndex(tile);
3714 if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
3715 if (!IsRailStation(tile) || !v->IsFrontEngine()) return VETSB_CONTINUE;
3716
3717 int station_ahead;
3718 int station_length;
3719 int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
3720
3721 /* Stop whenever that amount of station ahead + the distance from the
3722 * begin of the platform to the stop location is longer than the length
3723 * of the platform. Station ahead 'includes' the current tile where the
3724 * vehicle is on, so we need to subtract that. */
3725 if (stop + station_ahead - (int)TILE_SIZE >= station_length) return VETSB_CONTINUE;
3726
3728
3729 x &= 0xF;
3730 y &= 0xF;
3731
3732 if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
3733 if (y == TILE_SIZE / 2) {
3734 if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
3735 stop &= TILE_SIZE - 1;
3736
3737 if (x == stop) {
3738 return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
3739 } else if (x < stop) {
3741 uint16_t spd = std::max(0, (stop - x) * 20 - 15);
3742 if (spd < v->cur_speed) v->cur_speed = spd;
3743 }
3744 }
3745 } else if (v->type == VEH_ROAD) {
3747 if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
3748 if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
3749 /* Attempt to allocate a parking bay in a road stop */
3751 }
3752 }
3753 }
3754
3755 return VETSB_CONTINUE;
3756}
3757
3763{
3764 /* Collect cargoes accepted since the last big tick. */
3765 CargoTypes cargoes = 0;
3766 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
3767 if (HasBit(st->goods[cid].status, GoodsEntry::GES_ACCEPTED_BIGTICK)) SetBit(cargoes, cid);
3768 }
3769
3770 /* Anything to do? */
3771 if (cargoes == 0) return;
3772
3773 /* Loop over all houses in the catchment. */
3775 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3776 if (IsTileType(tile, MP_HOUSE)) {
3777 WatchedCargoCallback(tile, cargoes);
3778 }
3779 }
3780}
3781
3789{
3790 if (!st->IsInUse()) {
3791 if (++st->delete_ctr >= 8) delete st;
3792 return false;
3793 }
3794
3795 if (Station::IsExpected(st)) {
3797
3798 for (GoodsEntry &ge : Station::From(st)->goods) {
3800 }
3801 }
3802
3803
3805
3806 return true;
3807}
3808
3809static inline void byte_inc_sat(uint8_t *p)
3810{
3811 uint8_t b = *p + 1;
3812 if (b != 0) *p = b;
3813}
3814
3821static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UINT_MAX)
3822{
3823 /* If truncating also punish the source stations' ratings to
3824 * decrease the flow of incoming cargo. */
3825
3826 StationCargoAmountMap waiting_per_source;
3827 ge->cargo.Truncate(amount, &waiting_per_source);
3828 for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) {
3829 Station *source_station = Station::GetIfValid(i->first);
3830 if (source_station == nullptr) continue;
3831
3832 GoodsEntry &source_ge = source_station->goods[cs->Index()];
3833 source_ge.max_waiting_cargo = std::max(source_ge.max_waiting_cargo, i->second);
3834 }
3835}
3836
3837static void UpdateStationRating(Station *st)
3838{
3839 bool waiting_changed = false;
3840
3841 byte_inc_sat(&st->time_since_load);
3842 byte_inc_sat(&st->time_since_unload);
3843
3844 for (const CargoSpec *cs : CargoSpec::Iterate()) {
3845 GoodsEntry *ge = &st->goods[cs->Index()];
3846 /* Slowly increase the rating back to its original level in the case we
3847 * didn't deliver cargo yet to this station. This happens when a bribe
3848 * failed while you didn't moved that cargo yet to a station. */
3849 if (!ge->HasRating() && ge->rating < INITIAL_STATION_RATING) {
3850 ge->rating++;
3851 }
3852
3853 /* Only change the rating if we are moving this cargo */
3854 if (ge->HasRating()) {
3855 byte_inc_sat(&ge->time_since_pickup);
3858 ge->last_speed = 0;
3859 TruncateCargo(cs, ge);
3860 waiting_changed = true;
3861 continue;
3862 }
3863
3864 bool skip = false;
3865 int rating = 0;
3866 uint waiting = ge->cargo.AvailableCount();
3867
3868 /* num_dests is at least 1 if there is any cargo as
3869 * INVALID_STATION is also a destination.
3870 */
3871 uint num_dests = (uint)ge->cargo.Packets()->MapSize();
3872
3873 /* Average amount of cargo per next hop, but prefer solitary stations
3874 * with only one or two next hops. They are allowed to have more
3875 * cargo waiting per next hop.
3876 * With manual cargo distribution waiting_avg = waiting / 2 as then
3877 * INVALID_STATION is the only destination.
3878 */
3879 uint waiting_avg = waiting / (num_dests + 1);
3880
3882 ge->rating = rating = MAX_STATION_RATING;
3883 skip = true;
3884 } else if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
3885 /* Perform custom station rating. If it succeeds the speed, days in transit and
3886 * waiting cargo ratings must not be executed. */
3887
3888 /* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
3889 uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
3890
3891 uint32_t var18 = ClampTo<uint8_t>(ge->time_since_pickup)
3892 | (ClampTo<uint16_t>(ge->max_waiting_cargo) << 8)
3893 | (ClampTo<uint8_t>(last_speed) << 24);
3894 /* Convert to the 'old' vehicle types */
3895 uint32_t var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
3896 uint16_t callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
3897 if (callback != CALLBACK_FAILED) {
3898 skip = true;
3899 rating = GB(callback, 0, 14);
3900
3901 /* Simulate a 15 bit signed value */
3902 if (HasBit(callback, 14)) rating -= 0x4000;
3903 }
3904 }
3905
3906 if (!skip) {
3907 int b = ge->last_speed - 85;
3908 if (b >= 0) rating += b >> 2;
3909
3910 uint8_t waittime = ge->time_since_pickup;
3911 if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
3912 if (waittime <= 21) rating += 25;
3913 if (waittime <= 12) rating += 25;
3914 if (waittime <= 6) rating += 45;
3915 if (waittime <= 3) rating += 35;
3916
3917 rating -= 90;
3918 if (ge->max_waiting_cargo <= 1500) rating += 55;
3919 if (ge->max_waiting_cargo <= 1000) rating += 35;
3920 if (ge->max_waiting_cargo <= 600) rating += 10;
3921 if (ge->max_waiting_cargo <= 300) rating += 20;
3922 if (ge->max_waiting_cargo <= 100) rating += 10;
3923 }
3924
3925 if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
3926
3927 uint8_t age = ge->last_age;
3928 if (age < 3) rating += 10;
3929 if (age < 2) rating += 10;
3930 if (age < 1) rating += 13;
3931
3932 {
3933 int or_ = ge->rating; // old rating
3934
3935 /* only modify rating in steps of -2, -1, 0, 1 or 2 */
3936 ge->rating = rating = or_ + Clamp(ClampTo<uint8_t>(rating) - or_, -2, 2);
3937
3938 /* if rating is <= 64 and more than 100 items waiting on average per destination,
3939 * remove some random amount of goods from the station */
3940 if (rating <= 64 && waiting_avg >= 100) {
3941 int dec = Random() & 0x1F;
3942 if (waiting_avg < 200) dec &= 7;
3943 waiting -= (dec + 1) * num_dests;
3944 waiting_changed = true;
3945 }
3946
3947 /* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
3948 if (rating <= 127 && waiting != 0) {
3949 uint32_t r = Random();
3950 if (rating <= (int)GB(r, 0, 7)) {
3951 /* Need to have int, otherwise it will just overflow etc. */
3952 waiting = std::max((int)waiting - (int)((GB(r, 8, 2) - 1) * num_dests), 0);
3953 waiting_changed = true;
3954 }
3955 }
3956
3957 /* At some point we really must cap the cargo. Previously this
3958 * was a strict 4095, but now we'll have a less strict, but
3959 * increasingly aggressive truncation of the amount of cargo. */
3960 static const uint WAITING_CARGO_THRESHOLD = 1 << 12;
3961 static const uint WAITING_CARGO_CUT_FACTOR = 1 << 6;
3962 static const uint MAX_WAITING_CARGO = 1 << 15;
3963
3964 if (waiting > WAITING_CARGO_THRESHOLD) {
3965 uint difference = waiting - WAITING_CARGO_THRESHOLD;
3966 waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
3967
3968 waiting = std::min(waiting, MAX_WAITING_CARGO);
3969 waiting_changed = true;
3970 }
3971
3972 /* We can't truncate cargo that's already reserved for loading.
3973 * Thus StoredCount() here. */
3974 if (waiting_changed && waiting < ge->cargo.AvailableCount()) {
3975 /* Feed back the exact own waiting cargo at this station for the
3976 * next rating calculation. */
3977 ge->max_waiting_cargo = 0;
3978
3979 TruncateCargo(cs, ge, ge->cargo.AvailableCount() - waiting);
3980 } else {
3981 /* If the average number per next hop is low, be more forgiving. */
3982 ge->max_waiting_cargo = waiting_avg;
3983 }
3984 }
3985 }
3986 }
3987
3988 StationID index = st->index;
3989 if (waiting_changed) {
3990 SetWindowDirty(WC_STATION_VIEW, index); // update whole window
3991 } else {
3992 SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
3993 }
3994}
3995
4004void RerouteCargo(Station *st, CargoID c, StationID avoid, StationID avoid2)
4005{
4006 GoodsEntry &ge = st->goods[c];
4007
4008 /* Reroute cargo in station. */
4009 ge.cargo.Reroute(UINT_MAX, &ge.cargo, avoid, avoid2, &ge);
4010
4011 /* Reroute cargo staged to be transferred. */
4012 for (Vehicle *v : st->loading_vehicles) {
4013 for (Vehicle *u = v; u != nullptr; u = u->Next()) {
4014 if (u->cargo_type != c) continue;
4015 u->cargo.Reroute(UINT_MAX, &u->cargo, avoid, avoid2, &ge);
4016 }
4017 }
4018}
4019
4029{
4030 for (CargoID c = 0; c < NUM_CARGO; ++c) {
4031 const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(c) != DT_MANUAL);
4032 GoodsEntry &ge = from->goods[c];
4034 if (lg == nullptr) continue;
4035 std::vector<NodeID> to_remove{};
4036 for (Edge &edge : (*lg)[ge.node].edges) {
4037 Station *to = Station::Get((*lg)[edge.dest_node].station);
4038 assert(to->goods[c].node == edge.dest_node);
4039 assert(TimerGameEconomy::date >= edge.LastUpdate());
4040 auto timeout = TimerGameEconomy::Date(LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3));
4041 if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
4042 bool updated = false;
4043
4044 if (auto_distributed) {
4045 /* Have all vehicles refresh their next hops before deciding to
4046 * remove the node. */
4047 std::vector<Vehicle *> vehicles;
4048 for (OrderList *l : OrderList::Iterate()) {
4049 bool found_from = false;
4050 bool found_to = false;
4051 for (Order *order = l->GetFirstOrder(); order != nullptr; order = order->next) {
4052 if (!order->IsType(OT_GOTO_STATION) && !order->IsType(OT_IMPLICIT)) continue;
4053 if (order->GetDestination() == from->index) {
4054 found_from = true;
4055 if (found_to) break;
4056 } else if (order->GetDestination() == to->index) {
4057 found_to = true;
4058 if (found_from) break;
4059 }
4060 }
4061 if (!found_to || !found_from) continue;
4062 vehicles.push_back(l->GetFirstSharedVehicle());
4063 }
4064
4065 auto iter = vehicles.begin();
4066 while (iter != vehicles.end()) {
4067 Vehicle *v = *iter;
4068 /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
4070 LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
4071 }
4072 if (edge.LastUpdate() == TimerGameEconomy::date) {
4073 updated = true;
4074 break;
4075 }
4076
4077 Vehicle *next_shared = v->NextShared();
4078 if (next_shared) {
4079 *iter = next_shared;
4080 ++iter;
4081 } else {
4082 iter = vehicles.erase(iter);
4083 }
4084
4085 if (iter == vehicles.end()) iter = vehicles.begin();
4086 }
4087 }
4088
4089 if (!updated) {
4090 /* If it's still considered dead remove it. */
4091 to_remove.emplace_back(to->goods[c].node);
4092 ge.flows.DeleteFlows(to->index);
4093 RerouteCargo(from, c, to->index, from->index);
4094 }
4095 } else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
4096 edge.Restrict();
4097 ge.flows.RestrictFlows(to->index);
4098 RerouteCargo(from, c, to->index, from->index);
4099 } else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
4100 edge.Release();
4101 }
4102 }
4103 /* Remove dead edges. */
4104 for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
4105
4106 assert(TimerGameEconomy::date >= lg->LastCompression());
4108 lg->Compress();
4109 }
4110 }
4111}
4112
4122void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateMode mode)
4123{
4124 GoodsEntry &ge1 = st->goods[cargo];
4125 Station *st2 = Station::Get(next_station_id);
4126 GoodsEntry &ge2 = st2->goods[cargo];
4127 LinkGraph *lg = nullptr;
4128 if (ge1.link_graph == INVALID_LINK_GRAPH) {
4129 if (ge2.link_graph == INVALID_LINK_GRAPH) {
4131 lg = new LinkGraph(cargo);
4133 ge2.link_graph = lg->index;
4134 ge2.node = lg->AddNode(st2);
4135 } else {
4136 Debug(misc, 0, "Can't allocate link graph");
4137 }
4138 } else {
4139 lg = LinkGraph::Get(ge2.link_graph);
4140 }
4141 if (lg) {
4142 ge1.link_graph = lg->index;
4143 ge1.node = lg->AddNode(st);
4144 }
4145 } else if (ge2.link_graph == INVALID_LINK_GRAPH) {
4146 lg = LinkGraph::Get(ge1.link_graph);
4147 ge2.link_graph = lg->index;
4148 ge2.node = lg->AddNode(st2);
4149 } else {
4150 lg = LinkGraph::Get(ge1.link_graph);
4151 if (ge1.link_graph != ge2.link_graph) {
4153 if (lg->Size() < lg2->Size()) {
4155 lg2->Merge(lg); // Updates GoodsEntries of lg
4156 lg = lg2;
4157 } else {
4159 lg->Merge(lg2); // Updates GoodsEntries of lg2
4160 }
4161 }
4162 }
4163 if (lg != nullptr) {
4164 (*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, mode);
4165 }
4166}
4167
4174void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, uint32_t time)
4175{
4176 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
4177 if (v->refit_cap > 0) {
4178 /* The cargo count can indeed be higher than the refit_cap if
4179 * wagons have been auto-replaced and subsequently auto-
4180 * refitted to a higher capacity. The cargo gets redistributed
4181 * among the wagons in that case.
4182 * As usage is not such an important figure anyway we just
4183 * ignore the additional cargo then.*/
4184 IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
4185 std::min<uint>(v->refit_cap, v->cargo.StoredCount()), time, EUM_INCREASE);
4186 }
4187 }
4188}
4189
4190/* called for every station each tick */
4191static void StationHandleSmallTick(BaseStation *st)
4192{
4193 if ((st->facilities & FACIL_WAYPOINT) != 0 || !st->IsInUse()) return;
4194
4195 uint8_t b = st->delete_ctr + 1;
4196 if (b >= Ticks::STATION_RATING_TICKS) b = 0;
4197 st->delete_ctr = b;
4198
4199 if (b == 0) UpdateStationRating(Station::From(st));
4200}
4201
4202void OnTick_Station()
4203{
4204 if (_game_mode == GM_EDITOR) return;
4205
4206 for (BaseStation *st : BaseStation::Iterate()) {
4207 StationHandleSmallTick(st);
4208
4209 /* Clean up the link graph about once a week. */
4212 };
4213
4214 /* Spread out big-tick over STATION_ACCEPTANCE_TICKS ticks. */
4216 /* Stop processing this station if it was deleted */
4217 if (!StationHandleBigTick(st)) continue;
4218 }
4219
4220 /* Spread out station animation over STATION_ACCEPTANCE_TICKS ticks. */
4222 TriggerStationAnimation(st, st->xy, SAT_250_TICKS);
4223 TriggerRoadStopAnimation(st, st->xy, SAT_250_TICKS);
4224 if (Station::IsExpected(st)) AirportAnimationTrigger(Station::From(st), AAT_STATION_250_TICKS);
4225 }
4226 }
4227}
4228
4230static IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto)
4231{
4232 for (Station *st : Station::Iterate()) {
4233 for (GoodsEntry &ge : st->goods) {
4236 }
4237 }
4238});
4239
4240void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
4241{
4242 ForAllStationsRadius(tile, radius, [&](Station *st) {
4243 if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
4244 for (GoodsEntry &ge : st->goods) {
4245 if (ge.status != 0) {
4246 ge.rating = ClampTo<uint8_t>(ge.rating + amount);
4247 }
4248 }
4249 }
4250 });
4251}
4252
4253static uint UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceType source_type, SourceID source_id)
4254{
4255 /* We can't allocate a CargoPacket? Then don't do anything
4256 * at all; i.e. just discard the incoming cargo. */
4257 if (!CargoPacket::CanAllocateItem()) return 0;
4258
4259 GoodsEntry &ge = st->goods[type];
4260 amount += ge.amount_fract;
4261 ge.amount_fract = GB(amount, 0, 8);
4262
4263 amount >>= 8;
4264 /* No new "real" cargo item yet. */
4265 if (amount == 0) return 0;
4266
4267 StationID next = ge.GetVia(st->index);
4268 ge.cargo.Append(new CargoPacket(st->index, amount, source_type, source_id), next);
4269 LinkGraph *lg = nullptr;
4270 if (ge.link_graph == INVALID_LINK_GRAPH) {
4272 lg = new LinkGraph(type);
4274 ge.link_graph = lg->index;
4275 ge.node = lg->AddNode(st);
4276 } else {
4277 Debug(misc, 0, "Can't allocate link graph");
4278 }
4279 } else {
4280 lg = LinkGraph::Get(ge.link_graph);
4281 }
4282 if (lg != nullptr) (*lg)[ge.node].UpdateSupply(amount);
4283
4284 if (!ge.HasRating()) {
4287 }
4288
4290 TriggerStationAnimation(st, st->xy, SAT_NEW_CARGO, type);
4291 AirportAnimationTrigger(st, AAT_STATION_NEW_CARGO, type);
4292 TriggerRoadStopRandomisation(st, st->xy, RSRT_NEW_CARGO, type);
4293 TriggerRoadStopAnimation(st, st->xy, SAT_NEW_CARGO, type);
4294
4295
4297 st->MarkTilesDirty(true);
4298 return amount;
4299}
4300
4301static bool IsUniqueStationName(const std::string &name)
4302{
4303 for (const Station *st : Station::Iterate()) {
4304 if (!st->name.empty() && st->name == name) return false;
4305 }
4306
4307 return true;
4308}
4309
4317CommandCost CmdRenameStation(DoCommandFlag flags, StationID station_id, const std::string &text)
4318{
4319 Station *st = Station::GetIfValid(station_id);
4320 if (st == nullptr) return CMD_ERROR;
4321
4322 CommandCost ret = CheckOwnership(st->owner);
4323 if (ret.Failed()) return ret;
4324
4325 bool reset = text.empty();
4326
4327 if (!reset) {
4329 if (!IsUniqueStationName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
4330 }
4331
4332 if (flags & DC_EXEC) {
4333 st->cached_name.clear();
4334 if (reset) {
4335 st->name.clear();
4336 } else {
4337 st->name = text;
4338 }
4339
4340 st->UpdateVirtCoord();
4342 }
4343
4344 return CommandCost();
4345}
4346
4347static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby)
4348{
4349 for (Station *st : nearby) {
4350 if (st->TileIsInCatchment(tile)) stations.insert(st);
4351 }
4352}
4353
4359{
4360 if (this->tile != INVALID_TILE) {
4361 if (IsTileType(this->tile, MP_HOUSE)) {
4362 /* Town nearby stations need to be filtered per tile. */
4363 assert(this->w == 1 && this->h == 1);
4364 AddNearbyStationsByCatchment(this->tile, this->stations, Town::GetByTile(this->tile)->stations_near);
4365 } else {
4366 ForAllStationsAroundTiles(*this, [this](Station *st, TileIndex) {
4367 this->stations.insert(st);
4368 return true;
4369 });
4370 }
4371 this->tile = INVALID_TILE;
4372 }
4373 return this->stations;
4374}
4375
4376
4377static bool CanMoveGoodsToStation(const Station *st, CargoID type)
4378{
4379 /* Is the station reserved exclusively for somebody else? */
4380 if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
4381
4382 /* Lowest possible rating, better not to give cargo anymore. */
4383 if (st->goods[type].rating == 0) return false;
4384
4385 /* Selectively servicing stations, and not this one. */
4386 if (_settings_game.order.selectgoods && !st->goods[type].HasVehicleEverTriedLoading()) return false;
4387
4388 if (IsCargoInClass(type, CC_PASSENGERS)) {
4389 /* Passengers are never served by just a truck stop. */
4390 if (st->facilities == FACIL_TRUCK_STOP) return false;
4391 } else {
4392 /* Non-passengers are never served by just a bus stop. */
4393 if (st->facilities == FACIL_BUS_STOP) return false;
4394 }
4395 return true;
4396}
4397
4398uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList &all_stations, Owner exclusivity)
4399{
4400 /* Return if nothing to do. Also the rounding below fails for 0. */
4401 if (all_stations.empty()) return 0;
4402 if (amount == 0) return 0;
4403
4404 Station *first_station = nullptr;
4405 typedef std::pair<Station *, uint> StationInfo;
4406 std::vector<StationInfo> used_stations;
4407
4408 for (Station *st : all_stations) {
4409 if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
4410 if (!CanMoveGoodsToStation(st, type)) continue;
4411
4412 /* Avoid allocating a vector if there is only one station to significantly
4413 * improve performance in this common case. */
4414 if (first_station == nullptr) {
4415 first_station = st;
4416 continue;
4417 }
4418 if (used_stations.empty()) {
4419 used_stations.reserve(2);
4420 used_stations.emplace_back(first_station, 0);
4421 }
4422 used_stations.emplace_back(st, 0);
4423 }
4424
4425 /* no stations around at all? */
4426 if (first_station == nullptr) return 0;
4427
4428 if (used_stations.empty()) {
4429 /* only one station around */
4430 amount *= first_station->goods[type].rating + 1;
4431 return UpdateStationWaiting(first_station, type, amount, source_type, source_id);
4432 }
4433
4434 uint company_best[OWNER_NONE + 1] = {}; // best rating for each company, including OWNER_NONE
4435 uint company_sum[OWNER_NONE + 1] = {}; // sum of ratings for each company
4436 uint best_rating = 0;
4437 uint best_sum = 0; // sum of best ratings for each company
4438
4439 for (auto &p : used_stations) {
4440 auto owner = p.first->owner;
4441 auto rating = p.first->goods[type].rating;
4442 if (rating > company_best[owner]) {
4443 best_sum += rating - company_best[owner]; // it's usually faster than iterating companies later
4444 company_best[owner] = rating;
4445 if (rating > best_rating) best_rating = rating;
4446 }
4447 company_sum[owner] += rating;
4448 }
4449
4450 /* From now we'll calculate with fractional cargo amounts.
4451 * First determine how much cargo we really have. */
4452 amount *= best_rating + 1;
4453
4454 uint moving = 0;
4455 for (auto &p : used_stations) {
4456 uint owner = p.first->owner;
4457 /* Multiply the amount by (company best / sum of best for each company) to get cargo allocated to a company
4458 * and by (station rating / sum of ratings in a company) to get the result for a single station. */
4459 p.second = amount * company_best[owner] * p.first->goods[type].rating / best_sum / company_sum[owner];
4460 moving += p.second;
4461 }
4462
4463 /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */
4464 if (amount > moving) {
4465 std::stable_sort(used_stations.begin(), used_stations.end(), [type](const StationInfo &a, const StationInfo &b) {
4466 return b.first->goods[type].rating < a.first->goods[type].rating;
4467 });
4468
4469 assert(amount - moving <= used_stations.size());
4470 for (uint i = 0; i < amount - moving; i++) {
4471 used_stations[i].second++;
4472 }
4473 }
4474
4475 uint moved = 0;
4476 for (auto &p : used_stations) {
4477 moved += UpdateStationWaiting(p.first, type, p.second, source_type, source_id);
4478 }
4479
4480 return moved;
4481}
4482
4483void UpdateStationDockingTiles(Station *st)
4484{
4485 st->docking_station.Clear();
4486
4487 /* For neutral stations, start with the industry area instead of dock area */
4488 const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
4489
4490 if (area->tile == INVALID_TILE) return;
4491
4492 int x = TileX(area->tile);
4493 int y = TileY(area->tile);
4494
4495 /* Expand the area by a tile on each side while
4496 * making sure that we remain inside the map. */
4497 int x2 = std::min<int>(x + area->w + 1, Map::SizeX());
4498 int x1 = std::max<int>(x - 1, 0);
4499
4500 int y2 = std::min<int>(y + area->h + 1, Map::SizeY());
4501 int y1 = std::max<int>(y - 1, 0);
4502
4503 TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
4504 for (TileIndex tile : ta) {
4505 if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
4506 }
4507}
4508
4509void BuildOilRig(TileIndex tile)
4510{
4511 if (!Station::CanAllocateItem()) {
4512 Debug(misc, 0, "Can't allocate station for oilrig at 0x{:X}, reverting to oilrig only", tile);
4513 return;
4514 }
4515
4516 Station *st = new Station(tile);
4517 _station_kdtree.Insert(st->index);
4518 st->town = ClosestTownFromTile(tile, UINT_MAX);
4519
4520 st->string_id = GenerateStationName(st, tile, STATIONNAMING_OILRIG);
4521
4522 assert(IsTileType(tile, MP_INDUSTRY));
4523 /* Mark industry as associated both ways */
4524 st->industry = Industry::GetByTile(tile);
4525 st->industry->neutral_station = st;
4526 DeleteAnimatedTile(tile);
4527 MakeOilrig(tile, st->index, GetWaterClass(tile));
4528
4529 st->owner = OWNER_NONE;
4530 st->airport.type = AT_OILRIG;
4531 st->airport.Add(tile);
4532 st->ship_station.Add(tile);
4535 UpdateStationDockingTiles(st);
4536
4537 st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
4538
4539 st->UpdateVirtCoord();
4540
4541 /* An industry tile has now been replaced with a station tile, this may change the overlap between station catchments and industry tiles.
4542 * Recalculate the station catchment for all stations currently in the industry's nearby list.
4543 * Clear the industry's station nearby list first because Station::RecomputeCatchment cannot remove nearby industries in this case. */
4545 StationList nearby = std::move(st->industry->stations_near);
4546 st->industry->stations_near.clear();
4547 for (Station *near : nearby) {
4548 near->RecomputeCatchment(true);
4549 UpdateStationAcceptance(near, true);
4550 }
4551 }
4552
4553 st->RecomputeCatchment();
4554 UpdateStationAcceptance(st, false);
4555}
4556
4557void DeleteOilRig(TileIndex tile)
4558{
4559 Station *st = Station::GetByTile(tile);
4560
4561 MakeWaterKeepingClass(tile, OWNER_NONE);
4562
4563 /* The oil rig station is not supposed to be shared with anything else */
4564 assert(st->facilities == (FACIL_AIRPORT | FACIL_DOCK) && st->airport.type == AT_OILRIG);
4565 if (st->industry != nullptr && st->industry->neutral_station == st) {
4566 /* Don't leave dangling neutral station pointer */
4567 st->industry->neutral_station = nullptr;
4568 }
4569 delete st;
4570}
4571
4572static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
4573{
4574 if (IsAnyRoadStopTile(tile)) {
4575 for (RoadTramType rtt : _roadtramtypes) {
4576 /* Update all roadtypes, no matter if they are present */
4577 if (GetRoadOwner(tile, rtt) == old_owner) {
4578 RoadType rt = GetRoadType(tile, rtt);
4579 if (rt != INVALID_ROADTYPE) {
4580 /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
4581 Company::Get(old_owner)->infrastructure.road[rt] -= 2;
4582 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
4583 }
4584 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
4585 }
4586 }
4587 }
4588
4589 if (!IsTileOwner(tile, old_owner)) return;
4590
4591 if (new_owner != INVALID_OWNER) {
4592 /* Update company infrastructure counts. Only do it here
4593 * if the new owner is valid as otherwise the clear
4594 * command will do it for us. No need to dirty windows
4595 * here, we'll redraw the whole screen anyway.*/
4596 Company *old_company = Company::Get(old_owner);
4597 Company *new_company = Company::Get(new_owner);
4598
4599 /* Update counts for underlying infrastructure. */
4600 switch (GetStationType(tile)) {
4601 case STATION_RAIL:
4602 case STATION_WAYPOINT:
4603 if (!IsStationTileBlocked(tile)) {
4604 old_company->infrastructure.rail[GetRailType(tile)]--;
4605 new_company->infrastructure.rail[GetRailType(tile)]++;
4606 }
4607 break;
4608
4609 case STATION_BUS:
4610 case STATION_TRUCK:
4611 case STATION_ROADWAYPOINT:
4612 /* Road stops were already handled above. */
4613 break;
4614
4615 case STATION_BUOY:
4616 case STATION_DOCK:
4617 if (GetWaterClass(tile) == WATER_CLASS_CANAL) {
4618 old_company->infrastructure.water--;
4619 new_company->infrastructure.water++;
4620 }
4621 break;
4622
4623 default:
4624 break;
4625 }
4626
4627 /* Update station tile count. */
4628 if (!IsBuoy(tile) && !IsAirport(tile)) {
4629 old_company->infrastructure.station--;
4630 new_company->infrastructure.station++;
4631 }
4632
4633 /* for buoys, owner of tile is owner of water, st->owner == OWNER_NONE */
4634 SetTileOwner(tile, new_owner);
4636 } else {
4637 if (IsDriveThroughStopTile(tile)) {
4638 /* Remove the drive-through road stop */
4639 if (IsRoadWaypoint(tile)) {
4641 } else {
4642 Command<CMD_REMOVE_ROAD_STOP>::Do(DC_EXEC | DC_BANKRUPT, tile, 1, 1, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, false);
4643 }
4644 assert(IsTileType(tile, MP_ROAD));
4645 /* Change owner of tile and all roadtypes */
4646 ChangeTileOwner(tile, old_owner, new_owner);
4647 } else {
4649 /* Set tile owner of water under (now removed) buoy and dock to OWNER_NONE.
4650 * Update owner of buoy if it was not removed (was in orders).
4651 * Do not update when owned by OWNER_WATER (sea and rivers). */
4652 if ((IsTileType(tile, MP_WATER) || IsBuoyTile(tile)) && IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
4653 }
4654 }
4655}
4656
4666{
4667 /* Water flooding can always clear road stops. */
4668 if (_current_company == OWNER_WATER) return CommandCost();
4669
4670 CommandCost ret;
4671
4672 if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
4673 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
4674 if (tram_owner != OWNER_NONE) {
4675 ret = CheckOwnership(tram_owner);
4676 if (ret.Failed()) return ret;
4677 }
4678 }
4679
4680 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
4681 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
4682 if (road_owner == OWNER_TOWN) {
4683 ret = CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags);
4684 if (ret.Failed()) return ret;
4685 } else if (road_owner != OWNER_NONE) {
4686 ret = CheckOwnership(road_owner);
4687 if (ret.Failed()) return ret;
4688 }
4689 }
4690
4691 return CommandCost();
4692}
4693
4701{
4702 if (flags & DC_AUTO) {
4703 switch (GetStationType(tile)) {
4704 default: break;
4705 case STATION_RAIL: return CommandCost(STR_ERROR_MUST_DEMOLISH_RAILROAD);
4706 case STATION_WAYPOINT: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4707 case STATION_AIRPORT: return CommandCost(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
4708 case STATION_TRUCK: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
4709 case STATION_BUS: return CommandCost(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
4710 case STATION_ROADWAYPOINT: return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
4711 case STATION_BUOY: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY);
4712 case STATION_DOCK: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
4713 case STATION_OILRIG:
4714 SetDParam(1, STR_INDUSTRY_NAME_OIL_RIG);
4715 return CommandCost(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY);
4716 }
4717 }
4718
4719 switch (GetStationType(tile)) {
4720 case STATION_RAIL: return RemoveRailStation(tile, flags);
4721 case STATION_WAYPOINT: return RemoveRailWaypoint(tile, flags);
4722 case STATION_AIRPORT: return RemoveAirport(tile, flags);
4723 case STATION_TRUCK: [[fallthrough]];
4724 case STATION_BUS:
4725 if (IsDriveThroughStopTile(tile)) {
4726 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4727 if (remove_road.Failed()) return remove_road;
4728 }
4729 return RemoveRoadStop(tile, flags);
4730 case STATION_ROADWAYPOINT: {
4731 CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
4732 if (remove_road.Failed()) return remove_road;
4733 return RemoveRoadWaypointStop(tile, flags);
4734 }
4735 case STATION_BUOY: return RemoveBuoy(tile, flags);
4736 case STATION_DOCK: return RemoveDock(tile, flags);
4737 default: break;
4738 }
4739
4740 return CMD_ERROR;
4741}
4742
4743static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
4744{
4746 /* TODO: If you implement newgrf callback 149 'land slope check', you have to decide what to do with it here.
4747 * TTDP does not call it.
4748 */
4749 if (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
4750 switch (GetStationType(tile)) {
4751 case STATION_WAYPOINT:
4752 case STATION_RAIL: {
4753 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetRailStationAxis(tile))) break;
4754 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4755 }
4756
4757 case STATION_AIRPORT:
4758 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4759
4760 case STATION_TRUCK:
4761 case STATION_BUS:
4762 case STATION_ROADWAYPOINT: {
4763 if (IsDriveThroughStopTile(tile)) {
4764 if (!AutoslopeCheckForAxis(tile, z_new, tileh_new, GetDriveThroughStopAxis(tile))) break;
4765 } else {
4766 if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetBayRoadStopDir(tile))) break;
4767 }
4768 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
4769 }
4770
4771 default: break;
4772 }
4773 }
4774 }
4775 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
4776}
4777
4783uint FlowStat::GetShare(StationID st) const
4784{
4785 uint32_t prev = 0;
4786 for (const auto &it : this->shares) {
4787 if (it.second == st) {
4788 return it.first - prev;
4789 } else {
4790 prev = it.first;
4791 }
4792 }
4793 return 0;
4794}
4795
4802StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const
4803{
4804 if (this->unrestricted == 0) return INVALID_STATION;
4805 assert(!this->shares.empty());
4806 SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
4807 assert(it != this->shares.end() && it->first <= this->unrestricted);
4808 if (it->second != excluded && it->second != excluded2) return it->second;
4809
4810 /* We've hit one of the excluded stations.
4811 * Draw another share, from outside its range. */
4812
4813 uint end = it->first;
4814 uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
4815 uint interval = end - begin;
4816 if (interval >= this->unrestricted) return INVALID_STATION; // Only one station in the map.
4817 uint new_max = this->unrestricted - interval;
4818 uint rand = RandomRange(new_max);
4819 SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
4820 this->shares.upper_bound(rand + interval);
4821 assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
4822 if (it2->second != excluded && it2->second != excluded2) return it2->second;
4823
4824 /* We've hit the second excluded station.
4825 * Same as before, only a bit more complicated. */
4826
4827 uint end2 = it2->first;
4828 uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
4829 uint interval2 = end2 - begin2;
4830 if (interval2 >= new_max) return INVALID_STATION; // Only the two excluded stations in the map.
4831 new_max -= interval2;
4832 if (begin > begin2) {
4833 Swap(begin, begin2);
4834 Swap(end, end2);
4835 Swap(interval, interval2);
4836 }
4837 rand = RandomRange(new_max);
4838 SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
4839 if (rand < begin) {
4840 it3 = this->shares.upper_bound(rand);
4841 } else if (rand < begin2 - interval) {
4842 it3 = this->shares.upper_bound(rand + interval);
4843 } else {
4844 it3 = this->shares.upper_bound(rand + interval + interval2);
4845 }
4846 assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
4847 return it3->second;
4848}
4849
4856{
4857 assert(!this->shares.empty());
4858 SharesMap new_shares;
4859 uint i = 0;
4860 for (const auto &it : this->shares) {
4861 new_shares[++i] = it.second;
4862 if (it.first == this->unrestricted) this->unrestricted = i;
4863 }
4864 this->shares.swap(new_shares);
4865 assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
4866}
4867
4874void FlowStat::ChangeShare(StationID st, int flow)
4875{
4876 /* We assert only before changing as afterwards the shares can actually
4877 * be empty. In that case the whole flow stat must be deleted then. */
4878 assert(!this->shares.empty());
4879
4880 uint removed_shares = 0;
4881 uint added_shares = 0;
4882 uint last_share = 0;
4883 SharesMap new_shares;
4884 for (const auto &it : this->shares) {
4885 if (it.second == st) {
4886 if (flow < 0) {
4887 uint share = it.first - last_share;
4888 if (flow == INT_MIN || (uint)(-flow) >= share) {
4889 removed_shares += share;
4890 if (it.first <= this->unrestricted) this->unrestricted -= share;
4891 if (flow != INT_MIN) flow += share;
4892 last_share = it.first;
4893 continue; // remove the whole share
4894 }
4895 removed_shares += (uint)(-flow);
4896 } else {
4897 added_shares += (uint)(flow);
4898 }
4899 if (it.first <= this->unrestricted) this->unrestricted += flow;
4900
4901 /* If we don't continue above the whole flow has been added or
4902 * removed. */
4903 flow = 0;
4904 }
4905 new_shares[it.first + added_shares - removed_shares] = it.second;
4906 last_share = it.first;
4907 }
4908 if (flow > 0) {
4909 new_shares[last_share + (uint)flow] = st;
4910 if (this->unrestricted < last_share) {
4911 this->ReleaseShare(st);
4912 } else {
4913 this->unrestricted += flow;
4914 }
4915 }
4916 this->shares.swap(new_shares);
4917}
4918
4924void FlowStat::RestrictShare(StationID st)
4925{
4926 assert(!this->shares.empty());
4927 uint flow = 0;
4928 uint last_share = 0;
4929 SharesMap new_shares;
4930 for (auto &it : this->shares) {
4931 if (flow == 0) {
4932 if (it.first > this->unrestricted) return; // Not present or already restricted.
4933 if (it.second == st) {
4934 flow = it.first - last_share;
4935 this->unrestricted -= flow;
4936 } else {
4937 new_shares[it.first] = it.second;
4938 }
4939 } else {
4940 new_shares[it.first - flow] = it.second;
4941 }
4942 last_share = it.first;
4943 }
4944 if (flow == 0) return;
4945 new_shares[last_share + flow] = st;
4946 this->shares.swap(new_shares);
4947 assert(!this->shares.empty());
4948}
4949
4955void FlowStat::ReleaseShare(StationID st)
4956{
4957 assert(!this->shares.empty());
4958 uint flow = 0;
4959 uint next_share = 0;
4960 bool found = false;
4961 for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
4962 if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
4963 if (found) {
4964 flow = next_share - it->first;
4965 this->unrestricted += flow;
4966 break;
4967 } else {
4968 if (it->first == this->unrestricted) return; // !found -> Limit not hit.
4969 if (it->second == st) found = true;
4970 }
4971 next_share = it->first;
4972 }
4973 if (flow == 0) return;
4974 SharesMap new_shares;
4975 new_shares[flow] = st;
4976 for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
4977 if (it->second != st) {
4978 new_shares[flow + it->first] = it->second;
4979 } else {
4980 flow = 0;
4981 }
4982 }
4983 this->shares.swap(new_shares);
4984 assert(!this->shares.empty());
4985}
4986
4993{
4994 assert(runtime > 0);
4995 SharesMap new_shares;
4996 uint share = 0;
4997 for (auto i : this->shares) {
4998 share = std::max(share + 1, i.first * 30 / runtime);
4999 new_shares[share] = i.second;
5000 if (this->unrestricted == i.first) this->unrestricted = share;
5001 }
5002 this->shares.swap(new_shares);
5003}
5004
5011void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
5012{
5013 FlowStatMap::iterator origin_it = this->find(origin);
5014 if (origin_it == this->end()) {
5015 this->emplace(origin, FlowStat(via, flow));
5016 } else {
5017 origin_it->second.ChangeShare(via, flow);
5018 assert(!origin_it->second.GetShares()->empty());
5019 }
5020}
5021
5030void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
5031{
5032 FlowStatMap::iterator prev_it = this->find(origin);
5033 if (prev_it == this->end()) {
5034 FlowStat fs(via, flow);
5035 fs.AppendShare(INVALID_STATION, flow);
5036 this->emplace(origin, fs);
5037 } else {
5038 prev_it->second.ChangeShare(via, flow);
5039 prev_it->second.ChangeShare(INVALID_STATION, flow);
5040 assert(!prev_it->second.GetShares()->empty());
5041 }
5042}
5043
5049{
5050 for (auto &i : *this) {
5051 FlowStat &fs = i.second;
5052 uint local = fs.GetShare(INVALID_STATION);
5053 if (local > INT_MAX) { // make sure it fits in an int
5054 fs.ChangeShare(self, -INT_MAX);
5055 fs.ChangeShare(INVALID_STATION, -INT_MAX);
5056 local -= INT_MAX;
5057 }
5058 fs.ChangeShare(self, -(int)local);
5059 fs.ChangeShare(INVALID_STATION, -(int)local);
5060
5061 /* If the local share is used up there must be a share for some
5062 * remote station. */
5063 assert(!fs.GetShares()->empty());
5064 }
5065}
5066
5074{
5075 StationIDStack ret;
5076 for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
5077 FlowStat &s_flows = f_it->second;
5078 s_flows.ChangeShare(via, INT_MIN);
5079 if (s_flows.GetShares()->empty()) {
5080 ret.Push(f_it->first);
5081 this->erase(f_it++);
5082 } else {
5083 ++f_it;
5084 }
5085 }
5086 return ret;
5087}
5088
5094{
5095 for (auto &it : *this) {
5096 it.second.RestrictShare(via);
5097 }
5098}
5099
5104void FlowStatMap::ReleaseFlows(StationID via)
5105{
5106 for (auto &it : *this) {
5107 it.second.ReleaseShare(via);
5108 }
5109}
5110
5116{
5117 uint ret = 0;
5118 for (const auto &it : *this) {
5119 ret += (--(it.second.GetShares()->end()))->first;
5120 }
5121 return ret;
5122}
5123
5129uint FlowStatMap::GetFlowVia(StationID via) const
5130{
5131 uint ret = 0;
5132 for (const auto &it : *this) {
5133 ret += it.second.GetShare(via);
5134 }
5135 return ret;
5136}
5137
5143uint FlowStatMap::GetFlowFrom(StationID from) const
5144{
5145 FlowStatMap::const_iterator i = this->find(from);
5146 if (i == this->end()) return 0;
5147 return (--(i->second.GetShares()->end()))->first;
5148}
5149
5156uint FlowStatMap::GetFlowFromVia(StationID from, StationID via) const
5157{
5158 FlowStatMap::const_iterator i = this->find(from);
5159 if (i == this->end()) return 0;
5160 return i->second.GetShare(via);
5161}
5162
5163extern const TileTypeProcs _tile_type_station_procs = {
5164 DrawTile_Station, // draw_tile_proc
5165 GetSlopePixelZ_Station, // get_slope_z_proc
5166 ClearTile_Station, // clear_tile_proc
5167 nullptr, // add_accepted_cargo_proc
5168 GetTileDesc_Station, // get_tile_desc_proc
5169 GetTileTrackStatus_Station, // get_tile_track_status_proc
5170 ClickTile_Station, // click_tile_proc
5171 AnimateTile_Station, // animate_tile_proc
5172 TileLoop_Station, // tile_loop_proc
5173 ChangeTileOwner_Station, // change_tile_owner_proc
5174 nullptr, // add_produced_cargo_proc
5175 VehicleEnter_Station, // vehicle_enter_tile_proc
5176 GetFoundation_Station, // get_foundation_proc
5177 TerraformTile_Station, // terraform_tile_proc
5178};
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:207
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
@ NUM_AIRPORTS
Maximal number of airports in total.
Definition airport.h:41
@ AT_OILRIG
Oilrig airport.
Definition airport.h:38
static const uint64_t AIRPORT_CLOSED_block
Dummy block for indicating a closed airport.
Definition airport.h:128
@ FLYING
Vehicle is flying in the air.
Definition airport.h:75
Enum of the default airport tiles.
void DeleteAnimatedTile(TileIndex tile)
Stops animation on the given tile.
void AddAnimatedTile(TileIndex tile, bool mark_dirty)
Add the given tile to the animated tile table (if it does not exist yet).
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 CargoID
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
bool IsValidCargoID(CargoID t)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:107
uint16_t SourceID
Contains either industry ID, town ID or company ID (or INVALID_SOURCE)
Definition cargo_type.h:143
static const CargoID NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:74
SourceType
Types of cargo source and destination.
Definition cargo_type.h:137
bool IsCargoInClass(CargoID c, CargoClass cc)
Does cargo c have cargo class cc?
Definition cargotype.h:239
@ CC_MAIL
Mail.
Definition cargotype.h:51
@ CC_LIQUID
Liquids (Oil, Water, Rubber)
Definition cargotype.h:56
@ CC_PASSENGERS
Passengers.
Definition cargotype.h:50
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.
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?
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:347
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:127
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:137
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:231
struct RailTypeInfo::@23 base_sprites
Struct containing the main sprites.
struct RailTypeInfo::@26 strings
Strings associated with the rail type.
StringID name
Name of this rail type.
Definition rail.h:176
uint8_t fallback_railtype
Original railtype number to use when drawing non-newgrf railtypes, or when drawing stations.
Definition rail.h:201
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:295
struct RoadTypeInfo::@29 strings
Strings associated with the rail type.
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:142
StringID name
Name of this rail type.
Definition road.h:103
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.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
DoCommandFlag
List of flags for a command.
@ DC_AUTO
don't allow building on structures
@ DC_BANKRUPT
company bankrupts, skip money check, skip vehicle on tile check in some cases
@ DC_EXEC
execute the given command
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.
Owner
Enum for all companies/owners.
@ INVALID_OWNER
An invalid owner.
@ OWNER_NONE
The tile has no ownership.
@ OWNER_WATER
The tile/execution is done by "water".
@ OWNER_TOWN
A town owns the tile, or a town is expanding.
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,...)
Ouptut 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
debug_inline constexpr bool HasFlag(const T x, const T y)
Checks if a value in a bitset enum is set.
Definition enum_type.hpp:58
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:988
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:18
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:19
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:243
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 INV...
@ INDUSTRYLIFE_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.).
Some typedefs for the main game.
@ DT_MANUAL
Manual distribution. No link graph calculations are run.
EdgeUpdateMode
Special modes for updating links.
@ EUM_INCREASE
Increase capacity.
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:247
uint DistanceFromEdge(TileIndex tile)
Param the minimum distance to an edge.
Definition map.cpp:206
uint DistanceMax(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles.
Definition map.cpp:178
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:97
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:146
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:373
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:440
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:389
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:552
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:415
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:567
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).
static const uint8_t ANIM_STATUS_NO_ANIMATION
There is no animation.
@ SAT_NEW_CARGO
Trigger station on new cargo arrival.
@ SAT_BUILT
Trigger tile when built.
@ SAT_250_TICKS
Trigger station every 250 ticks.
@ CBM_STATION_DRAW_TILE_LAYOUT
Use callback to select a tile layout to use when drawing.
@ CBM_STATION_SLOPE_CHECK
Check slope of new station tiles.
@ CBM_STATION_AVAIL
Availability of station in construction window.
@ 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.
@ CBM_ROAD_STOP_AVAIL
Availability of road stop in construction window.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
@ CBM_CARGO_STATION_RATING_CALC
custom station rating for this cargo type
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.
@ ROADSTOP_DRAW_MODE_ROAD
Bay stops: Draw the road itself.
@ ROADSTOP_DRAW_MODE_OVERLAY
Drive-through stops: Draw the road overlay, e.g. pavement.
@ ROADSTOP_DRAW_MODE_WAYP_GROUND
Waypoints: Draw the sprite layout ground tile (on top of the road)
@ RSRT_NEW_CARGO
Trigger roadstop on arrival of new cargo.
bool IsWaypointClass(const RoadStopClass &cls)
Test if a RoadStopClass is the waypoint class.
RoadStopClassID
@ ROADSTOPTYPE_FREIGHT
This RoadStop is for freight (truck) stops.
@ ROADSTOPTYPE_ALL
This RoadStop is for both types of station road stops.
@ ROADSTOPTYPE_PASSENGER
This RoadStop is for passenger (bus) stops.
@ RSF_NO_CATENARY
Do not show catenary.
@ RSF_DRAW_MODE_REGISTER
Read draw mode from register 0x100.
@ RSF_DRIVE_THROUGH_ONLY
Stop is drive-through only.
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, CargoID 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.
@ SSF_SEPARATE_GROUND
Use different sprite set for ground sprites.
@ SSF_CUSTOM_FOUNDATIONS
Draw custom foundations.
@ SSF_EXTENDED_FOUNDATIONS
Extended foundation block instead of simple.
uint16_t GetStationLayoutKey(uint8_t platforms, uint8_t length)
Get the station layout key for a given station layout size.
@ SRT_NEW_CARGO
Trigger station on new cargo arrival.
StationClassID
Functions related to news.
void AddNewsItem(StringID string, NewsType type, NewsFlag flags, NewsReferenceType reftype1=NR_NONE, uint32_t ref1=UINT32_MAX, NewsReferenceType reftype2=NR_NONE, uint32_t ref2=UINT32_MAX, std::unique_ptr< NewsAllocatedData > &&data=nullptr)
Add a new newsitem to be shown.
Definition news_gui.cpp:897
@ NT_ACCEPTANCE
A type of cargo is (no longer) accepted.
Definition news_type.h:37
@ NR_STATION
Reference station. Scroll to station when clicking on the news. Delete news when station is deleted.
Definition news_type.h:56
@ NF_INCOLOUR
Bit value for coloured news.
Definition news_type.h:74
@ NF_SMALL
Small news item. (Information window with text and viewport)
Definition news_type.h:80
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
@ RTSG_GROUND
Main group of ground images.
Definition rail.h:52
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:51
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:375
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:335
RailTrackOffset
Offsets for sprites within an overlay/underlay set.
Definition rail.h:70
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:72
@ RTO_X
Piece of rail in X direction.
Definition rail.h:71
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:307
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:242
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:227
@ ROTSG_OVERLAY
Optional: Images for overlaying track.
Definition road.h:61
@ ROTSG_ROADSTOP
Required: Bay stop surface.
Definition road.h:70
@ ROTSG_GROUND
Required: Main group of ground images.
Definition road.h:62
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:252
CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadTramType rtt, DoCommandFlag flags, bool town_check)
Is it allowed to remove the given road bits from the given tile?
Definition road_cmd.cpp:262
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
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:57
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
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:647
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:592
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:99
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:89
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:168
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1605
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1545
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1535
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
Definition sprites.h:1548
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.
CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector< T * > &affected_stations, DoCommandFlag flags, Money removal_cost, bool keep_rail)
Remove a number of tiles from any rail station within the area.
CommandCost CmdOpenCloseAirport(DoCommandFlag flags, StationID station_id)
Open/close an airport to incoming aircraft.
CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
Clear a single tile of a station.
CargoTypes GetEmptyMask(const Station *st)
Get a mask of the cargo types that are empty at the station.
static CommandCost BuildStationPart(Station **st, DoCommandFlag flags, bool reuse, TileArea area, StationNaming name_class)
Common part of building various station parts and possibly attaching them to an existing one.
uint8_t GetAirportNoiseLevelForDistance(const AirportSpec *as, uint distance)
Get a possible noise reduction factor based on distance from town center.
CommandCost CmdRemoveRoadStop(DoCommandFlag flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road)
Remove bus or truck stops.
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.
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 CmdRemoveFromRoadWaypoint(DoCommandFlag flags, TileIndex start, TileIndex end)
Remove road waypoints.
void RerouteCargo(Station *st, CargoID c, StationID avoid, StationID avoid2)
Reroute cargo of type c at station st or in any vehicles unloading there.
static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_tile, int &allowed_z, DoCommandFlag 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 bool CMSAMine(TileIndex tile)
Check whether the tile is a mine.
CommandCost CmdRemoveFromRailStation(DoCommandFlag flags, TileIndex start, TileIndex end, bool keep_rail)
Remove a single tile from a rail station.
static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount=UINT_MAX)
Truncate the cargo by a specific amount.
static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCommandFlag flags)
Checks if an airport can be built at the given location and clear the area.
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 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.
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.
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 CmdBuildDock(DoCommandFlag flags, TileIndex tile, StationID station_to_join, bool adjacent)
Build a dock/haven.
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.
static bool FindNearIndustryName(TileIndex tile, void *user_data)
Find a station action 0 property 24 station name, or reduce the free_names if needed.
static CommandCost RemoveRailWaypoint(TileIndex tile, DoCommandFlag flags)
Remove a rail waypoint.
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.
static CommandCost CalculateRailStationCost(TileArea tile_area, DoCommandFlag 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.
void TriggerWatchedCargoCallbacks(Station *st)
Run the watched cargo callback for all houses in the catchment area.
CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, uint8_t airport_type, uint8_t layout, StationID station_to_join, bool allow_adjacent)
Place an Airport.
CommandCost CmdBuildRoadStop(DoCommandFlag 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.
CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlag 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 CmdRemoveFromRailWaypoint(DoCommandFlag flags, TileIndex start, TileIndex end, bool keep_rail)
Remove a single tile from a waypoint.
static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
Remove an airport.
static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
Remove a dock.
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.
void UpdateStationAcceptance(Station *st, bool show_msg)
Update the acceptance for a station.
CommandCost CmdBuildRailStation(DoCommandFlag 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 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 CmdRenameStation(DoCommandFlag flags, StationID station_id, const std::string &text)
Rename a station.
static TileIndex FindDockLandPart(TileIndex t)
Find the part of a dock that is land-based.
static CommandCost RemoveGenericRoadStop(DoCommandFlag flags, const TileArea &roadstop_area, StationType station_type, bool remove_road)
Remove a tile area of road stop or road waypoints.
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 CommandCost CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
Check if a drive-through road stop tile can be cleared.
static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
Get the acceptance of cargoes around the station in.
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).
void IncreaseStats(Station *st, CargoID 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.
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.
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index=-1)
Remove a bus station/truck stop.
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlag 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.
CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index=-1)
Remove a road waypoint.
static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject)
Add news item for when a station changes which cargoes it accepts.
CommandCost RemoveRailStation(T *st, DoCommandFlag flags, Money removal_cost)
Remove a rail station/waypoint.
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.
@ ROADSTOP_BUS
A standard stop for buses.
@ ROADSTOP_TRUCK
A standard stop for trucks.
@ ROADSTOP_END
End of valid types.
@ FACIL_DOCK
Station with a dock.
@ FACIL_BUS_STOP
Station with bus stops.
@ FACIL_AIRPORT
Station with an airport.
@ FACIL_WAYPOINT
Station is a waypoint.
@ FACIL_TRUCK_STOP
Station with truck stops.
@ FACIL_TRAIN
Station with train station.
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:359
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:257
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
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?
Definition airport.h:147
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
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.
uint64_t flags
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
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.
StationFacility facilities
The facilities that this station has.
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.
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:114
Container for cargo from the same location and time.
Definition cargopacket.h:40
Specification of a cargo type.
Definition cargotype.h:76
CargoClasses classes
Classes of this cargo type.
Definition cargotype.h:83
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition cargotype.h:139
CargoID Index() const
Determines index of this cargospec.
Definition cargotype.h:110
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat station_rating
Fix station ratings at 100%.
Definition cheat_type.h:35
GUISettings gui
settings related to the GUI
uint32_t station
Count of company owned station tiles.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
uint32_t water
Count of company owned track bits for canals.
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
bool build_on_slopes
allow building on slopes
bool road_stop_on_town_road
allow building of drive-through road stops on town owned roads
bool road_stop_on_competitor_road
allow building of drive-through road stops on roads owned by competitors
uint8_t town_council_tolerance
minimum required town ratings to be allowed to demolish stuff
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:58
const DrawTileSeqStruct * seq
Array of child sprites. Terminated with a terminator entry.
Definition sprite.h:60
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:59
bool station_noise_level
build new airports when the town noise level is still within accepted limits
Information about GRF, used in the game and (part of it) in savegames.
const char * GetName() const
Get the name of this grf.
bool HasGrfFile() const
Test if this entity was introduced by NewGRF.
uint32_t grfid
grfid that introduced this entity.
const struct GRFFile * grffile
grf file that introduced this entity
std::array< const struct SpriteGroup *, Tcnt > spritegroup
pointers to the different sprites of the entity
bool show_track_reservation
highlight reserved tracks.
uint8_t 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
Stores station stats for a single cargo.
FlowStatMap flows
Planned flows through this station.
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?
@ 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.
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.
NodeID node
ID of node in link graph referring to this goods entry.
StationCargoList cargo
The cargo packets of cargo waiting in this station.
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.
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.
IndustryLifeType life_type
This is also known as Industry production flag, in newgrf specs.
bool enabled
entity still available (by default true).newgrf can disable it, though
Defines the internal data of a functional industry.
Definition industry.h:66
IndustryType type
type of industry.
Definition industry.h:102
ProducedCargoes produced
produced cargo slots
Definition industry.h:97
Owner owner
owner of the industry. Which SHOULD always be (imho) OWNER_NONE
Definition industry.h:103
static Industry * GetByTile(TileIndex tile)
Get the industry of the given tile.
Definition industry.h:238
TileArea location
Location of the industry.
Definition industry.h:94
Station * neutral_station
Associated neutral station.
Definition industry.h:96
StationList stations_near
NOSAVE: List of nearby stations.
Definition industry.h:110
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:279
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:270
static debug_inline uint Size()
Get the size of the map.
Definition map_func.h:288
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(),...
const 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:259
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:24
PaletteID pal
The palette (use PAL_NONE) if not needed)
Definition gfx_type.h:25
Coordinates of a point in 2D.
Tindex index
Index of this pool item.
static size_t GetNumItems()
Returns number of valid items in the pool.
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static Titem * Get(size_t 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.
GRFFilePropsBase< NUM_CARGO+3 > 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
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(size_t index)
Gets station with given index.
static Station * GetIfValid(size_t index)
Returns station if the index is a valid index for this station type.
static bool IsValidID(size_t index)
Tests whether given index is a valid index for station of this 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 callback_mask
Bitmask of station callbacks that have to be called.
uint8_t disallowed_lengths
Bitmask of platform lengths available for the station.
StringID name
Name of this station.
uint8_t disallowed_platforms
Bitmask of number of platforms available for the station.
uint8_t flags
Bitmask of flags, bit 0: use different sprite set; bit 1: divide cargo about by station size.
std::unordered_map< uint16_t, std::vector< uint8_t > > layouts
Custom platform layouts, keyed by platform and length combined.
@ NoWires
Tile should NOT contain catenary wires.
@ Pylons
Tile should contain catenary pylons.
@ Blocked
Tile is blocked to vehicles.
std::vector< TileFlags > tileflags
List of tile flags.
GRFFilePropsBase< NUM_CARGO+3 > grf_prop
Properties related the the grf file.
std::vector< NewGRFSpriteLayout > renderdata
Number of tile layouts.
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...
GoodsEntry goods[NUM_CARGO]
Goods at this station.
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.
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:462
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:226
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
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
StringID airport_tile_name
Name of the airport tile.
Definition tile_cmd.h:62
Owner owner[4]
Name of the owner(s)
Definition tile_cmd.h:55
StringID owner_type[4]
Type of each owner.
Definition tile_cmd.h:56
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:158
Town data structure.
Definition town.h:54
CompanyMask statues
which companies have a statue?
Definition town.h:70
TileIndex xy
town center tile
Definition town.h:55
uint16_t noise_reached
level of noise that all the airports are generating
Definition town.h:68
uint16_t MaxTownNoise() const
Calculate the max town noise.
Definition town.h:125
CompanyID exclusivity
which company has exclusivity
Definition town.h:75
uint8_t exclusive_counter
months till the exclusivity expires
Definition town.h:76
void UpdatePosition(int center, int top, StringID str, StringID str_small=STR_NULL)
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:89
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.
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)
CargoID cargo_type
type of cargo this vehicle is carrying
Vehicle * Next() const
Get the next vehicle of this vehicle.
uint16_t refit_cap
Capacity left over from before last refit.
uint8_t vehstatus
Status.
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
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:78
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
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, DoCommandFlag 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:546
void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
Find a vehicle from a specific location.
Definition vehicle.cpp:505
@ VS_TRAIN_SLOWING
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:599
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:767
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:671
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:777
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:587
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, DoCommandFlag 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:1140
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3127
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:3219
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:3114
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3101
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:3236
@ 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.