* Fix the speed table index remap in TablePurger()
TablePurger() rebuilds the speed table and has to move SemNextIndex and
SemNextStopIndex along with it. Two cases came out wrong.
A trimmed point was never invalidated: the index was first overwritten with
the position the next kept point would take, and the guard that should reset
it to -1 compared the loop counter against that new value, so it only matched
when nothing had been trimmed before it. The index then pointed at an
unrelated record, and if that record happens to sit at zero velocity the
placement can no longer be updated either, because the update requires the
recorded one to be non-zero.
An index pointing at the last entry was left untouched, because the loop stops
one short of it and that entry is appended afterwards. The stale value ended
up past the end of the trimmed table, which is what the provisory check in
TableTraceRoute() catches.
The placements are now recorded separately and assigned once the new table is
complete, with the always-kept last entry handled too. The trim condition is
shared by the counting and the copying pass instead of being spelled out
twice.
TableCheck() drops entries beyond a switch that changed under the scan without
adjusting the placements either, so they are invalidated there as well. The
check in TableTraceRoute() stays as a safety net, without the TODO.
* Sort the whole speed table in TableSort()
TableSort() made a single bubble pass over the table. That only moves an entry
one place towards the front, but TableTraceRoute() adds the scanned events of a
track before the track itself, so a track carrying k scanned events lands k
places too far and needs k passes. With two or more events in one slot - a cab
signal resonator next to a signal, a signal and a W4 - the track entry stays
behind entries that are farther away, and TableUpdate() walks the table out of
distance order until enough later scans have shifted it back.
The range before the last slot is now sorted with std::stable_sort, and the
semaphor placements are moved along by looking their events up again.
---------
Co-authored-by: pwarszawski <pwarszawski@gmail.com>
When the Autorewident function is enabled, the EP fuse switch will no longer automatically engage or disengage if the player is controlling the train. This would happen for example when approaching a station.
std::uniform_real/int_distribution has UB when min>max. The old "fancy Random"
used interpolate(a,b,t) which tolerated a>b; the migration to
std::uniform_real_distribution did not, and only one inverted call site was
fixed by hand. Swap the bounds inside Random()/Random(int)/LocalRandom() so
every call site is safe, and reorder the two inverted literal calls in
Driver.cpp (fActionTime buzzer / departure delay) for readability.
The dumb3d->glm / utilities-simplification refactor replaced the old custom
clamp() (which tolerated Lo>Hi, returning the upper bound) with std::clamp,
where inverted bounds are undefined behaviour. Only a few sites were patched
afterwards, by hand, with std::minmax plumbing (incl. a stray `static` inside
a per-axle loop). Others silently produced wrong results in the AI braking /
acceleration path.
Add a safe_clamp() helper (normalizes inverted bounds) and use it ONLY where
Lo<=Hi cannot be proven at compile time (config min/max pairs, sign-dependent
expressions, container underflow). Sites with constant bounds or [0, x>=0]
keep std::clamp. Remove the manual std::minmax workarounds and rewrite the
damaged-track jolt code value-first.
- The warning signal is no longer going to be played for an obnoxiously long amount of time by AI drivers when driving vehicles with a manual warning signal switch, most notably EN57.
- A new `moveDepartureWarned` driving flag has been created to accomodate this behavior.
- Added the action time counter and the missing driving flags to the Vehicle AI section.