basic isolated track section hierarchy, track vertical radius property, ai passenger stop logic tweaks, ai signal speed limit tracking logic tweaks

This commit is contained in:
tmj-fstate
2018-09-11 23:17:59 +02:00
parent 251a31a705
commit d05e404d75
10 changed files with 140 additions and 91 deletions

View File

@@ -812,6 +812,8 @@ void TController::TableCheck(double fDistance)
}
};
auto const passengerstopmaxdistance { 400.0 }; // maximum allowed distance between passenger stop point and consist head
TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fNext, double &fAcc)
{ // ustalenie parametrów, zwraca typ komendy, jeśli sygnał podaje prędkość do jazdy
// fVelDes - prędkość zadana
@@ -826,14 +828,17 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
TCommandType go = TCommandType::cm_Unknown;
eSignNext = NULL;
// te flagi są ustawiane tutaj, w razie potrzeby
iDrivigFlags &= ~(moveTrackEnd | moveSwitchFound | moveSemaphorFound | moveSpeedLimitFound);
iDrivigFlags &= ~(moveTrackEnd | moveSwitchFound | moveSemaphorFound | /*moveSpeedLimitFound*/ moveStopPointFound );
for( std::size_t i = 0; i < sSpeedTable.size(); ++i )
{ // sprawdzenie rekordów od (iFirst) do (iLast), o ile są istotne
if (sSpeedTable[i].iFlags & spEnabled) // badanie istotności
{ // o ile dana pozycja tabelki jest istotna
if (sSpeedTable[i].iFlags & spPassengerStopPoint)
{ // jeśli przystanek, trzeba obsłużyć wg rozkładu
if (sSpeedTable[i].iFlags & spPassengerStopPoint) {
// jeśli przystanek, trzeba obsłużyć wg rozkładu
iDrivigFlags |= moveStopPointFound;
// stop points are irrelevant when not in one of the basic modes
if( ( OrderCurrentGet() & ( Obey_train | Shunt ) ) == 0 ) { continue; }
// first 19 chars of the command is expected to be "PassengerStopPoint:" so we skip them
if ( ToLower(sSpeedTable[i].evEvent->CommandGet()).compare( 19, sizeof(asNextStop), ToLower(asNextStop)) != 0 )
{ // jeśli nazwa nie jest zgodna
@@ -858,7 +863,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
{ // jeśli nie ma tu postoju
sSpeedTable[i].fVelNext = -1; // maksymalna prędkość w tym miejscu
// przy 160km/h jedzie 44m/s, to da dokładność rzędu 5 sekund
if (sSpeedTable[i].fDist < 200.0) {
if (sSpeedTable[i].fDist < passengerstopmaxdistance * 0.5 ) {
// zaliczamy posterunek w pewnej odległości przed (choć W4 nie zasłania już semafora)
#if LOGSTOPS
WriteLog(
@@ -880,8 +885,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
} // koniec obsługi przelotu na W4
else {
// zatrzymanie na W4
if ( ( false == sSpeedTable[i].bMoved )
&& ( ( OrderCurrentGet() & ( Obey_train | Shunt ) ) != 0 ) ) {
if ( false == sSpeedTable[i].bMoved ) {
// potentially shift the stop point in accordance with its defined parameters
/*
// https://rainsted.com/pl/Wersja/18.2.133#Okr.C4.99gi_dla_W4_i_W32
@@ -910,16 +914,17 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
}
}
isatpassengerstop = (
( sSpeedTable[ i ].fDist <= passengerstopmaxdistance )
// Ra 2F1I: odległość plus długość pociągu musi być mniejsza od długości
// peronu, chyba że pociąg jest dłuższy, to wtedy minimalna.
// jeśli długość peronu ((sSpeedTable[i].evEvent->ValueGet(2)) nie podana,
// przyjąć odległość fMinProximityDist
( iDrivigFlags & moveStopCloser ) ?
( sSpeedTable[ i ].fDist + fLength ) <=
std::max(
std::abs( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ),
2.0 * fMaxProximityDist + fLength ) : // fmaxproximitydist typically equals ~50 m
sSpeedTable[ i ].fDist < d_to_next_sem );
&& ( ( iDrivigFlags & moveStopCloser ) != 0 ?
sSpeedTable[ i ].fDist + fLength <=
std::max(
std::abs( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ),
2.0 * fMaxProximityDist + fLength ) : // fmaxproximitydist typically equals ~50 m
sSpeedTable[ i ].fDist < d_to_next_sem ) );
if( !eSignNext ) {
//jeśli nie widzi następnego sygnału ustawia dotychczasową
@@ -928,8 +933,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
if( mvOccupied->Vel > 0.3 ) {
// jeśli jedzie (nie trzeba czekać, aż się drgania wytłumią - drzwi zamykane od 1.0) to będzie zatrzymanie
sSpeedTable[ i ].fVelNext = 0;
}
else if( true == isatpassengerstop ) {
} else if( true == isatpassengerstop ) {
// jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4
if( !AIControllFlag ) {
// w razie przełączenia na AI ma nie podciągać do W4, gdy użytkownik zatrzymał za daleko
@@ -1069,7 +1073,11 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
iDrivigFlags |= moveStopHere | moveStartHorn;
continue; // nie analizować prędkości
} // koniec obsługi ostatniej stacji
} // if (MoverParameters->Vel==0.0)
} // vel 0, at passenger stop
else {
// HACK: momentarily deactivate W4 to trick the controller into moving closer
sSpeedTable[ i ].fVelNext = -1;
} // vel 0, outside of passenger stop
} // koniec obsługi zatrzymania na W4
} // koniec warunku pomijania W4 podczas zmiany czoła
else
@@ -1121,15 +1129,11 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
if (sSpeedTable[i].iFlags & spOutsideStation)
{ // jeśli W5, to reakcja zależna od trybu jazdy
if (OrderCurrentGet() & Obey_train)
{ // w trybie pociągowym: można przyspieszyć do wskazanej prędkości (po
// zjechaniu z rozjazdów)
{ // w trybie pociągowym: można przyspieszyć do wskazanej prędkości (po zjechaniu z rozjazdów)
v = -1.0; // ignorować?
//TODO trzeba zmienić przypisywanie VelSignal na VelSignalLast
if (sSpeedTable[i].fDist < 0.0) // jeśli wskaźnik został minięty
{
VelSignalLast = v; //ustawienie prędkości na -1
// iStationStart=TrainParams->StationIndex; //zaktualizować
// wyświetlanie rozkładu
}
else if (!(iDrivigFlags & moveSwitchFound)) // jeśli rozjazdy już minięte
VelSignalLast = v; //!!! to też koniec ograniczenia
@@ -1174,7 +1178,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
else
{
iDrivigFlags |= moveSemaphorFound; //jeśli z przodu to dajemy falgę, że jest
d_to_next_sem = Min0R(sSpeedTable[i].fDist, d_to_next_sem);
d_to_next_sem = std::min(sSpeedTable[i].fDist, d_to_next_sem);
}
if( sSpeedTable[ i ].fDist <= d_to_next_sem )
{
@@ -1338,7 +1342,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
&& ( true == TestFlag( sSpeedTable[ i ].iFlags, ( spEnabled | spEvent | spPassengerStopPoint ) ) )
&& ( false == isatpassengerstop ) ) {
// ma podjechać bliżej - czy na pewno w tym miejscu taki warunek?
a = ( ( ( iDrivigFlags & moveStopCloser ) != 0 ) ?
a = ( ( d > passengerstopmaxdistance ) || ( ( iDrivigFlags & moveStopCloser ) != 0 ) ?
fAcc :
0.0 );
}
@@ -1409,9 +1413,12 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
} // if (sSpeedTable[i].iFlags&1)
} // for
if (VelSignalLast >= 0.0 && !(iDrivigFlags & (moveSemaphorFound | moveSwitchFound)) &&
(OrderCurrentGet() & Obey_train))
VelSignalLast = -1.0; // jeśli mieliśmy ograniczenie z semafora i nie ma przed nami
// jeśli mieliśmy ograniczenie z semafora i nie ma przed nami
if( ( VelSignalLast >= 0.0 )
&& ( ( iDrivigFlags & ( moveSemaphorFound | moveSwitchFound | moveStopPointFound ) ) == 0 )
&& ( true == TestFlag( OrderCurrentGet(), Obey_train ) ) ) {
VelSignalLast = -1.0;
}
//analiza spisanych z tabelki ograniczeń i nadpisanie aktualnego
if( ( true == isatpassengerstop ) && ( mvOccupied->Vel < 0.01 ) ) {
@@ -2562,8 +2569,9 @@ bool TController::ReleaseEngine() {
eAction = TAction::actSleep; //śpi (wygaszony)
OrderNext(Wait_for_orders); //żeby nie próbował coś robić dalej
TableClear(); // zapominamy ograniczenia
iDrivigFlags &= ~moveActive; // ma nie skanować sygnałów i nie reagować na komendy
TableClear(); // zapominamy ograniczenia
VelSignalLast = -1.0;
}
return OK;
}
@@ -3332,10 +3340,10 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
iStationStart = TrainParams->StationIndex;
asNextStop = TrainParams->NextStop();
iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym
NewCommand = Global.asCurrentSceneryPath + NewCommand;
// NewCommand = Global.asCurrentSceneryPath + NewCommand;
auto lookup =
FileExists(
{ NewCommand },
{ Global.asCurrentSceneryPath + NewCommand, szSoundPath + NewCommand },
{ ".ogg", ".flac", ".wav" } );
if( false == lookup.first.empty() ) {
// wczytanie dźwięku odjazdu podawanego bezpośrenido
@@ -3343,9 +3351,10 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
iGuardRadio = 0; // nie przez radio
}
else {
NewCommand += "radio";
auto lookup =
FileExists(
{ NewCommand + "radio" },
{ Global.asCurrentSceneryPath + NewCommand, szSoundPath + NewCommand },
{ ".ogg", ".flac", ".wav" } );
if( false == lookup.first.empty() ) {
// wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie)
@@ -4051,10 +4060,12 @@ TController::UpdateSituation(double dt) {
}
*/
// route scan
double routescanrange = (
mvOccupied->Vel > 5.0 ?
400 + fBrakeDist :
30.0 * fDriverDist ); // 1500m dla stojących pociągów;
auto const routescanrange {
std::max(
750.0,
mvOccupied->Vel > 5.0 ?
400 + fBrakeDist :
30.0 * fDriverDist ) }; // 1500m dla stojących pociągów;
// Ra 2015-01: przy dłuższej drodze skanowania AI jeździ spokojniej
// 2. Sprawdzić, czy tabelka pokrywa założony odcinek (nie musi, jeśli jest STOP).
// 3. Sprawdzić, czy trajektoria ruchu przechodzi przez zwrotnice - jeśli tak, to sprawdzić,

View File

@@ -52,9 +52,12 @@ enum TMovementStatus
moveDoorOpened = 0x20000, // drzwi zostały otwarte - doliczyć czas na zamknięcie
movePushPull = 0x40000, // zmiana czoła przez zmianę kabiny - nie odczepiać przy zmianie kierunku
moveSemaphorFound = 0x80000, // na drodze skanowania został znaleziony semafor
moveStopPointFound = 0x100000 // stop point detected ahead
/*
moveSemaphorWasElapsed = 0x100000, // minięty został semafor
moveTrainInsideStation = 0x200000, // pociąg między semaforem a rozjazdami lub następnym semaforem
moveSpeedLimitFound = 0x400000 // pociąg w ograniczeniu z podaną jego długością
*/
};
enum TStopReason

View File

@@ -68,12 +68,12 @@ TSwitchExtension::TSwitchExtension(TTrack *owner, int const what)
TSwitchExtension::~TSwitchExtension()
{ // nie ma nic do usuwania
}
/*
TIsolated::TIsolated()
{ // utworznie pustego
TIsolated("none", NULL);
};
*/
TIsolated::TIsolated(std::string const &n, TIsolated *i) :
asName( n ), pNext( i )
{
@@ -102,6 +102,15 @@ TIsolated * TIsolated::Find(std::string const &n)
return pRoot;
};
bool
TIsolated::AssignEvents() {
evBusy = simulation::Events.FindEvent( asName + ":busy" );
evFree = simulation::Events.FindEvent( asName + ":free" );
return ( evBusy != nullptr ) && ( evFree != nullptr );
}
void TIsolated::Modify(int i, TDynamicObject *o)
{ // dodanie lub odjęcie osi
if (iAxles)
@@ -131,6 +140,10 @@ void TIsolated::Modify(int i, TDynamicObject *o)
pMemCell->UpdateValues( "", 0, int( pMemCell->Value2() ) | 1, update_memval2 ); // zmieniamy ostatnią wartość na nieparzystą
}
}
// pass the event to the parent
if( pParent != nullptr ) {
pParent->Modify( i, o );
}
};
// tworzenie nowego odcinka ruchu
@@ -851,6 +864,12 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
iAction |= 0x40; // flaga opuszczenia pantografu (tor uwzględniany w skanowaniu jako
// ograniczenie dla pantografujących)
}
else if( str == "vradius" ) {
// y-axis track radius
// NOTE: not used/implemented
parser->getTokens();
*parser >> fVerticalRadius;
}
else
ErrorLog("Unknown property: \"" + str + "\" in track \"" + m_name + "\"");
parser->getTokens();
@@ -954,19 +973,6 @@ std::string TTrack::IsolatedName()
return "";
};
bool TTrack::IsolatedEventsAssign(TEvent *busy, TEvent *free)
{ // ustawia zdarzenia dla odcinka izolowanego
if (pIsolated)
{
if (busy)
pIsolated->evBusy = busy;
if (free)
pIsolated->evFree = free;
return true;
}
return false;
};
// ABu: przeniesione z Path.h i poprawione!!!
bool TTrack::AddDynamicObject(TDynamicObject *Dynamic)
{ // dodanie pojazdu do trajektorii
@@ -2764,7 +2770,7 @@ TTrack::export_as_text_( std::ostream &Output ) const {
eEnvironment == e_canyon ? "canyon" :
eEnvironment == e_mountains ? "mountains" :
"none" )
<< ' ';
<< ' ';
// visibility
// NOTE: 'invis' would be less wrong than 'unvis', but potentially incompatible with old 3rd party tools
Output << ( m_visible ? "vis" : "unvis" ) << ' ';
@@ -2772,8 +2778,8 @@ TTrack::export_as_text_( std::ostream &Output ) const {
// texture parameters are supplied only if the path is set as visible
auto texturefile { (
m_material1 != null_handle ?
GfxRenderer.Material( m_material1 ).name :
"none" ) };
GfxRenderer.Material( m_material1 ).name :
"none" ) };
if( texturefile.find( szTexturePath ) == 0 ) {
// don't include 'textures/' in the path
texturefile.erase( 0, std::string{ szTexturePath }.size() );
@@ -2784,8 +2790,8 @@ TTrack::export_as_text_( std::ostream &Output ) const {
texturefile = (
m_material2 != null_handle ?
GfxRenderer.Material( m_material2 ).name :
"none" );
GfxRenderer.Material( m_material2 ).name :
"none" );
if( texturefile.find( szTexturePath ) == 0 ) {
// don't include 'textures/' in the path
texturefile.erase( 0, std::string{ szTexturePath }.size() );
@@ -2828,11 +2834,14 @@ TTrack::export_as_text_( std::ostream &Output ) const {
for( auto &eventsequence : eventsequences ) {
for( auto &event : *( eventsequence.second ) ) {
if( false == event.first.empty() ) {
Output
<< eventsequence.first << ' '
<< event.first << ' ';
}
// NOTE: actual event name can be potentially different from its cached name, if it was renamed in the editor
// therefore on export we pull the name from the event itself, if the binding isn't broken
Output
<< eventsequence.first << ' '
<< ( event.second != nullptr ?
event.second->asName :
event.first )
<< ' ';
}
}
if( ( SwitchExtension )
@@ -2850,6 +2859,9 @@ TTrack::export_as_text_( std::ostream &Output ) const {
if( fOverhead != -1.0 ) {
Output << "overhead " << fOverhead << ' ';
}
if( fVerticalRadius != 0.f ) {
Output << "vradius " << fVerticalRadius << ' ';
}
// footer
Output
<< "endtrack"
@@ -3091,15 +3103,6 @@ path_table::InitTracks() {
}
} // switch
// pobranie nazwy odcinka izolowanego
auto const isolatedname { track->IsolatedName() };
if( false == isolatedname.empty() ) {
// jeśli została zwrócona nazwa
track->IsolatedEventsAssign(
simulation::Events.FindEvent( isolatedname + ":busy" ),
simulation::Events.FindEvent( isolatedname + ":free" ) );
}
if( ( trackname[ 0 ] == '*' )
&& ( !track->CurrentPrev() && track->CurrentNext() ) ) {
// możliwy portal, jeśli nie podłączony od strony 1
@@ -3108,25 +3111,25 @@ path_table::InitTracks() {
}
}
TIsolated *isolated = TIsolated::Root();
auto *isolated = TIsolated::Root();
while( isolated ) {
// jeśli się znajdzie, to podać wskaźnik
isolated->AssignEvents();
auto *memorycell = simulation::Memory.find( isolated->asName ); // czy jest komóka o odpowiedniej nazwie
if( memorycell != nullptr ) {
// przypisanie powiązanej komórki
isolated->pMemCell = memorycell;
}
else {
if( memorycell == nullptr ) {
// utworzenie automatycznej komórki
// TODO: determine suitable location for this one, create and add world reference node
scene::node_data nodedata;
nodedata.name = isolated->asName;
auto *memorycell = new TMemCell( nodedata ); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy
memorycell = new TMemCell( nodedata ); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy
// NOTE: autogenerated cells aren't exported; they'll be autogenerated anew when exported file is loaded
memorycell->is_exportable = false;
simulation::Memory.insert( memorycell );
isolated->pMemCell = memorycell; // wskaźnik komóki przekazany do odcinka izolowanego
}
// przypisanie powiązanej komórki
isolated->pMemCell = memorycell;
isolated = isolated->Next();
}
}

31
Track.h
View File

@@ -99,18 +99,32 @@ class TIsolated
{ // obiekt zbierający zajętości z kilku odcinków
public:
// constructors
TIsolated();
TIsolated(const std::string &n, TIsolated *i);
// methods
static void DeleteAll();
static TIsolated * Find(const std::string &n); // znalezienie obiektu albo utworzenie nowego
bool AssignEvents();
void Modify(int i, TDynamicObject *o); // dodanie lub odjęcie osi
bool Busy() {
return (iAxles > 0); };
static TIsolated * Root() {
return (pRoot); };
TIsolated * Next() {
return (pNext); };
inline
bool
Busy() {
return (iAxles > 0); };
inline
static TIsolated *
Root() {
return (pRoot); };
inline
TIsolated *
Next() {
return (pNext); };
inline
void
parent( TIsolated *Parent ) {
pParent = Parent; }
inline
TIsolated *
parent() const {
return pParent; }
// members
std::string asName; // nazwa obiektu, baza do nazw eventów
TEvent *evBusy { nullptr }; // zdarzenie wyzwalane po zajęciu grupy
@@ -120,6 +134,7 @@ private:
// members
int iAxles { 0 }; // ilość osi na odcinkach obsługiwanych przez obiekt
TIsolated *pNext { nullptr }; // odcinki izolowane są trzymane w postaci listy jednikierunkowej
TIsolated *pParent { nullptr }; // optional parent piece, collecting data from its children
static TIsolated *pRoot; // początek listy
};
@@ -140,6 +155,7 @@ private:
// McZapkie-070402: dodalem zmienne opisujace rozmiary tekstur
int iTrapezoid = 0; // 0-standard, 1-przechyłka, 2-trapez, 3-oba
double fRadiusTable[ 2 ] = { 0.0, 0.0 }; // dwa promienie, drugi dla zwrotnicy
float fVerticalRadius { 0.f }; // y-axis track radius (currently not supported)
float fTexLength = 4.0f; // długość powtarzania tekstury w metrach
float fTexRatio1 = 1.0f; // proporcja boków tekstury nawierzchni (żeby zaoszczędzić na rozmiarach tekstur...)
float fTexRatio2 = 1.0f; // proporcja boków tekstury chodnika (żeby zaoszczędzić na rozmiarach tekstur...)
@@ -265,7 +281,6 @@ public:
if (pIsolated)
pIsolated->Modify(i, o); }; // dodanie lub odjęcie osi
std::string IsolatedName();
bool IsolatedEventsAssign(TEvent *busy, TEvent *free);
double WidthTotal();
bool IsGroupable();
int TestPoint( Math3D::vector3 *Point);

View File

@@ -251,9 +251,10 @@ timetable_panel::update() {
to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Dm ) ).substr( 1, 2 ) :
" | " );
auto const candeparture = (
( owner->iStationStart < table->StationIndex )
( owner->iStationStart < table->StationIndex )
&& ( i < table->StationIndex )
&& ( ( time.wHour * 60 + time.wMinute ) >= ( tableline->Dh * 60 + tableline->Dm ) ) );
&& ( ( tableline->Ah < 0 ) // pass-through, always valid
|| ( time.wHour * 60 + time.wMinute >= tableline->Dh * 60 + tableline->Dm ) ) );
auto traveltime =
" "
+ ( i < 2 ? "" :
@@ -726,7 +727,7 @@ debug_panel::update_section_ai( std::vector<text_line> &Output ) {
textline =
"Acceleration:\n desired: " + to_string( mechanik.AccDesired, 2 )
+ ", corrected: " + to_string( mechanik.AccDesired * mechanik.BrakeAccFactor(), 2 )
+ "\n current: " + to_string( mechanik.AbsAccS_pub, 2 )
+ "\n current: " + to_string( mechanik.AbsAccS_pub + 0.001f, 2 )
+ ", slope: " + to_string( mechanik.fAccGravity + 0.001f, 2 ) + " (" + ( mechanik.fAccGravity > 0.01 ? "\\" : ( mechanik.fAccGravity < -0.01 ? "/" : "-" ) ) + ")"
+ "\n brake threshold: " + to_string( mechanik.fAccThreshold, 2 )
+ ", delays: " + to_string( mechanik.fBrake_a0[ 0 ], 2 )
@@ -744,7 +745,7 @@ debug_panel::update_section_ai( std::vector<text_line> &Output ) {
std::vector<std::string> const drivingflagnames {
"StopCloser", "StopPoint", "Active", "Press", "Connect", "Primary", "Late", "StopHere",
"StartHorn", "StartHornNow", "StartHornDone", "Oerlikons", "IncSpeed", "TrackEnd", "SwitchFound", "GuardSignal",
"Visibility", "DoorOpened", "PushPull", "SemaphorFound", "SemaphorWasElapsed", "TrainInsideStation", "SpeedLimitFound" };
"Visibility", "DoorOpened", "PushPull", "SemaphorFound", "StopPointFound" /*"SemaphorWasElapsed", "TrainInsideStation", "SpeedLimitFound"*/ };
textline = "Driving flags:";
for( int idx = 0, flagbit = 1; idx < drivingflagnames.size(); ++idx, flagbit <<= 1 ) {

View File

@@ -147,7 +147,7 @@ itemproperties_panel::update( scene::basic_node const *Node ) {
textline += ", ";
}
textline += (
event.second ?
event.second != nullptr ?
event.second->asName :
event.first + " (missing)" );
}

View File

@@ -196,7 +196,7 @@ std::string cParser::readToken( bool ToLower, const char *Break ) {
// update line counter
++mLine;
}
} while( token == "" && mStream->peek() != EOF ); // double check to deal with trailing spaces
} while( token == "" && mStream->peek() != EOF ); // double check in case of consecutive separators
}
if( false == parameters.empty() ) {

View File

@@ -67,6 +67,7 @@ state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad )
std::pair<
std::string,
deserializefunction> > functionlist = {
{ "area", &state_serializer::deserialize_area },
{ "atmo", &state_serializer::deserialize_atmo },
{ "camera", &state_serializer::deserialize_camera },
{ "config", &state_serializer::deserialize_config },
@@ -125,6 +126,20 @@ state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad )
}
}
void
state_serializer::deserialize_area( cParser &Input, scene::scratch_data &Scratchpad ) {
// first parameter specifies name of parent piece...
auto token { Input.getToken<std::string>() };
auto *groupowner { TIsolated::Find( token ) };
// ...followed by list of its children
while( ( false == ( token = Input.getToken<std::string>() ).empty() )
&& ( token != "endarea" ) ) {
// bind the children with their parent
auto *isolated { TIsolated::Find( token ) };
isolated->parent( groupowner );
}
}
void
state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ) {

View File

@@ -29,6 +29,7 @@ private:
// methods
// restores class data from provided stream
void deserialize( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_area( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad );

View File

@@ -1,5 +1,5 @@
#pragma once
#define VERSION_MAJOR 18
#define VERSION_MINOR 909
#define VERSION_MINOR 911
#define VERSION_REVISION 0