Update: Translations from eints
[openttd-github.git] / src / newgrf_roadstop.cpp
blob66c818c5f17a9fb5b2a5fbb5f07e6adf2339eeb6
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 */
8 /** @file command.cpp Handling of NewGRF road stops. */
10 #include "stdafx.h"
11 #include "debug.h"
12 #include "station_base.h"
13 #include "roadstop_base.h"
14 #include "newgrf_roadstop.h"
15 #include "newgrf_class_func.h"
16 #include "newgrf_cargo.h"
17 #include "newgrf_roadtype.h"
18 #include "gfx_type.h"
19 #include "company_func.h"
20 #include "road.h"
21 #include "window_type.h"
22 #include "timer/timer_game_calendar.h"
23 #include "town.h"
24 #include "viewport_func.h"
25 #include "newgrf_animation_base.h"
26 #include "newgrf_sound.h"
28 #include "safeguards.h"
30 template <>
31 void RoadStopClass::InsertDefaults()
33 /* Set up initial data */
34 RoadStopClass::Get(RoadStopClass::Allocate(ROADSTOP_CLASS_LABEL_DEFAULT))->name = STR_STATION_CLASS_DFLT;
35 RoadStopClass::Get(RoadStopClass::Allocate(ROADSTOP_CLASS_LABEL_DEFAULT))->Insert(nullptr);
36 RoadStopClass::Get(RoadStopClass::Allocate(ROADSTOP_CLASS_LABEL_WAYPOINT))->name = STR_STATION_CLASS_WAYP;
37 RoadStopClass::Get(RoadStopClass::Allocate(ROADSTOP_CLASS_LABEL_WAYPOINT))->Insert(nullptr);
40 template <>
41 bool RoadStopClass::IsUIAvailable(uint) const
43 return true;
46 /* Instantiate RoadStopClass. */
47 template class NewGRFClass<RoadStopSpec, RoadStopClassID, ROADSTOP_CLASS_MAX>;
49 static const uint NUM_ROADSTOPSPECS_PER_STATION = 63; ///< Maximum number of parts per station.
51 uint32_t RoadStopScopeResolver::GetRandomBits() const
53 if (this->st == nullptr) return 0;
55 uint32_t bits = this->st->random_bits;
56 if (this->tile != INVALID_TILE && Station::IsExpected(this->st)) {
57 bits |= Station::From(this->st)->GetRoadStopRandomBits(this->tile) << 16;
59 return bits;
62 uint32_t RoadStopScopeResolver::GetTriggers() const
64 return this->st == nullptr ? 0 : this->st->waiting_triggers;
67 uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const
69 auto get_road_type_variable = [&](RoadTramType rtt) -> uint32_t {
70 RoadType rt;
71 if (this->tile == INVALID_TILE) {
72 rt = (GetRoadTramType(this->roadtype) == rtt) ? this->roadtype : INVALID_ROADTYPE;
73 } else {
74 rt = GetRoadType(this->tile, rtt);
76 if (rt == INVALID_ROADTYPE) {
77 return 0xFFFFFFFF;
78 } else {
79 return GetReverseRoadTypeTranslation(rt, this->roadstopspec->grf_prop.grffile);
83 switch (variable) {
84 /* View/rotation */
85 case 0x40: return this->view;
87 /* Stop type: 0: bus, 1: truck, 2: waypoint */
88 case 0x41:
89 if (this->type == STATION_BUS) return 0;
90 if (this->type == STATION_TRUCK) return 1;
91 return 2;
93 /* Terrain type */
94 case 0x42: return this->tile == INVALID_TILE ? 0 : (GetTileSlope(this->tile) << 8 | GetTerrainType(this->tile, TCX_NORMAL));
96 /* Road type */
97 case 0x43: return get_road_type_variable(RTT_ROAD);
99 /* Tram type */
100 case 0x44: return get_road_type_variable(RTT_TRAM);
102 /* Town zone and Manhattan distance of closest town */
103 case 0x45: {
104 if (this->tile == INVALID_TILE) return HZB_TOWN_EDGE << 16;
105 const Town *t = (this->st == nullptr) ? ClosestTownFromTile(this->tile, UINT_MAX) : this->st->town;
106 return t != nullptr ? (GetTownRadiusGroup(t, this->tile) << 16 | ClampTo<uint16_t>(DistanceManhattan(this->tile, t->xy))) : HZB_TOWN_EDGE << 16;
109 /* Get square of Euclidian distance of closest town */
110 case 0x46: {
111 if (this->tile == INVALID_TILE) return 0;
112 const Town *t = (this->st == nullptr) ? ClosestTownFromTile(this->tile, UINT_MAX) : this->st->town;
113 return t != nullptr ? DistanceSquare(this->tile, t->xy) : 0;
116 /* Company information */
117 case 0x47: return GetCompanyInfo(this->st == nullptr ? _current_company : this->st->owner);
119 /* Animation frame */
120 case 0x49: return this->tile == INVALID_TILE ? 0 : this->st->GetRoadStopAnimationFrame(this->tile);
122 /* Misc info */
123 case 0x50: {
124 uint32_t result = 0;
125 if (this->tile == INVALID_TILE) {
126 SetBit(result, 4);
128 return result;
131 /* Variables which use the parameter */
132 /* Variables 0x60 to 0x65 and 0x69 are handled separately below */
134 /* Animation frame of nearby tile */
135 case 0x66: {
136 if (this->tile == INVALID_TILE) return UINT_MAX;
137 TileIndex tile = this->tile;
138 if (parameter != 0) tile = GetNearbyTile(parameter, tile);
139 return (IsAnyRoadStopTile(tile) && GetStationIndex(tile) == this->st->index) ? this->st->GetRoadStopAnimationFrame(tile) : UINT_MAX;
142 /* Land info of nearby tile */
143 case 0x67: {
144 if (this->tile == INVALID_TILE) return 0;
145 TileIndex tile = this->tile;
146 if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required
147 return GetNearbyTileInformation(tile, this->ro.grffile->grf_version >= 8);
150 /* Road stop info of nearby tiles */
151 case 0x68: {
152 if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
153 TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
155 if (!IsAnyRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
157 uint32_t grfid = this->st->roadstop_speclist[GetCustomRoadStopSpecIndex(this->tile)].grfid;
158 bool same_orientation = GetStationGfx(this->tile) == GetStationGfx(nearby_tile);
159 bool same_station = GetStationIndex(nearby_tile) == this->st->index;
160 uint32_t res = GetStationGfx(nearby_tile) << 12 | !same_orientation << 11 | !!same_station << 10;
161 StationType type = GetStationType(nearby_tile);
162 if (type == STATION_TRUCK) res |= (1 << 16);
163 if (type == STATION_ROADWAYPOINT) res |= (2 << 16);
164 if (type == this->type) SetBit(res, 20);
166 if (IsCustomRoadStopSpecIndex(nearby_tile)) {
167 const auto &sm = BaseStation::GetByTile(nearby_tile)->roadstop_speclist[GetCustomRoadStopSpecIndex(nearby_tile)];
168 res |= 1 << (sm.grfid != grfid ? 9 : 8) | ClampTo<uint8_t>(sm.localidx);
170 return res;
173 /* GRFID of nearby road stop tiles */
174 case 0x6A: {
175 if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
176 TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
178 if (!IsAnyRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
179 if (!IsCustomRoadStopSpecIndex(nearby_tile)) return 0;
181 const auto &sm = BaseStation::GetByTile(nearby_tile)->roadstop_speclist[GetCustomRoadStopSpecIndex(nearby_tile)];
182 return sm.grfid;
185 /* 16 bit road stop ID of nearby tiles */
186 case 0x6B: {
187 if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
188 TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
190 if (!IsAnyRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
191 if (!IsCustomRoadStopSpecIndex(nearby_tile)) return 0xFFFE;
193 uint32_t grfid = this->st->roadstop_speclist[GetCustomRoadStopSpecIndex(this->tile)].grfid;
195 const auto &sm = BaseStation::GetByTile(nearby_tile)->roadstop_speclist[GetCustomRoadStopSpecIndex(nearby_tile)];
196 if (sm.grfid == grfid) {
197 return sm.localidx;
200 return 0xFFFE;
203 case 0xF0: return this->st == nullptr ? 0 : this->st->facilities; // facilities
205 case 0xFA: return ClampTo<uint16_t>((this->st == nullptr ? TimerGameCalendar::date : this->st->build_date) - CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR); // build date
208 if (this->st != nullptr) return this->st->GetNewGRFVariable(this->ro, variable, parameter, available);
210 available = false;
211 return UINT_MAX;
214 const SpriteGroup *RoadStopResolverObject::ResolveReal(const RealSpriteGroup *group) const
216 if (group == nullptr) return nullptr;
218 return group->loading[0];
221 RoadStopResolverObject::RoadStopResolverObject(const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, RoadType roadtype, StationType type, uint8_t view,
222 CallbackID callback, uint32_t param1, uint32_t param2)
223 : ResolverObject(roadstopspec->grf_prop.grffile, callback, param1, param2), roadstop_scope(*this, st, roadstopspec, tile, roadtype, type, view)
225 CargoID ctype = SpriteGroupCargo::SG_DEFAULT_NA;
227 if (st == nullptr) {
228 /* No station, so we are in a purchase list */
229 ctype = SpriteGroupCargo::SG_PURCHASE;
230 } else if (Station::IsExpected(st)) {
231 const Station *station = Station::From(st);
232 /* Pick the first cargo that we have waiting */
233 for (const CargoSpec *cs : CargoSpec::Iterate()) {
234 if (roadstopspec->grf_prop.spritegroup[cs->Index()] != nullptr &&
235 station->goods[cs->Index()].cargo.TotalCount() > 0) {
236 ctype = cs->Index();
237 break;
242 if (roadstopspec->grf_prop.spritegroup[ctype] == nullptr) {
243 ctype = SpriteGroupCargo::SG_DEFAULT;
246 /* Remember the cargo type we've picked */
247 this->roadstop_scope.cargo_type = ctype;
248 this->root_spritegroup = roadstopspec->grf_prop.spritegroup[ctype];
251 TownScopeResolver *RoadStopResolverObject::GetTown()
253 if (!this->town_scope.has_value()) {
254 Town *t;
255 if (this->roadstop_scope.st != nullptr) {
256 t = this->roadstop_scope.st->town;
257 } else {
258 t = ClosestTownFromTile(this->roadstop_scope.tile, UINT_MAX);
260 if (t == nullptr) return nullptr;
261 this->town_scope.emplace(*this, t, this->roadstop_scope.st == nullptr);
263 return &*this->town_scope;
266 uint16_t GetRoadStopCallback(CallbackID callback, uint32_t param1, uint32_t param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, RoadType roadtype, StationType type, uint8_t view)
268 RoadStopResolverObject object(roadstopspec, st, tile, roadtype, type, view, callback, param1, param2);
269 return object.ResolveCallback();
273 * Draw representation of a road stop tile for GUI purposes.
274 * @param x position x of image.
275 * @param y position y of image.
276 * @param image an int offset for the sprite.
277 * @param roadtype the RoadType of the underlying road.
278 * @param spec the RoadStop's spec.
279 * @return true of the tile was drawn (allows for fallback to default graphics)
281 void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, StationType type, int view)
283 assert(roadtype != INVALID_ROADTYPE);
284 assert(spec != nullptr);
286 const RoadTypeInfo *rti = GetRoadTypeInfo(roadtype);
287 RoadStopResolverObject object(spec, nullptr, INVALID_TILE, roadtype, type, view);
288 const SpriteGroup *group = object.Resolve();
289 if (group == nullptr || group->type != SGT_TILELAYOUT) return;
290 const DrawTileSprites *dts = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr);
292 PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company);
294 SpriteID image = dts->ground.sprite;
295 PaletteID pal = dts->ground.pal;
297 RoadStopDrawMode draw_mode;
298 if (HasBit(spec->flags, RSF_DRAW_MODE_REGISTER)) {
299 draw_mode = static_cast<RoadStopDrawMode>(GetRegister(0x100));
300 } else {
301 draw_mode = spec->draw_mode;
304 if (type == STATION_ROADWAYPOINT) {
305 DrawSprite(SPR_ROAD_PAVED_STRAIGHT_X, PAL_NONE, x, y);
306 if ((draw_mode & ROADSTOP_DRAW_MODE_WAYP_GROUND) && GB(image, 0, SPRITE_WIDTH) != 0) {
307 DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y);
309 } else if (GB(image, 0, SPRITE_WIDTH) != 0) {
310 DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y);
313 if (view >= 4) {
314 /* Drive-through stop */
315 uint sprite_offset = 5 - view;
317 /* Road underlay takes precedence over tram */
318 if (type == STATION_ROADWAYPOINT || draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) {
319 if (rti->UsesOverlay()) {
320 SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND);
321 DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
323 SpriteID overlay = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY);
324 if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
325 } else if (RoadTypeIsTram(roadtype)) {
326 DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
329 } else {
330 /* Bay stop */
331 if ((draw_mode & ROADSTOP_DRAW_MODE_ROAD) && rti->UsesOverlay()) {
332 SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP);
333 DrawSprite(ground + view, PAL_NONE, x, y);
337 DrawCommonTileSeqInGUI(x, y, dts, 0, 0, palette, true);
340 /** Wrapper for animation control, see GetRoadStopCallback. */
341 uint16_t GetAnimRoadStopCallback(CallbackID callback, uint32_t param1, uint32_t param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, int)
343 return GetRoadStopCallback(callback, param1, param2, roadstopspec, st, tile, INVALID_ROADTYPE, GetStationType(tile), GetStationGfx(tile));
346 struct RoadStopAnimationFrameAnimationHelper {
347 static uint8_t Get(BaseStation *st, TileIndex tile) { return st->GetRoadStopAnimationFrame(tile); }
348 static bool Set(BaseStation *st, TileIndex tile, uint8_t frame) { return st->SetRoadStopAnimationFrame(tile, frame); }
351 /** Helper class for animation control. */
352 struct RoadStopAnimationBase : public AnimationBase<RoadStopAnimationBase, RoadStopSpec, BaseStation, int, GetAnimRoadStopCallback, RoadStopAnimationFrameAnimationHelper> {
353 static const CallbackID cb_animation_speed = CBID_STATION_ANIMATION_SPEED;
354 static const CallbackID cb_animation_next_frame = CBID_STATION_ANIM_NEXT_FRAME;
356 static const RoadStopCallbackMask cbm_animation_speed = CBM_ROAD_STOP_ANIMATION_SPEED;
357 static const RoadStopCallbackMask cbm_animation_next_frame = CBM_ROAD_STOP_ANIMATION_NEXT_FRAME;
360 void AnimateRoadStopTile(TileIndex tile)
362 const RoadStopSpec *ss = GetRoadStopSpec(tile);
363 if (ss == nullptr) return;
365 RoadStopAnimationBase::AnimateTile(ss, BaseStation::GetByTile(tile), tile, HasBit(ss->flags, RSF_CB141_RANDOM_BITS));
368 void TriggerRoadStopAnimation(BaseStation *st, TileIndex trigger_tile, StationAnimationTrigger trigger, CargoID cargo_type)
370 /* Get Station if it wasn't supplied */
371 if (st == nullptr) st = BaseStation::GetByTile(trigger_tile);
373 /* Check the cached animation trigger bitmask to see if we need
374 * to bother with any further processing. */
375 if (!HasBit(st->cached_roadstop_anim_triggers, trigger)) return;
377 uint16_t random_bits = Random();
378 auto process_tile = [&](TileIndex cur_tile) {
379 const RoadStopSpec *ss = GetRoadStopSpec(cur_tile);
380 if (ss != nullptr && HasBit(ss->animation.triggers, trigger)) {
381 CargoID cargo;
382 if (!IsValidCargoID(cargo_type)) {
383 cargo = INVALID_CARGO;
384 } else {
385 cargo = ss->grf_prop.grffile->cargo_map[cargo_type];
387 RoadStopAnimationBase::ChangeAnimationFrame(CBID_STATION_ANIM_START_STOP, ss, st, cur_tile, (random_bits << 16) | Random(), (uint8_t)trigger | (cargo << 8));
391 if (trigger == SAT_NEW_CARGO || trigger == SAT_CARGO_TAKEN || trigger == SAT_250_TICKS) {
392 for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) {
393 process_tile(tile_data.tile);
395 } else {
396 process_tile(trigger_tile);
401 * Trigger road stop randomisation
403 * @param st the station being triggered
404 * @param tile the exact tile of the station that should be triggered
405 * @param trigger trigger type
406 * @param cargo_type cargo type causing the trigger
408 void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTrigger trigger, CargoID cargo_type)
410 if (st == nullptr) st = Station::GetByTile(tile);
412 /* Check the cached cargo trigger bitmask to see if we need
413 * to bother with any further processing. */
414 if (st->cached_roadstop_cargo_triggers == 0) return;
415 if (IsValidCargoID(cargo_type) && !HasBit(st->cached_roadstop_cargo_triggers, cargo_type)) return;
417 SetBit(st->waiting_triggers, trigger);
419 uint32_t whole_reseed = 0;
421 /* Bitmask of completely empty cargo types to be matched. */
422 CargoTypes empty_mask = (trigger == RSRT_CARGO_TAKEN) ? GetEmptyMask(st) : 0;
424 uint32_t used_triggers = 0;
425 auto process_tile = [&](TileIndex cur_tile) {
426 const RoadStopSpec *ss = GetRoadStopSpec(cur_tile);
427 if (ss == nullptr) return;
429 /* Cargo taken "will only be triggered if all of those
430 * cargo types have no more cargo waiting." */
431 if (trigger == RSRT_CARGO_TAKEN) {
432 if ((ss->cargo_triggers & ~empty_mask) != 0) return;
435 if (!IsValidCargoID(cargo_type) || HasBit(ss->cargo_triggers, cargo_type)) {
436 RoadStopResolverObject object(ss, st, cur_tile, INVALID_ROADTYPE, GetStationType(cur_tile), GetStationGfx(cur_tile));
437 object.waiting_triggers = st->waiting_triggers;
439 const SpriteGroup *group = object.Resolve();
440 if (group == nullptr) return;
442 used_triggers |= object.used_triggers;
444 uint32_t reseed = object.GetReseedSum();
445 if (reseed != 0) {
446 whole_reseed |= reseed;
447 reseed >>= 16;
449 /* Set individual tile random bits */
450 uint8_t random_bits = st->GetRoadStopRandomBits(cur_tile);
451 random_bits &= ~reseed;
452 random_bits |= Random() & reseed;
453 st->SetRoadStopRandomBits(cur_tile, random_bits);
455 MarkTileDirtyByTile(cur_tile);
459 if (trigger == RSRT_NEW_CARGO || trigger == RSRT_CARGO_TAKEN) {
460 for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) {
461 process_tile(tile_data.tile);
463 } else {
464 process_tile(tile);
467 /* Update whole station random bits */
468 st->waiting_triggers &= ~used_triggers;
469 if ((whole_reseed & 0xFFFF) != 0) {
470 st->random_bits &= ~whole_reseed;
471 st->random_bits |= Random() & whole_reseed;
476 * Checks if there's any new stations by a specific RoadStopType
477 * @param rs the RoadStopType to check.
478 * @param roadtype the RoadType to check.
479 * @return true if there was any new RoadStopSpec's found for the given RoadStopType and RoadType, else false.
481 bool GetIfNewStopsByType(RoadStopType rs, RoadType roadtype)
483 for (const auto &cls : RoadStopClass::Classes()) {
484 /* Ignore the waypoint class. */
485 if (IsWaypointClass(cls)) continue;
486 /* Ignore the default class with only the default station. */
487 if (cls.Index() == ROADSTOP_CLASS_DFLT && cls.GetSpecCount() == 1) continue;
488 if (GetIfClassHasNewStopsByType(&cls, rs, roadtype)) return true;
490 return false;
494 * Checks if the given RoadStopClass has any specs assigned to it, compatible with the given RoadStopType.
495 * @param roadstopclass the RoadStopClass to check.
496 * @param rs the RoadStopType to check.
497 * @param roadtype the RoadType to check.
498 * @return true if the RoadStopSpec has any specs compatible with the given RoadStopType and RoadType.
500 bool GetIfClassHasNewStopsByType(const RoadStopClass *roadstopclass, RoadStopType rs, RoadType roadtype)
502 for (const auto spec : roadstopclass->Specs()) {
503 if (GetIfStopIsForType(spec, rs, roadtype)) return true;
505 return false;
509 * Checks if the given RoadStopSpec is compatible with the given RoadStopType.
510 * @param roadstopspec the RoadStopSpec to check.
511 * @param rs the RoadStopType to check.
512 * @param roadtype the RoadType to check.
513 * @return true if the RoadStopSpec is compatible with the given RoadStopType and RoadType.
515 bool GetIfStopIsForType(const RoadStopSpec *roadstopspec, RoadStopType rs, RoadType roadtype)
517 // The roadstopspec is nullptr, must be the default station, always return true.
518 if (roadstopspec == nullptr) return true;
520 if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(roadtype)) return false;
521 if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(roadtype)) return false;
523 if (roadstopspec->stop_type == ROADSTOPTYPE_ALL) return true;
525 switch (rs) {
526 case ROADSTOP_BUS:
527 if (roadstopspec->stop_type == ROADSTOPTYPE_PASSENGER) return true;
528 break;
530 case ROADSTOP_TRUCK:
531 if (roadstopspec->stop_type == ROADSTOPTYPE_FREIGHT) return true;
532 break;
534 default:
535 NOT_REACHED();
537 return false;
540 const RoadStopSpec *GetRoadStopSpec(TileIndex t)
542 if (!IsCustomRoadStopSpecIndex(t)) return nullptr;
544 const BaseStation *st = BaseStation::GetByTile(t);
545 uint specindex = GetCustomRoadStopSpecIndex(t);
546 return specindex < st->roadstop_speclist.size() ? st->roadstop_speclist[specindex].spec : nullptr;
549 int AllocateSpecToRoadStop(const RoadStopSpec *statspec, BaseStation *st, bool exec)
551 uint i;
553 if (statspec == nullptr || st == nullptr) return 0;
555 /* Try to find the same spec and return that one */
556 for (i = 1; i < st->roadstop_speclist.size() && i < NUM_ROADSTOPSPECS_PER_STATION; i++) {
557 if (st->roadstop_speclist[i].spec == statspec) return i;
560 /* Try to find an unused spec slot */
561 for (i = 1; i < st->roadstop_speclist.size() && i < NUM_ROADSTOPSPECS_PER_STATION; i++) {
562 if (st->roadstop_speclist[i].spec == nullptr && st->roadstop_speclist[i].grfid == 0) break;
565 if (i == NUM_ROADSTOPSPECS_PER_STATION) {
566 /* Full, give up */
567 return -1;
570 if (exec) {
571 if (i >= st->roadstop_speclist.size()) st->roadstop_speclist.resize(i + 1);
572 st->roadstop_speclist[i].spec = statspec;
573 st->roadstop_speclist[i].grfid = statspec->grf_prop.grffile->grfid;
574 st->roadstop_speclist[i].localidx = statspec->grf_prop.local_id;
576 RoadStopUpdateCachedTriggers(st);
579 return i;
582 void DeallocateSpecFromRoadStop(BaseStation *st, uint8_t specindex)
584 /* specindex of 0 (default) is never freeable */
585 if (specindex == 0) return;
587 /* Check custom road stop tiles if the specindex is still in use */
588 for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) {
589 if (GetCustomRoadStopSpecIndex(tile_data.tile) == specindex) {
590 return;
594 /* This specindex is no longer in use, so deallocate it */
595 st->roadstop_speclist[specindex].spec = nullptr;
596 st->roadstop_speclist[specindex].grfid = 0;
597 st->roadstop_speclist[specindex].localidx = 0;
599 /* If this was the highest spec index, reallocate */
600 if (specindex == st->roadstop_speclist.size() - 1) {
601 size_t num_specs;
602 for (num_specs = st->roadstop_speclist.size() - 1; num_specs > 0; num_specs--) {
603 if (st->roadstop_speclist[num_specs].grfid != 0) break;
606 if (num_specs > 0) {
607 st->roadstop_speclist.resize(num_specs + 1);
608 } else {
609 st->roadstop_speclist.clear();
610 st->cached_roadstop_anim_triggers = 0;
611 st->cached_roadstop_cargo_triggers = 0;
612 return;
616 RoadStopUpdateCachedTriggers(st);
620 * Update the cached animation trigger bitmask for a station.
621 * @param st Station to update.
623 void RoadStopUpdateCachedTriggers(BaseStation *st)
625 st->cached_roadstop_anim_triggers = 0;
626 st->cached_roadstop_cargo_triggers = 0;
628 /* Combine animation trigger bitmask for all road stop specs
629 * of this station. */
630 for (const auto &sm : GetStationSpecList<RoadStopSpec>(st)) {
631 if (sm.spec == nullptr) continue;
632 st->cached_roadstop_anim_triggers |= sm.spec->animation.triggers;
633 st->cached_roadstop_cargo_triggers |= sm.spec->cargo_triggers;