Fix #14827: Store random trigger information per-tile. (#14830)

This commit is contained in:
Peter Nelson
2026-02-03 23:01:55 +00:00
committed by GitHub
parent 1cb2015694
commit 7341c4d9aa
5 changed files with 80 additions and 10 deletions
+1
View File
@@ -74,6 +74,7 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
TimerGameCalendar::Date build_date{}; ///< Date of construction
uint16_t random_bits = 0; ///< Random bits assigned to this station
std::unordered_map<TileIndex, StationRandomTriggers> tile_waiting_random_triggers;
StationRandomTriggers waiting_random_triggers; ///< Waiting triggers (NewGRF), shared by all station parts/tiles, road stops, ... essentially useless and broken by design.
StationAnimationTriggers cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
StationAnimationTriggers cached_roadstop_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing should happen.
+27 -5
View File
@@ -66,7 +66,14 @@ uint32_t RoadStopScopeResolver::GetRandomBits() const
uint32_t RoadStopScopeResolver::GetRandomTriggers() const
{
return this->st == nullptr ? 0 : this->st->waiting_random_triggers.base();
if (this->st == nullptr) return 0;
StationRandomTriggers triggers = st->waiting_random_triggers;
auto it = this->st->tile_waiting_random_triggers.find(this->tile);
if (it != std::end(this->st->tile_waiting_random_triggers)) triggers.Set(it->second);
return triggers.base();
}
uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const
@@ -418,6 +425,17 @@ void TriggerRoadStopAnimation(BaseStation *st, TileIndex trigger_tile, StationAn
*/
void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandomTrigger trigger, CargoType cargo_type)
{
enum TriggerArea : uint8_t {
TA_TILE,
TA_PLATFORM,
TA_WHOLE,
};
/* List of coverage areas for each animation trigger */
static constexpr TriggerArea tas[] = {
TA_WHOLE, TA_WHOLE, TA_PLATFORM, TA_PLATFORM, TA_PLATFORM, TA_PLATFORM
};
assert(st != nullptr);
/* Check the cached cargo trigger bitmask to see if we need
@@ -426,7 +444,9 @@ void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandom
if (st->cached_roadstop_cargo_triggers == 0) return;
if (IsValidCargoType(cargo_type) && !HasBit(st->cached_roadstop_cargo_triggers, cargo_type)) return;
st->waiting_random_triggers.Set(trigger);
TriggerArea ta = tas[to_underlying(trigger)];
if (ta == TA_WHOLE) st->waiting_random_triggers.Set(trigger);
StationRandomTriggers used_random_triggers;
uint32_t whole_reseed = 0;
@@ -436,11 +456,12 @@ void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandom
empty_mask = GetEmptyMask(Station::From(st));
}
StationRandomTriggers used_random_triggers;
auto process_tile = [&](TileIndex cur_tile) {
const RoadStopSpec *ss = GetRoadStopSpec(cur_tile);
if (ss == nullptr) return;
st->tile_waiting_random_triggers[tile].Set(trigger);
/* Cargo taken "will only be triggered if all of those
* cargo types have no more cargo waiting." */
if (trigger == StationRandomTrigger::CargoTaken) {
@@ -449,10 +470,11 @@ void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandom
if (!IsValidCargoType(cargo_type) || HasBit(ss->cargo_triggers, cargo_type)) {
RoadStopResolverObject object(ss, st, cur_tile, INVALID_ROADTYPE, GetStationType(cur_tile), GetStationGfx(cur_tile));
object.SetWaitingRandomTriggers(st->waiting_random_triggers);
object.SetWaitingRandomTriggers(st->waiting_random_triggers | st->tile_waiting_random_triggers[tile]);
object.ResolveRerandomisation();
st->tile_waiting_random_triggers[tile].Reset(object.GetUsedRandomTriggers());
used_random_triggers.Set(object.GetUsedRandomTriggers());
uint32_t reseed = object.GetReseedSum();
@@ -470,7 +492,7 @@ void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandom
}
}
};
if (trigger == StationRandomTrigger::NewCargo || trigger == StationRandomTrigger::CargoTaken) {
if (ta == TA_WHOLE) {
for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) {
process_tile(tile_data.tile);
}
+17 -5
View File
@@ -244,7 +244,14 @@ static uint32_t GetRailContinuationInfo(TileIndex tile)
/* virtual */ uint32_t StationScopeResolver::GetRandomTriggers() const
{
return this->st == nullptr ? 0 : this->st->waiting_random_triggers.base();
if (this->st == nullptr) return 0;
StationRandomTriggers triggers = st->waiting_random_triggers;
auto it = this->st->tile_waiting_random_triggers.find(this->tile);
if (it != std::end(this->st->tile_waiting_random_triggers)) triggers.Set(it->second);
return triggers.base();
}
@@ -949,7 +956,7 @@ void TriggerStationAnimation(BaseStation *st, TileIndex trigger_tile, StationAni
void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
{
/* List of coverage areas for each animation trigger */
static const TriggerArea tas[] = {
static constexpr TriggerArea tas[] = {
TA_WHOLE, TA_WHOLE, TA_PLATFORM, TA_PLATFORM, TA_PLATFORM, TA_PLATFORM
};
@@ -970,12 +977,16 @@ void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, Statio
}
/* Store triggers now for var 5F */
st->waiting_random_triggers.Set(trigger);
TriggerArea ta = tas[to_underlying(trigger)];
if (ta == TA_WHOLE) st->waiting_random_triggers.Set(trigger);
StationRandomTriggers used_random_triggers;
/* Check all tiles over the station to check if the specindex is still in use */
for (TileIndex tile : GetRailTileArea(st, trigger_tile, tas[static_cast<size_t>(trigger)])) {
for (TileIndex tile : GetRailTileArea(st, trigger_tile, ta)) {
if (st->TileBelongsToRailStation(tile)) {
/* Store triggers now for var 5F */
st->tile_waiting_random_triggers[tile].Set(trigger);
const StationSpec *ss = GetStationSpec(tile);
if (ss == nullptr) continue;
@@ -987,10 +998,11 @@ void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, Statio
if (!IsValidCargoType(cargo_type) || HasBit(ss->cargo_triggers, cargo_type)) {
StationResolverObject object(ss, st, tile, CBID_RANDOM_TRIGGER, 0);
object.SetWaitingRandomTriggers(st->waiting_random_triggers);
object.SetWaitingRandomTriggers(st->waiting_random_triggers | st->tile_waiting_random_triggers[tile]);
object.ResolveRerandomisation();
st->tile_waiting_random_triggers[tile].Reset(object.GetUsedRandomTriggers());
used_random_triggers.Set(object.GetUsedRandomTriggers());
uint32_t reseed = object.GetReseedSum();
+32
View File
@@ -229,6 +229,37 @@ public:
template class SlStationSpecList<StationSpec>;
template class SlStationSpecList<RoadStopSpec>;
class SlStationWaitingTriggers : public DefaultSaveLoadHandler<SlStationWaitingTriggers, BaseStation> {
public:
using StationWaitingTriggersPair = std::pair<const TileIndex, StationRandomTriggers>;
static inline const SaveLoad description[] = {
SLE_VAR(StationWaitingTriggersPair, first, SLE_UINT32),
SLE_VAR(StationWaitingTriggersPair, second, SLE_UINT8),
};
static inline const SaveLoadCompatTable compat_description = _station_cargo_sl_compat;
void Save(BaseStation *st) const override
{
SlSetStructListLength(st->tile_waiting_random_triggers.size());
for (const auto &pair : st->tile_waiting_random_triggers) {
SlObject(const_cast<StationWaitingTriggersPair *>(&pair), this->GetDescription());
}
}
void Load(BaseStation *st) const override
{
size_t num = SlGetStructListLength(UINT32_MAX);
if (num == 0) return;
StationWaitingTriggersPair pair;
for (uint j = 0; j < num; ++j) {
SlObject(&pair, this->GetLoadDescription());
st->tile_waiting_random_triggers.emplace(pair.first, pair.second);
}
}
};
class SlStationCargo : public DefaultSaveLoadHandler<SlStationCargo, GoodsEntry> {
public:
static inline const SaveLoad description[] = {
@@ -563,6 +594,7 @@ public:
/* Used by newstations for graphic variations */
SLE_VAR(BaseStation, random_bits, SLE_UINT16),
SLE_VARNAME(BaseStation, waiting_random_triggers, "waiting_triggers", SLE_UINT8),
SLEG_STRUCTLIST("tile_waiting_triggers", SlStationWaitingTriggers),
SLEG_CONDVAR("num_specs", SlStationSpecList<StationSpec>::last_num_specs, SLE_UINT8, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH),
};
static inline const SaveLoadCompatTable compat_description = _station_base_sl_compat;
+3
View File
@@ -1806,6 +1806,7 @@ CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_st
Company::Get(owner)->infrastructure.station--;
DirtyCompanyInfrastructureWindows(owner);
st->tile_waiting_random_triggers.erase(tile);
st->rect.AfterRemoveTile(st, tile);
AddTrackToSignalBuffer(tile, track, owner);
YapfNotifyTrackLayoutChange(tile, track);
@@ -2329,6 +2330,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlags flags, int repl
if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? StationType::Truck: StationType::Bus);
st->tile_waiting_random_triggers.erase(tile);
st->RemoveRoadStopTileData(tile);
if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
@@ -2388,6 +2390,7 @@ CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int rep
wp->rect.AfterRemoveTile(wp, tile);
wp->tile_waiting_random_triggers.erase(tile);
wp->RemoveRoadStopTileData(tile);
if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);