mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 22:39:19 +02:00
Keep one primary per consist and let a secondary AI claim it.
A driver without movePrimary no longer just idles. UpdateSecondary() cuts BrakeValveActive so a secondary AI stops fighting the primary over the brake pipe, and - as long as no driver in the consist has iEngineActive - keeps scanning ahead of CabOccupied, so it can take movePrimary and start the engine once a signal permits. scan_route() orients by CabOccupied when DirActive is 0, and ClaimConsistPrimary() clears movePrimary on every other driver in the consist, then calls TableClear() and CheckVehicles(), so sSpeedTable is rebuilt for the new front and ctOwner follows it. - walk the consist with FirstFind()/Neighbour() instead of Next()/Prev(), which stopped at the starting vehicle and mishandled reversed members; - ReassignConsistPrimary() picks a single driver after any coupling change, both from TDynamicObject::couple() and from the AI path; - IsOccupiedByAnotherConsist() decides membership with BelongsToConsist() and takes a signed distance, so a consist left behind no longer cuts the scan short. DistanceToAnotherConsist() hands back the gap it already computes, so OtherConsistAtSignal() counts a foreign vehicle only while it stands closer than the signal itself, instead of comparing track starts; - TSpeedPos::Set() marks an entry with spNotOurShuntSignal when a spShuntSemaphor was already permitting as the entry was added. A shunting signal does not address its permission to a particular consist, so a dormant driver does not wake up on a permission it did not see light up; the flag drops once the signal shows stop, so its own turn is never blocked. spSemaphor is excluded: a semaphore sets a route for us and permits us even when found already lit. The check reads SemPermitIndex - the entry that produced the permitting command - because SemNextIndex can point at an earlier signal. The flag is also stamped whenever the permission turns out to belong to a consist standing in front of us - both where TableUpdateEvent() already knows the signal is for the vehicle ahead, and in check_route_ahead() - so the addressee pulling away does not hand the still-lit signal over to whoever waited behind it; - the scantable debug panel lists the flags by name (spNotOurShuntSignal shows as not-ours) and folds the nearest vehicle ahead into the table by its own distance.
This commit is contained in:
@@ -831,59 +831,83 @@ state_serializer::deserialize_editorterrain(cParser &Input, scene::scratch_data
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using trainset_data = scene::scratch_data::trainset_data;
|
||||
|
||||
void couple_trainset( trainset_data &trainset )
|
||||
{ // couple before picking the primary - reassign sees uncoupled members as separate consists
|
||||
for( std::size_t i = 1; i < trainset.vehicles.size(); ++i ) {
|
||||
trainset.vehicles[ i - 1 ]->AttachNext(
|
||||
trainset.vehicles[ i ],
|
||||
trainset.couplings[ i - 1 ] );
|
||||
}
|
||||
}
|
||||
|
||||
TDynamicObject *select_trainset_driver( trainset_data &trainset )
|
||||
{ // picks the primary after coupling and hands it its orders, if any
|
||||
trainset.vehicles.front()->ReassignConsistPrimary();
|
||||
|
||||
for( auto *vehicle : trainset.vehicles ) {
|
||||
if( vehicle->Mechanik == nullptr || !vehicle->Mechanik->primary() ) {
|
||||
continue;
|
||||
}
|
||||
auto const lookup { trainset.assignment.find( Global.asLang ) };
|
||||
if( lookup != trainset.assignment.end() ) {
|
||||
vehicle->Mechanik->assignment() = lookup->second;
|
||||
}
|
||||
return vehicle;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void init_trainset_driver( trainset_data const &trainset )
|
||||
{
|
||||
if( trainset.driver == nullptr || trainset.driver->Mechanik == nullptr ) {
|
||||
return;
|
||||
}
|
||||
auto *controller { trainset.driver->Mechanik };
|
||||
controller->DirectionInitial();
|
||||
controller->PutCommand(
|
||||
"Timetable:" + trainset.name,
|
||||
trainset.velocity,
|
||||
0,
|
||||
nullptr );
|
||||
}
|
||||
|
||||
void apply_trainset_end_signals( trainset_data const &trainset )
|
||||
{
|
||||
// coupler 0 on the last vehicle: plate end signals (the AI fixes them once it starts)
|
||||
if( trainset.couplings.back() != coupling::faux ) {
|
||||
return;
|
||||
}
|
||||
// with a driver: markers + plates; without one: plates only (the AI corrects it on startup)
|
||||
auto const rearlights {
|
||||
trainset.driver != nullptr
|
||||
? light::redmarker_left | light::redmarker_right | light::rearendsignals
|
||||
: light::rearendsignals };
|
||||
trainset.vehicles.back()->RaLightsSet( -1, rearlights );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
state_serializer::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) {
|
||||
|
||||
if( false == Scratchpad.trainset.is_open
|
||||
|| true == Scratchpad.trainset.vehicles.empty() ) {
|
||||
// not bloody likely but we better check for it just the same
|
||||
if( !Scratchpad.trainset.is_open || Scratchpad.trainset.vehicles.empty() ) {
|
||||
ErrorLog( "Bad trainset: empty trainset defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
|
||||
Scratchpad.trainset.is_open = false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t vehicleindex { 0 };
|
||||
for( auto *vehicle : Scratchpad.trainset.vehicles ) {
|
||||
// go through list of vehicles in the trainset, coupling them together and checking for potential driver
|
||||
if( vehicle->Mechanik != nullptr
|
||||
&& vehicle->Mechanik->primary() ) {
|
||||
// primary driver will receive the timetable for this trainset
|
||||
Scratchpad.trainset.driver = vehicle;
|
||||
// they'll also receive assignment data if there's any
|
||||
auto const lookup { Scratchpad.trainset.assignment.find( Global.asLang ) };
|
||||
if( lookup != Scratchpad.trainset.assignment.end() ) {
|
||||
vehicle->Mechanik->assignment() = lookup->second;
|
||||
}
|
||||
}
|
||||
if( vehicleindex > 0 ) {
|
||||
// from second vehicle on couple it with the previous one
|
||||
Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachNext(
|
||||
vehicle,
|
||||
Scratchpad.trainset.couplings[ vehicleindex - 1 ] );
|
||||
}
|
||||
++vehicleindex;
|
||||
}
|
||||
auto &trainset { Scratchpad.trainset };
|
||||
|
||||
if( Scratchpad.trainset.driver != nullptr ) {
|
||||
// if present, send timetable to the driver
|
||||
// wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy
|
||||
auto *controller = Scratchpad.trainset.driver->Mechanik;
|
||||
controller->DirectionInitial();
|
||||
controller->PutCommand(
|
||||
"Timetable:" + Scratchpad.trainset.name,
|
||||
Scratchpad.trainset.velocity,
|
||||
0,
|
||||
nullptr );
|
||||
}
|
||||
if( Scratchpad.trainset.couplings.back() == coupling::faux ) {
|
||||
// jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane (jak AI się odpali, to sobie poprawi)
|
||||
// place end signals only on trains without a driver, activate markers otherwise
|
||||
Scratchpad.trainset.vehicles.back()->RaLightsSet(
|
||||
-1,
|
||||
Scratchpad.trainset.driver != nullptr ? light::redmarker_left | light::redmarker_right | light::rearendsignals : light::rearendsignals );
|
||||
}
|
||||
// all done
|
||||
Scratchpad.trainset.is_open = false;
|
||||
couple_trainset( trainset );
|
||||
trainset.driver = select_trainset_driver( trainset );
|
||||
init_trainset_driver( trainset );
|
||||
apply_trainset_end_signals( trainset );
|
||||
|
||||
trainset.is_open = false;
|
||||
}
|
||||
|
||||
// creates path and its wrapper, restoring class data from provided stream
|
||||
|
||||
Reference in New Issue
Block a user