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/>.
8 /** @file command.cpp Handling of NewGRF road stops. */
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"
19 #include "company_func.h"
21 #include "window_type.h"
22 #include "timer/timer_game_calendar.h"
24 #include "viewport_func.h"
25 #include "newgrf_animation_base.h"
26 #include "newgrf_sound.h"
28 #include "safeguards.h"
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);
41 bool RoadStopClass::IsUIAvailable(uint
) const
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;
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 {
71 if (this->tile
== INVALID_TILE
) {
72 rt
= (GetRoadTramType(this->roadtype
) == rtt
) ? this->roadtype
: INVALID_ROADTYPE
;
74 rt
= GetRoadType(this->tile
, rtt
);
76 if (rt
== INVALID_ROADTYPE
) {
79 return GetReverseRoadTypeTranslation(rt
, this->roadstopspec
->grf_prop
.grffile
);
85 case 0x40: return this->view
;
87 /* Stop type: 0: bus, 1: truck, 2: waypoint */
89 if (this->type
== STATION_BUS
) return 0;
90 if (this->type
== STATION_TRUCK
) return 1;
94 case 0x42: return this->tile
== INVALID_TILE
? 0 : (GetTileSlope(this->tile
) << 8 | GetTerrainType(this->tile
, TCX_NORMAL
));
97 case 0x43: return get_road_type_variable(RTT_ROAD
);
100 case 0x44: return get_road_type_variable(RTT_TRAM
);
102 /* Town zone and Manhattan distance of closest town */
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 */
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
);
125 if (this->tile
== INVALID_TILE
) {
131 /* Variables which use the parameter */
132 /* Variables 0x60 to 0x65 and 0x69 are handled separately below */
134 /* Animation frame of nearby tile */
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 */
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 */
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
);
173 /* GRFID of nearby road stop tiles */
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
)];
185 /* 16 bit road stop ID of nearby tiles */
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
) {
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
);
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
;
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) {
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()) {
255 if (this->roadstop_scope
.st
!= nullptr) {
256 t
= this->roadstop_scope
.st
->town
;
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));
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
);
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
);
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
)) {
382 if (!IsValidCargoID(cargo_type
)) {
383 cargo
= INVALID_CARGO
;
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
);
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();
446 whole_reseed
|= reseed
;
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
);
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;
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;
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;
527 if (roadstopspec
->stop_type
== ROADSTOPTYPE_PASSENGER
) return true;
531 if (roadstopspec
->stop_type
== ROADSTOPTYPE_FREIGHT
) return true;
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
)
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
) {
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
);
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
) {
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) {
602 for (num_specs
= st
->roadstop_speclist
.size() - 1; num_specs
> 0; num_specs
--) {
603 if (st
->roadstop_speclist
[num_specs
].grfid
!= 0) break;
607 st
->roadstop_speclist
.resize(num_specs
+ 1);
609 st
->roadstop_speclist
.clear();
610 st
->cached_roadstop_anim_triggers
= 0;
611 st
->cached_roadstop_cargo_triggers
= 0;
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
;