16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 22:09:19 +02:00

scenery groups export and deserialization, associated events included in scenery node groups, AI brake charging tweak, door locking and vehicle hunting oscillation sounds

This commit is contained in:
tmj-fstate
2018-08-24 21:11:35 +02:00
parent 24eda2d63f
commit 09f24df109
14 changed files with 304 additions and 118 deletions

View File

@@ -275,14 +275,13 @@ void Console::ValueSet(int x, double y)
WriteLog( " fraction=" + std::to_string( y ) );
}
}
double temp = (((((Global.fCalibrateOut[x][5] * y) + Global.fCalibrateOut[x][4]) * y +
Global.fCalibrateOut[x][3]) *
y +
Global.fCalibrateOut[x][2]) *
y +
Global.fCalibrateOut[x][1]) *
y +
Global.fCalibrateOut[x][0]; // zakres <0;1>
double temp = (((((
Global.fCalibrateOut[x][5] * y)
+ Global.fCalibrateOut[x][4]) * y
+ Global.fCalibrateOut[x][3]) * y
+ Global.fCalibrateOut[x][2]) * y
+ Global.fCalibrateOut[x][1]) * y
+ Global.fCalibrateOut[x][0]; // zakres <0;1>
if( Global.iCalibrateOutDebugInfo == x ) {
WriteLog( " calibrated=" + std::to_string( temp ) );
}
@@ -320,11 +319,13 @@ float Console::AnalogCalibrateGet(int x)
if (iMode == 4 && PoKeys55[0])
{
float b = PoKeys55[0]->fAnalog[x];
b = (((((Global.fCalibrateIn[x][5] * b) + Global.fCalibrateIn[x][4]) * b +
Global.fCalibrateIn[x][3]) * b +
Global.fCalibrateIn[x][2]) * b +
Global.fCalibrateIn[x][1]) * b +
Global.fCalibrateIn[x][0];
b = (((((
Global.fCalibrateIn[x][5] * b)
+ Global.fCalibrateIn[x][4]) * b
+ Global.fCalibrateIn[x][3]) * b
+ Global.fCalibrateIn[x][2]) * b
+ Global.fCalibrateIn[x][1]) * b
+ Global.fCalibrateIn[x][0];
if (x == 0) return (b + 2) / 8;
if (x == 1) return b/10;
else return b;

View File

@@ -2021,8 +2021,12 @@ bool TController::CheckVehicles(TOrders user)
pVehicles[1] = p; // zapamiętanie ostatniego
fLength += p->MoverParameters->Dim.L; // dodanie długości pojazdu
fMass += p->MoverParameters->TotalMass; // dodanie masy łącznie z ładunkiem
if (fVelMax < 0 ? true : p->MoverParameters->Vmax < fVelMax)
fVelMax = p->MoverParameters->Vmax; // ustalenie maksymalnej prędkości dla składu
fVelMax = min_speed( fVelMax, p->MoverParameters->Vmax ); // ustalenie maksymalnej prędkości dla składu
// reset oerlikon brakes consist flag as different type was detected
if( ( p->MoverParameters->BrakeSubsystem != TBrakeSubSystem::ss_ESt )
&& ( p->MoverParameters->BrakeSubsystem != TBrakeSubSystem::ss_LSt ) ) {
iDrivigFlags &= ~( moveOerlikons );
}
p = p->Neightbour(dir); // pojazd podłączony od wskazanej strony
}
if (main)
@@ -2034,9 +2038,8 @@ bool TController::CheckVehicles(TOrders user)
p = pVehicles[0];
while (p)
{
if (TrainParams)
if (p->asDestination == "none")
p->DestinationSet(TrainParams->Relation2, TrainParams->TrainName); // relacja docelowa, jeśli nie było
if (p->asDestination == "none")
p->DestinationSet(TrainParams->Relation2, TrainParams->TrainName); // relacja docelowa, jeśli nie było
if (AIControllFlag) // jeśli prowadzi komputer
p->RaLightsSet(0, 0); // gasimy światła
if (p->MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector)
@@ -3020,10 +3023,8 @@ void TController::SpeedSet()
mvOccupied->DirectionForward(); //żeby EN57 jechały na drugiej nastawie
{
if (mvControlling->MainCtrlPos &&
!mvControlling
->StLinFlag) // jak niby jedzie, ale ma rozłączone liniowe
mvControlling->DecMainCtrl(
2); // to na zero i czekać na przewalenie kułakowego
!mvControlling->StLinFlag) // jak niby jedzie, ale ma rozłączone liniowe
mvControlling->DecMainCtrl(2); // to na zero i czekać na przewalenie kułakowego
else
switch (mvControlling->MainCtrlPos)
{ // ruch nastawnika uzależniony jest od aktualnie ustawionej
@@ -5225,10 +5226,7 @@ TController::UpdateSituation(double dt) {
}
if( ( mvOccupied->BrakeCtrlPos < 0 )
&& ( mvOccupied->EqvtPipePress > (
fReady < 0.25 ?
5.1 :
5.2 ) ) ) {
&& ( mvOccupied->EqvtPipePress > ( fReady < 0.25 ? 5.1 : 5.2 ) ) ) {
mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) );
}
}

View File

@@ -4203,6 +4203,21 @@ void TDynamicObject::RenderSounds() {
}
}
}
// door locks
if( MoverParameters->DoorBlockedFlag() != m_doorlocks ) {
// toggle state of the locks...
m_doorlocks = !m_doorlocks;
// ...and play relevant sounds
for( auto &door : m_doorsounds ) {
if( m_doorlocks ) {
door.lock.play( sound_flags::exclusive );
}
else {
door.unlock.play( sound_flags::exclusive );
}
}
}
// horns
if( TestFlag( MoverParameters->WarningSignal, 1 ) ) {
sHorn1.play( sound_flags::exclusive | sound_flags::looping );
@@ -5413,7 +5428,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
}
else if( token == "dooropen:" ) {
sound_source soundtemplate { sound_placement::general };
sound_source soundtemplate { sound_placement::general, 25.f };
soundtemplate.deserialize( parser, sound_type::single );
soundtemplate.owner( this );
for( auto &door : m_doorsounds ) {
@@ -5425,7 +5440,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
}
else if( token == "doorclose:" ) {
sound_source soundtemplate { sound_placement::general };
sound_source soundtemplate { sound_placement::general, 25.f };
soundtemplate.deserialize( parser, sound_type::single );
soundtemplate.owner( this );
for( auto &door : m_doorsounds ) {
@@ -5436,8 +5451,32 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
}
}
else if( token == "doorlock:" ) {
sound_source soundtemplate { sound_placement::general, 12.5f };
soundtemplate.deserialize( parser, sound_type::single );
soundtemplate.owner( this );
for( auto &door : m_doorsounds ) {
// apply configuration to all defined doors, but preserve their individual offsets
auto const dooroffset { door.lock.offset() };
door.lock = soundtemplate;
door.lock.offset( dooroffset );
}
}
else if( token == "doorunlock:" ) {
sound_source soundtemplate { sound_placement::general, 12.5f };
soundtemplate.deserialize( parser, sound_type::single );
soundtemplate.owner( this );
for( auto &door : m_doorsounds ) {
// apply configuration to all defined doors, but preserve their individual offsets
auto const dooroffset { door.unlock.offset() };
door.unlock = soundtemplate;
door.unlock.offset( dooroffset );
}
}
else if( token == "doorstepopen:" ) {
sound_source soundtemplate { sound_placement::general };
sound_source soundtemplate { sound_placement::general, 20.f };
soundtemplate.deserialize( parser, sound_type::single );
soundtemplate.owner( this );
for( auto &door : m_doorsounds ) {
@@ -5449,7 +5488,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
}
else if( token == "doorstepclose:" ) {
sound_source soundtemplate { sound_placement::general };
sound_source soundtemplate { sound_placement::general, 20.f };
soundtemplate.deserialize( parser, sound_type::single );
soundtemplate.owner( this );
for( auto &door : m_doorsounds ) {
@@ -5539,6 +5578,8 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
auto const location { glm::vec3 { MoverParameters->Dim.W * 0.5f, MoverParameters->Dim.H * 0.5f, offset } };
door.rsDoorClose.offset( location );
door.rsDoorOpen.offset( location );
door.lock.offset( location );
door.unlock.offset( location );
door.step_close.offset( location );
door.step_open.offset( location );
m_doorsounds.emplace_back( door );
@@ -5549,6 +5590,8 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
auto const location { glm::vec3 { MoverParameters->Dim.W * -0.5f, MoverParameters->Dim.H * 0.5f, offset } };
door.rsDoorClose.offset( location );
door.rsDoorOpen.offset( location );
door.lock.offset( location );
door.unlock.offset( location );
door.step_close.offset( location );
door.step_open.offset( location );
m_doorsounds.emplace_back( door );

View File

@@ -305,10 +305,12 @@ private:
};
struct door_sounds {
sound_source rsDoorOpen { sound_placement::general, 25.f }; // Ra: przeniesione z kabiny
sound_source rsDoorClose { sound_placement::general, 25.f };
sound_source step_open { sound_placement::general, 25.f };
sound_source step_close { sound_placement::general, 25.f };
sound_source rsDoorOpen { sound_placement::general }; // Ra: przeniesione z kabiny
sound_source rsDoorClose { sound_placement::general };
sound_source lock { sound_placement::general };
sound_source unlock { sound_placement::general };
sound_source step_open { sound_placement::general };
sound_source step_close { sound_placement::general };
};
struct exchange_sounds {
@@ -423,6 +425,7 @@ private:
std::array<coupler_sounds, 2> m_couplersounds; // always front and rear
std::vector<pantograph_sounds> m_pantographsounds; // typically 2 but can be less (or more?)
std::vector<door_sounds> m_doorsounds; // can expect symmetrical arrangement, but don't count on it
bool m_doorlocks { false }; // sound helper, current state of door locks
sound_source sDepartureSignal { sound_placement::general };
sound_source sHorn1 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE };
sound_source sHorn2 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE };

View File

@@ -1764,11 +1764,15 @@ event_manager::export_as_text( std::ostream &Output ) const {
Output << "// events\n";
for( auto const *event : m_events ) {
event->export_as_text( Output );
if( event->group() == null_handle ) {
event->export_as_text( Output );
}
}
Output << "// event launchers\n";
for( auto const *launcher : m_launchers.sequence() ) {
launcher->export_as_text( Output );
if( launcher->group() == null_handle ) {
launcher->export_as_text( Output );
}
}
}

19
Event.h
View File

@@ -95,6 +95,10 @@ public:
bool StopCommand();
void StopCommandSent();
void Append(TEvent *e);
void
group( scene::group_handle Group );
scene::group_handle
group() const;
// members
std::string asName;
bool m_ignored { false }; // replacement for tp_ignored
@@ -114,9 +118,24 @@ public:
bool m_conditionalelse { false }; // TODO: make a part of condition struct
private:
// methods
void Conditions( cParser *parser, std::string s );
// members
scene::group_handle m_group { null_handle }; // group this event belongs to, if any
};
inline
void
TEvent::group( scene::group_handle Group ) {
m_group = Group;
}
inline
scene::group_handle
TEvent::group() const {
return m_group;
}
class event_manager {
public:

View File

@@ -141,12 +141,12 @@ struct global_settings {
double fBrakeStep{ 1.0 }; // krok zmiany hamulca dla klawiszy [Num3] i [Num9]
// parametry kalibracyjne wejść z pulpitu
double fCalibrateIn[ 6 ][ 6 ] = {
{ 0, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 0 } };
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0 } };
// parametry kalibracyjne wyjść dla pulpitu
double fCalibrateOut[ 7 ][ 6 ] = {
{ 0, 1, 0, 0, 0, 0 },

133
Train.cpp
View File

@@ -4409,13 +4409,16 @@ void TTrain::UpdateMechPosition(double dt)
&& ( true == mvOccupied->TruckHunting ) ) {
// hunting oscillation
HuntingAngle = clamp_circular( HuntingAngle + 4.0 * HuntingShake.frequency * dt * mvOccupied->Vel, 360.0 );
shakevector.x +=
( std::sin( glm::radians( HuntingAngle ) ) * dt * HuntingShake.scale )
* interpolate(
auto const huntingamount =
interpolate(
0.0, 1.0,
clamp(
( mvOccupied->Vel - HuntingShake.fadein_begin ) / ( HuntingShake.fadein_end - HuntingShake.fadein_begin ),
0.0, 1.0 ) );
shakevector.x +=
( std::sin( glm::radians( HuntingAngle ) ) * dt * HuntingShake.scale )
* huntingamount;
IsHunting = ( huntingamount > 0.025 );
}
if( iVel > 0.5 ) {
@@ -5873,60 +5876,24 @@ TTrain::update_sounds( double const Deltatime ) {
&& ( false == Global.CabWindowOpen )
&& ( DynamicObject->GetVelocity() > 0.5 ) ) {
// frequency calculation
auto const normalizer { (
true == rsRunningNoise.is_combined() ?
mvOccupied->Vmax * 0.01f :
1.f ) };
auto const frequency {
rsRunningNoise.m_frequencyoffset
+ rsRunningNoise.m_frequencyfactor * mvOccupied->Vel * normalizer };
// volume calculation
volume =
rsRunningNoise.m_amplitudeoffset
+ rsRunningNoise.m_amplitudefactor * mvOccupied->Vel;
if( std::abs( mvOccupied->nrot ) > 0.01 ) {
// hamulce wzmagaja halas
auto const brakeforceratio { (
clamp(
mvOccupied->UnitBrakeForce / std::max( 1.0, mvOccupied->BrakeForceR( 1.0, mvOccupied->Vel ) / ( mvOccupied->NAxles * std::max( 1, mvOccupied->NBpA ) ) ),
0.0, 1.0 ) ) };
volume *= 1 + 0.125 * brakeforceratio;
}
// scale volume by track quality
// TODO: track quality and/or environment factors as separate subroutine
volume *=
interpolate(
0.8, 1.2,
clamp(
DynamicObject->MyTrack->iQualityFlag / 20.0,
0.0, 1.0 ) );
// for single sample sounds muffle the playback at low speeds
if( false == rsRunningNoise.is_combined() ) {
volume *=
interpolate(
0.0, 1.0,
clamp(
mvOccupied->Vel / 40.0,
0.0, 1.0 ) );
}
if( volume > 0.05 ) {
rsRunningNoise
.pitch( frequency )
.gain( volume )
.play( sound_flags::exclusive | sound_flags::looping );
}
else {
rsRunningNoise.stop();
}
update_sounds_runningnoise( rsRunningNoise );
}
else {
// don't play the optional ending sound if the listener switches views
rsRunningNoise.stop( true == FreeFlyModeFlag );
}
// hunting oscillation noise
if( ( false == FreeFlyModeFlag )
&& ( false == Global.CabWindowOpen )
&& ( DynamicObject->GetVelocity() > 0.5 )
&& ( IsHunting ) ) {
update_sounds_runningnoise( rsHuntingNoise );
}
else {
// don't play the optional ending sound if the listener switches views
rsHuntingNoise.stop( true == FreeFlyModeFlag );
}
// McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa
if (mvOccupied->SecuritySystem.Status > 0) {
@@ -5977,6 +5944,58 @@ TTrain::update_sounds( double const Deltatime ) {
}
}
void TTrain::update_sounds_runningnoise( sound_source &Sound ) {
// frequency calculation
auto const normalizer { (
true == Sound.is_combined() ?
mvOccupied->Vmax * 0.01f :
1.f ) };
auto const frequency {
Sound.m_frequencyoffset
+ Sound.m_frequencyfactor * mvOccupied->Vel * normalizer };
// volume calculation
auto volume =
Sound.m_amplitudeoffset
+ Sound.m_amplitudefactor * mvOccupied->Vel;
if( std::abs( mvOccupied->nrot ) > 0.01 ) {
// hamulce wzmagaja halas
auto const brakeforceratio { (
clamp(
mvOccupied->UnitBrakeForce / std::max( 1.0, mvOccupied->BrakeForceR( 1.0, mvOccupied->Vel ) / ( mvOccupied->NAxles * std::max( 1, mvOccupied->NBpA ) ) ),
0.0, 1.0 ) ) };
volume *= 1 + 0.125 * brakeforceratio;
}
// scale volume by track quality
// TODO: track quality and/or environment factors as separate subroutine
volume *=
interpolate(
0.8, 1.2,
clamp(
DynamicObject->MyTrack->iQualityFlag / 20.0,
0.0, 1.0 ) );
// for single sample sounds muffle the playback at low speeds
if( false == Sound.is_combined() ) {
volume *=
interpolate(
0.0, 1.0,
clamp(
mvOccupied->Vel / 40.0,
0.0, 1.0 ) );
}
if( volume > 0.05 ) {
Sound
.pitch( frequency )
.gain( volume )
.play( sound_flags::exclusive | sound_flags::looping );
}
else {
Sound.stop();
}
}
bool TTrain::CabChange(int iDirection)
{ // McZapkie-090902: zmiana kabiny 1->0->2 i z powrotem
if( ( DynamicObject->Mechanik == nullptr )
@@ -6146,6 +6165,14 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName)
rsRunningNoise.m_amplitudefactor /= ( 1 + mvOccupied->Vmax );
rsRunningNoise.m_frequencyfactor /= ( 1 + mvOccupied->Vmax );
}
else if( token == "huntingnoise:" ) {
// hunting oscillation sound:
rsHuntingNoise.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax );
rsHuntingNoise.owner( DynamicObject );
rsHuntingNoise.m_amplitudefactor /= ( 1 + mvOccupied->Vmax );
rsHuntingNoise.m_frequencyfactor /= ( 1 + mvOccupied->Vmax );
}
else if (token == "mechspring:")
{
// parametry bujania kamery:
@@ -6217,7 +6244,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
&dsbReverserKey, &dsbNastawnikJazdy, &dsbNastawnikBocz,
&dsbSwitch, &dsbPneumaticSwitch,
&rsHiss, &rsHissU, &rsHissE, &rsHissX, &rsHissT, &rsSBHiss, &rsSBHissU,
&rsFadeSound, &rsRunningNoise,
&rsFadeSound, &rsRunningNoise, &rsHuntingNoise,
&dsbHasler, &dsbBuzzer, &dsbSlipAlarm, &m_radiosound, &m_radiostop
};
for( auto sound : sounds ) {

View File

@@ -142,6 +142,7 @@ class TTrain
void set_paired_open_motor_connectors_button( bool const State );
// update function subroutines
void update_sounds( double const Deltatime );
void update_sounds_runningnoise( sound_source &Sound );
// command handlers
// NOTE: we're currently using universal handlers and static handler map but it may be beneficial to have these implemented on individual class instance basis
@@ -572,6 +573,7 @@ public: // reszta może by?publiczna
float fadein_end { 0.f }; // full effect speed in km/h
} HuntingShake;
float HuntingAngle { 0.f }; // crude approximation of hunting oscillation; current angle of sine wave
bool IsHunting { false };
sound_source dsbReverserKey { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-121211
sound_source dsbNastawnikJazdy { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
@@ -591,6 +593,7 @@ public: // reszta może by?publiczna
sound_source rsFadeSound { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source rsRunningNoise{ sound_placement::internal, EU07_SOUND_GLOBALRANGE };
sound_source rsHuntingNoise{ sound_placement::internal, EU07_SOUND_GLOBALRANGE };
sound_source dsbHasler { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };
sound_source dsbBuzzer { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };

View File

@@ -37,9 +37,10 @@ basic_editor::translate( scene::basic_node *Node, glm::dvec3 const &Location, bo
else {
// translate entire group
// TODO: contextual switch between group and item translation
auto nodegroup { scene::Groups.group( Node->group() ) };
// TODO: translation of affected/relevant events
auto &nodegroup { scene::Groups.group( Node->group() ).nodes };
std::for_each(
nodegroup.first, nodegroup.second,
std::begin( nodegroup ), std::end( nodegroup ),
[&]( auto *node ) {
translate_node( node, node->location() + translation ); } );
}
@@ -59,9 +60,10 @@ basic_editor::translate( scene::basic_node *Node, float const Offset ) {
else {
// translate entire group
// TODO: contextual switch between group and item translation
auto nodegroup { scene::Groups.group( Node->group() ) };
// TODO: translation of affected/relevant events
auto &nodegroup { scene::Groups.group( Node->group() ).nodes };
std::for_each(
nodegroup.first, nodegroup.second,
std::begin( nodegroup ), std::end( nodegroup ),
[&]( auto *node ) {
translate_node( node, offset ); } );
}
@@ -146,10 +148,11 @@ basic_editor::rotate( scene::basic_node *Node, glm::vec3 const &Angle, float con
else {
// rotate entire group
// TODO: contextual switch between group and item rotation
// TODO: translation of affected/relevant events
auto const rotationcenter { Node->location() };
auto nodegroup { scene::Groups.group( Node->group() ) };
auto &nodegroup { scene::Groups.group( Node->group() ).nodes };
std::for_each(
nodegroup.first, nodegroup.second,
std::begin( nodegroup ), std::end( nodegroup ),
[&]( auto *node ) {
rotate_node( node, rotation );
if( node != Node ) {

View File

@@ -10,6 +10,9 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "scenenodegroups.h"
#include "event.h"
#include "memcell.h"
namespace scene {
node_groups Groups;
@@ -37,7 +40,7 @@ node_groups::close() {
auto lookup { m_groupmap.find( closinggroup ) };
if( ( lookup != m_groupmap.end() )
&& ( lookup->second.size() <= 1 ) ) {
&& ( ( lookup->second.nodes.size() + lookup->second.events.size() ) <= 1 ) ) {
erase( lookup );
}
@@ -65,20 +68,62 @@ node_groups::insert( scene::group_handle const Group, scene::basic_node *Node )
if( Group == null_handle ) { return; }
auto &nodesequence { m_groupmap[ Group ] };
auto &nodesequence { m_groupmap[ Group ].nodes };
if( std::find( std::begin( nodesequence ), std::end( nodesequence ), Node ) == std::end( nodesequence ) ) {
// don't add the same node twice
nodesequence.emplace_back( Node );
}
}
// places provided event in specified group
void
node_groups::insert( scene::group_handle const Group, TEvent *Event ) {
// TBD, TODO: automatically unregister the event from its current group?
Event->group( Group );
if( Group == null_handle ) { return; }
auto &eventsequence { m_groupmap[ Group ].events };
if( std::find( std::begin( eventsequence ), std::end( eventsequence ), Event ) == std::end( eventsequence ) ) {
// don't add the same node twice
eventsequence.emplace_back( Event );
}
}
// sends basic content of the class in legacy (text) format to provided stream
void
node_groups::export_as_text( std::ostream &Output ) const {
for( auto const &group : m_groupmap ) {
Output << "group\n";
for( auto *node : group.second.nodes ) {
// HACK: auto-generated memory cells aren't exported, so we check for this
// TODO: is_exportable as basic_node method
if( ( typeid( *node ) == typeid( TMemCell ) )
&& ( false == static_cast<TMemCell *>( node )->is_exportable ) ) {
continue;
}
node->export_as_text( Output );
}
for( auto *event : group.second.events ) {
event->export_as_text( Output );
}
Output << "endgroup\n";
}
}
// removes specified group from the group list and group information from the group's nodes
void
node_groups::erase( group_map::const_iterator Group ) {
for( auto *node : Group->second ) {
for( auto *node : Group->second.nodes ) {
node->group( null_handle );
}
for( auto *event : Group->second.events ) {
event->group( null_handle );
}
m_groupmap.erase( Group );
}

View File

@@ -13,14 +13,16 @@ http://mozilla.org/MPL/2.0/.
namespace scene {
struct basic_group {
// members
std::vector<scene::basic_node *> nodes;
std::vector<TEvent *> events;
};
// holds lists of grouped scene nodes
class node_groups {
// NOTE: during scenario deserialization encountering *.inc file causes creation of a new group on the group stack
// this allows all nodes listed in this *.inc file to be grouped and potentially modified together by the editor.
private:
// types
using node_sequence = std::vector<scene::basic_node *>;
public:
// constructors
node_groups() = default;
@@ -37,14 +39,20 @@ public:
// places provided node in specified group
void
insert( scene::group_handle const Group, scene::basic_node *Node );
std::pair<node_sequence::iterator, node_sequence::iterator>
// places provided event in specified group
void
insert( scene::group_handle const Group, TEvent *Event );
// grants direct access to specified group
scene::basic_group &
group( scene::group_handle const Group ) {
auto &group { m_groupmap[ Group ] };
return { std::begin( group ), std::end( group ) }; }
return m_groupmap[ Group ]; }
// sends basic content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
private:
// types
using group_map = std::unordered_map<scene::group_handle, node_sequence>;
using group_map = std::unordered_map<scene::group_handle, scene::basic_group>;
// methods
// removes specified group from the group list and group information from the group's nodes
void

View File

@@ -73,6 +73,8 @@ state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad )
{ "description", &state_serializer::deserialize_description },
{ "event", &state_serializer::deserialize_event },
{ "firstinit", &state_serializer::deserialize_firstinit },
{ "group", &state_serializer::deserialize_group },
{ "endgroup", &state_serializer::deserialize_endgroup },
{ "light", &state_serializer::deserialize_light },
{ "node", &state_serializer::deserialize_node },
{ "origin", &state_serializer::deserialize_origin },
@@ -237,7 +239,10 @@ state_serializer::deserialize_event( cParser &Input, scene::scratch_data &Scratc
Scratchpad.location.offset.top().z ) );
event->Load( &Input, offset );
if( false == simulation::Events.insert( event ) ) {
if( true == simulation::Events.insert( event ) ) {
scene::Groups.insert( scene::Groups.handle(), event );
}
else {
delete event;
}
}
@@ -256,6 +261,18 @@ state_serializer::deserialize_firstinit( cParser &Input, scene::scratch_data &Sc
Scratchpad.initialized = true;
}
void
state_serializer::deserialize_group( cParser &Input, scene::scratch_data &Scratchpad ) {
scene::Groups.create();
}
void
state_serializer::deserialize_endgroup( cParser &Input, scene::scratch_data &Scratchpad ) {
scene::Groups.close();
}
void
state_serializer::deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ) {
@@ -897,27 +914,39 @@ state_serializer::export_as_text( std::string const &Scenariofile ) const {
filename = Global.asCurrentSceneryPath + filename + "_export";
std::ofstream scmfile { filename + ".scm" };
// groups
scmfile << "// groups\n";
scene::Groups.export_as_text( scmfile );
// tracks
scmfile << "// paths\n";
for( auto const *path : Paths.sequence() ) {
path->export_as_text( scmfile );
if( path->group() == null_handle ) {
path->export_as_text( scmfile );
}
}
// traction
scmfile << "// traction\n";
for( auto const *traction : Traction.sequence() ) {
traction->export_as_text( scmfile );
if( traction->group() == null_handle ) {
traction->export_as_text( scmfile );
}
}
// power grid
scmfile << "// traction power sources\n";
for( auto const *powersource : Powergrid.sequence() ) {
powersource->export_as_text( scmfile );
if( powersource->group() == null_handle ) {
powersource->export_as_text( scmfile );
}
}
// models
scmfile << "// instanced models\n";
for( auto const *instance : Instances.sequence() ) {
instance->export_as_text( scmfile );
if( instance->group() == null_handle ) {
instance->export_as_text( scmfile );
}
}
// sounds
// NOTE: sounds currently aren't included in groups
scmfile << "// sounds\n";
Region->export_as_text( scmfile );
@@ -925,7 +954,8 @@ state_serializer::export_as_text( std::string const &Scenariofile ) const {
// mem cells
ctrfile << "// memory cells\n";
for( auto const *memorycell : Memory.sequence() ) {
if( true == memorycell->is_exportable ) {
if( ( true == memorycell->is_exportable )
&& ( memorycell->group() == null_handle ) ) {
memorycell->export_as_text( ctrfile );
}
}

View File

@@ -35,6 +35,8 @@ private:
void deserialize_description( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_event( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_group( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_endgroup( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_light( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad );