mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-08 21:00:03 +01:00
Deleting a model in the editor left the renderer holding it as the picked node, the next pick used freed memory: the renderer forgets it now (Forget_Node). A texture filled straight from memory (internalsrc:) can't be read again, released by the garbage sweep it came back grey; it stays now, and the units bound with the old name are rebound. The arrays of the ED brake force sharing are vectors, they leaked once per update. The scene hierarchy and the editor menu items go through the translation. Python caches ignored.
599 lines
20 KiB
C++
599 lines
20 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "simulation/simulation.h"
|
|
#include "simulation/simulationtime.h"
|
|
#include "simulation/simulationenvironment.h"
|
|
|
|
#include "utilities/Globals.h"
|
|
#include "world/Event.h"
|
|
#include "world/MemCell.h"
|
|
#include "world/Track.h"
|
|
#include "world/Road.h"
|
|
#include "world/Sweep.h"
|
|
#include "world/RoadPoint.h"
|
|
#include "world/Traction.h"
|
|
#include "world/TractionPower.h"
|
|
#include "audio/sound.h"
|
|
#include "model/AnimModel.h"
|
|
#include "vehicle/DynObj.h"
|
|
#include "rendering/lightarray.h"
|
|
#include "rendering/particles.h"
|
|
#include "scene/scene.h"
|
|
#include "scene/scenelayers.h"
|
|
#include "vehicle/Train.h"
|
|
#include "application/application.h"
|
|
#include "rendering/renderer.h"
|
|
#include "utilities/Logs.h"
|
|
#include "vehicle/Driver.h"
|
|
|
|
namespace simulation {
|
|
|
|
state_manager State;
|
|
event_manager Events;
|
|
memory_table Memory;
|
|
path_table Paths;
|
|
road_table Roads;
|
|
sweep_table Sweeps;
|
|
junction_table Junctions;
|
|
roadpoint_table Roadpoints;
|
|
traction_table Traction;
|
|
powergridsource_table Powergrid;
|
|
instance_table Instances;
|
|
vehicle_table Vehicles;
|
|
train_table Trains;
|
|
light_array Lights;
|
|
particle_manager Particles;
|
|
|
|
#ifdef WITH_LUA
|
|
lua Lua;
|
|
#endif
|
|
|
|
scene::basic_region *Region { nullptr };
|
|
TTrain *Train { nullptr };
|
|
|
|
uint16_t prev_train_id { 0 };
|
|
bool is_ready { false };
|
|
|
|
std::shared_ptr<deserializer_state>
|
|
state_manager::deserialize_begin(std::string const &Scenariofile) {
|
|
|
|
return m_serializer.deserialize_begin( Scenariofile );
|
|
}
|
|
|
|
bool
|
|
state_manager::deserialize_continue(std::shared_ptr<deserializer_state> state) {
|
|
|
|
return m_serializer.deserialize_continue(state);
|
|
}
|
|
|
|
// stores class data in specified file, in legacy (text) format
|
|
void
|
|
state_manager::export_as_text( std::string const &Scenariofile ) const {
|
|
|
|
return m_serializer.export_as_text( Scenariofile );
|
|
}
|
|
|
|
void
|
|
state_manager::init_scripting_interface() {
|
|
|
|
// create scenario data memory cells
|
|
{
|
|
auto *memorycell = new TMemCell( {
|
|
0, -1,
|
|
"__simulation.weather",
|
|
"memcell" } );
|
|
simulation::Memory.insert( memorycell );
|
|
simulation::Region->insert( memorycell );
|
|
}
|
|
{
|
|
auto *memorycell = new TMemCell( {
|
|
0, -1,
|
|
"__simulation.time",
|
|
"memcell" } );
|
|
simulation::Memory.insert( memorycell );
|
|
simulation::Region->insert( memorycell );
|
|
}
|
|
{
|
|
auto *memorycell = new TMemCell( {
|
|
0, -1,
|
|
"__simulation.date",
|
|
"memcell" } );
|
|
simulation::Memory.insert( memorycell );
|
|
simulation::Region->insert( memorycell );
|
|
}
|
|
}
|
|
|
|
// legacy method, calculates changes in simulation state over specified time
|
|
void
|
|
state_manager::update( double const Deltatime, int Iterationcount ) {
|
|
// aktualizacja animacji krokiem FPS: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń
|
|
if (Deltatime == 0.0) { return; }
|
|
|
|
auto const totaltime { Deltatime * Iterationcount };
|
|
// NOTE: we perform animations first, as they can determine factors like contact with powergrid
|
|
TAnimModel::AnimUpdate( totaltime ); // wykonanie zakolejkowanych animacji
|
|
|
|
simulation::Powergrid.update( totaltime );
|
|
// road junctions, level crossings and the points road vehicles appear at; ahead of the vehicles, so the drivers act on what these decide
|
|
simulation::Junctions.update( totaltime );
|
|
simulation::Roadpoints.update( totaltime );
|
|
simulation::Vehicles.update( Deltatime, Iterationcount );
|
|
}
|
|
|
|
void
|
|
state_manager::update_clocks() {
|
|
|
|
// Ra 2014-07: przeliczenie kąta czasu (do animacji zależnych od czasu)
|
|
auto const &time = simulation::Time.data();
|
|
Global.fTimeAngleDeg = time.wHour * 15.0 + time.wMinute * 0.25 + (time.wSecond + 0.001 * time.wMilliseconds) / 240.0;
|
|
Global.fClockAngleDeg[ 0 ] = 36.0 * ( time.wSecond % 10 ); // jednostki sekund
|
|
Global.fClockAngleDeg[ 1 ] = 36.0 * ( time.wSecond / 10 ); // dziesiątki sekund
|
|
Global.fClockAngleDeg[ 2 ] = 36.0 * ( time.wMinute % 10 ); // jednostki minut
|
|
Global.fClockAngleDeg[ 3 ] = 36.0 * ( time.wMinute / 10 ); // dziesiątki minut
|
|
Global.fClockAngleDeg[ 4 ] = 36.0 * ( time.wHour % 10 ); // jednostki godzin
|
|
Global.fClockAngleDeg[ 5 ] = 36.0 * ( time.wHour / 10 ); // dziesiątki godzin
|
|
}
|
|
|
|
void
|
|
state_manager::update_scripting_interface() {
|
|
|
|
auto *weather{ Memory.find( "__simulation.weather" ) };
|
|
auto *time{ Memory.find( "__simulation.time" ) };
|
|
auto *date{ Memory.find( "__simulation.date" ) };
|
|
|
|
if( weather == nullptr || time == nullptr || date == nullptr ) {
|
|
// scripting interface cells weren't created, nothing to synchronize
|
|
return;
|
|
}
|
|
|
|
if( simulation::is_ready ) {
|
|
// potentially adjust weather
|
|
if( weather->Value1() != m_scriptinginterface.weather->Value1() ) {
|
|
Global.Overcast = std::clamp( (float)weather->Value1(), 0.f, 2.f );
|
|
simulation::Environment.compute_weather();
|
|
}
|
|
if( weather->Value2() != m_scriptinginterface.weather->Value2() ) {
|
|
Global.fFogEnd = std::clamp( (float)weather->Value2(), 10.f, 25000.f );
|
|
}
|
|
}
|
|
else {
|
|
m_scriptinginterface.weather = std::make_shared<TMemCell>( scene::node_data() );
|
|
m_scriptinginterface.date = std::make_shared<TMemCell>( scene::node_data() );
|
|
m_scriptinginterface.time = std::make_shared<TMemCell>( scene::node_data() );
|
|
}
|
|
|
|
// update scripting interface
|
|
weather->UpdateValues(
|
|
Global.Weather,
|
|
Global.Overcast,
|
|
Global.fFogEnd,
|
|
basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2 );
|
|
|
|
time->UpdateValues(
|
|
Global.Period,
|
|
Time.data().wHour,
|
|
Time.data().wMinute,
|
|
basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2 );
|
|
|
|
date->UpdateValues(
|
|
Global.Season,
|
|
Time.year_day(),
|
|
0,
|
|
basic_event::flags::text | basic_event::flags::value1 );
|
|
|
|
// cache cell state to detect potential script-issued changes on next cycle
|
|
*m_scriptinginterface.weather = *weather;
|
|
*m_scriptinginterface.time = *time;
|
|
*m_scriptinginterface.date = *date;
|
|
}
|
|
|
|
void state_manager::process_commands() {
|
|
command_data commanddata;
|
|
while( Commands.pop( commanddata, (uint32_t)command_target::simulation )) {
|
|
if (commanddata.command == user_command::consistreleaser) {
|
|
TDynamicObject *found_vehicle = simulation::Vehicles.find(commanddata.payload);
|
|
TDynamicObject *vehicle = found_vehicle;
|
|
|
|
while (vehicle) {
|
|
vehicle->MoverParameters->Hamulec->Releaser(commanddata.action != GLFW_RELEASE ? 1 : 0);
|
|
vehicle = vehicle->Next();
|
|
}
|
|
|
|
vehicle = found_vehicle;
|
|
while (vehicle) {
|
|
vehicle->MoverParameters->Hamulec->Releaser(commanddata.action != GLFW_RELEASE ? 1 : 0);
|
|
vehicle = vehicle->Prev();
|
|
}
|
|
}
|
|
|
|
if (commanddata.action == GLFW_RELEASE) {
|
|
if (commanddata.command == user_command::debugtoggle)
|
|
DebugModeFlag = !DebugModeFlag;
|
|
|
|
if (commanddata.command == user_command::pausetoggle) {
|
|
if( Global.iPause & 1 ) {
|
|
// jeśli pauza startowa
|
|
// odpauzowanie, gdy po wczytaniu miało nie startować
|
|
Global.iPause ^= 1;
|
|
}
|
|
else {
|
|
Global.iPause ^= 2; // zmiana stanu zapauzowania
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (commanddata.command == user_command::focuspauseset) {
|
|
if( commanddata.param1 == 1.0 )
|
|
Global.iPause &= ~4; // odpauzowanie, gdy jest na pierwszym planie
|
|
else
|
|
Global.iPause |= 4; // włączenie pauzy, gdy nieaktywy
|
|
}
|
|
|
|
if (commanddata.command == user_command::entervehicle) {
|
|
// przesiadka do innego pojazdu
|
|
if( commanddata.payload == "ghostview" ) {
|
|
continue;
|
|
}
|
|
|
|
if (!commanddata.freefly)
|
|
// only available in free fly mode
|
|
continue;
|
|
|
|
// NOTE: because malformed scenario can have vehicle name duplicates we first try to locate vehicle in world, with name search as fallback
|
|
TDynamicObject *targetvehicle = std::get<TDynamicObject *>( simulation::Region->find_vehicle( commanddata.location, 50, false, false ) );
|
|
if( targetvehicle == nullptr || targetvehicle->name() != commanddata.payload ) {
|
|
targetvehicle = simulation::Vehicles.find( commanddata.payload );
|
|
}
|
|
|
|
if (!targetvehicle)
|
|
continue;
|
|
|
|
auto *senderlocaltrain { simulation::Trains.find_id( static_cast<std::uint16_t>( commanddata.param2 ) ) };
|
|
if( senderlocaltrain ) {
|
|
auto *currentvehicle { senderlocaltrain->Dynamic() };
|
|
auto const samevehicle { currentvehicle == targetvehicle };
|
|
|
|
if( samevehicle ) {
|
|
// we already control desired vehicle so don't overcomplicate things
|
|
continue;
|
|
}
|
|
|
|
auto const sameconsist{
|
|
targetvehicle->ctOwner == currentvehicle->Mechanik
|
|
|| targetvehicle->ctOwner == currentvehicle->ctOwner };
|
|
auto const isincharge{ currentvehicle->Mechanik->primary() };
|
|
auto const aidriveractive{ currentvehicle->Mechanik->AIControllFlag };
|
|
// TODO: support for primary mode request passed as commanddata.param1
|
|
if( !sameconsist && isincharge ) {
|
|
// oddajemy dotychczasowy AI
|
|
if (currentvehicle->Mechanik != nullptr)
|
|
currentvehicle->Mechanik->TakeControl( true );
|
|
}
|
|
|
|
if( sameconsist && !aidriveractive ) {
|
|
// since we're consist owner we can simply move to the destination vehicle
|
|
senderlocaltrain->MoveToVehicle( targetvehicle );
|
|
if (senderlocaltrain->Dynamic()->Mechanik != nullptr)
|
|
senderlocaltrain->Dynamic()->Mechanik->TakeControl( false, true );
|
|
}
|
|
}
|
|
|
|
auto *train { simulation::Trains.find( targetvehicle->name() ) };
|
|
|
|
if (train)
|
|
continue;
|
|
|
|
train = new TTrain();
|
|
if (train->Init(targetvehicle)) {
|
|
simulation::Trains.insert(train);
|
|
}
|
|
else {
|
|
delete train;
|
|
train = nullptr;
|
|
if( targetvehicle->name() == Global.local_start_vehicle ) {
|
|
ErrorLog( "Failed to initialize player train, \"" + Global.local_start_vehicle + "\"" );
|
|
Global.local_start_vehicle = "ghostview";
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (commanddata.command == user_command::queueevent) {
|
|
std::istringstream ss(commanddata.payload);
|
|
|
|
std::string event_name;
|
|
std::string vehicle_name;
|
|
std::getline(ss, event_name, '%');
|
|
std::getline(ss, vehicle_name, '%');
|
|
|
|
basic_event *ev = Events.FindEvent(event_name);
|
|
TDynamicObject *vehicle = nullptr;
|
|
if (!vehicle_name.empty())
|
|
vehicle = simulation::Vehicles.find(vehicle_name);
|
|
|
|
if (ev)
|
|
Events.AddToQuery(ev, vehicle);
|
|
}
|
|
|
|
if (commanddata.command == user_command::setlight) {
|
|
int light = std::round(commanddata.param1);
|
|
float state = commanddata.param2;
|
|
TAnimModel *model = simulation::Instances.find(commanddata.payload);
|
|
if (model)
|
|
model->LightSet(light, state);
|
|
}
|
|
|
|
if (commanddata.command == user_command::setdatetime) {
|
|
int yearday = std::round(commanddata.param1);
|
|
int minute = std::round(commanddata.param2);
|
|
simulation::Time.set_time(yearday, minute);
|
|
|
|
auto const weather { Global.Weather };
|
|
simulation::Environment.compute_season(yearday);
|
|
if( weather != Global.Weather ) {
|
|
// HACK: force re-calculation of precipitation
|
|
Global.Overcast = std::clamp( Global.Overcast - 0.0001f, 0.0f, 2.0f );
|
|
}
|
|
|
|
simulation::Environment.update_moon();
|
|
}
|
|
|
|
if (commanddata.command == user_command::setweather) {
|
|
Global.fFogEnd = commanddata.param1;
|
|
Global.Overcast = commanddata.param2;
|
|
|
|
simulation::Environment.compute_weather();
|
|
}
|
|
|
|
if (commanddata.command == user_command::settemperature) {
|
|
Global.AirTemperature = commanddata.param1;
|
|
Global.Overcast = commanddata.param2;
|
|
simulation::Environment.compute_weather();
|
|
}
|
|
|
|
if (commanddata.command == user_command::insertmodel) {
|
|
std::istringstream ss(commanddata.payload);
|
|
|
|
std::string name;
|
|
std::string data;
|
|
std::getline(ss, name, ':');
|
|
std::getline(ss, data, ':');
|
|
|
|
TAnimModel *model = simulation::State.create_model(data, name, commanddata.location);
|
|
simulation::State.create_eventlauncher("node -1 0 launcher eventlauncher 0 0 0 0.8 none -10000.0 obstacle_collision traintriggered end", name + "_snd", commanddata.location);
|
|
}
|
|
|
|
if (commanddata.command == user_command::deletemodel) {
|
|
simulation::State.delete_model(simulation::Instances.find(commanddata.payload));
|
|
simulation::State.delete_eventlauncher(simulation::Events.FindEventlauncher(commanddata.payload + "_snd"));
|
|
}
|
|
|
|
if (commanddata.command == user_command::globalradiostop) {
|
|
simulation::Region->RadioStop( commanddata.location );
|
|
}
|
|
|
|
if (commanddata.command == user_command::resetconsist) {
|
|
TDynamicObject *found_vehicle = simulation::Vehicles.find(commanddata.payload);
|
|
TDynamicObject *vehicle = found_vehicle;
|
|
|
|
while (vehicle) {
|
|
if (vehicle->Next())
|
|
vehicle = vehicle->Next();
|
|
else
|
|
break;
|
|
}
|
|
|
|
while (vehicle) {
|
|
vehicle->MoverParameters->DamageFlag = 0;
|
|
vehicle->MoverParameters->EngDmgFlag = 0;
|
|
vehicle->MoverParameters->V = 0.000001; // HACK: force vehicle position re-calculation
|
|
vehicle->MoverParameters->DistCounter = 0.0;
|
|
vehicle->MoverParameters->WheelFlat = 0.0;
|
|
vehicle->MoverParameters->AlarmChainFlag = false;
|
|
vehicle->MoverParameters->OffsetTrackH = 0.0;
|
|
vehicle->MoverParameters->OffsetTrackV = 0.0;
|
|
|
|
// pantographs
|
|
for( auto idx = 0; idx < vehicle->iAnimType[ ANIM_PANTS ]; ++idx ) {
|
|
auto &pantograph { *vehicle->pants[idx].fParamPants };
|
|
if( pantograph.PantWys >= 0.0 ) // negative value means pantograph is broken
|
|
continue;
|
|
pantograph.fAngleL = pantograph.fAngleL0;
|
|
pantograph.fAngleU = pantograph.fAngleU0;
|
|
pantograph.PantWys =
|
|
pantograph.fLenL1 * std::sin( pantograph.fAngleL )
|
|
+ pantograph.fLenU1 * std::sin( pantograph.fAngleU )
|
|
+ pantograph.fHeight;
|
|
}
|
|
|
|
vehicle = vehicle->Prev();
|
|
}
|
|
}
|
|
|
|
if (commanddata.command == user_command::fillcompressor) {
|
|
TDynamicObject *vehicle = simulation::Vehicles.find(commanddata.payload);
|
|
vehicle->MoverParameters->CompressedVolume = 8.0f * vehicle->MoverParameters->VeselVolume;
|
|
}
|
|
|
|
if (commanddata.command == user_command::dynamicmove) {
|
|
TDynamicObject *vehicle = simulation::Vehicles.find(commanddata.payload);
|
|
if (vehicle)
|
|
vehicle->move_set(commanddata.param1);
|
|
}
|
|
|
|
if (commanddata.command == user_command::consistteleport) {
|
|
std::istringstream ss(commanddata.payload);
|
|
|
|
std::string track_name;
|
|
std::string vehicle_name;
|
|
std::getline(ss, vehicle_name, '%');
|
|
std::getline(ss, track_name, '%');
|
|
|
|
TTrack *track = simulation::Paths.find(track_name);
|
|
TDynamicObject *vehicle = simulation::Vehicles.find(vehicle_name);
|
|
|
|
while (vehicle) {
|
|
if (vehicle->Next())
|
|
vehicle = vehicle->Next();
|
|
else
|
|
break;
|
|
}
|
|
|
|
double offset = 0.0;
|
|
|
|
while (vehicle) {
|
|
offset += vehicle->MoverParameters->Dim.L;
|
|
vehicle->place_on_track(track, offset, false);
|
|
vehicle = vehicle->Prev();
|
|
}
|
|
}
|
|
|
|
if (commanddata.command == user_command::spawntrainset) {
|
|
|
|
}
|
|
|
|
if (commanddata.command == user_command::pullalarmchain) {
|
|
TDynamicObject *vehicle = simulation::Vehicles.find(commanddata.payload);
|
|
if (vehicle)
|
|
vehicle->MoverParameters->AlarmChainSwitch(true);
|
|
}
|
|
|
|
if (commanddata.command == user_command::sendaicommand) {
|
|
std::istringstream ss(commanddata.payload);
|
|
|
|
std::string vehicle_name;
|
|
std::string command;
|
|
std::getline(ss, vehicle_name, '%');
|
|
std::getline(ss, command, '%');
|
|
|
|
TDynamicObject *vehicle = simulation::Vehicles.find(vehicle_name);
|
|
glm::dvec3 location = commanddata.location;
|
|
if (vehicle && vehicle->Mechanik)
|
|
vehicle->Mechanik->PutCommand(command, commanddata.param1, commanddata.param2, &location);
|
|
}
|
|
|
|
if (commanddata.command == user_command::quitsimulation) {
|
|
// TBD: allow clients to go into offline mode?
|
|
Application.queue_quit(true);
|
|
}
|
|
|
|
if (DebugModeFlag) {
|
|
if (commanddata.command == user_command::timejump) {
|
|
Time.update(commanddata.param1);
|
|
}
|
|
else if (commanddata.command == user_command::timejumplarge) {
|
|
Time.update(20.0 * 60.0);
|
|
}
|
|
else if (commanddata.command == user_command::timejumpsmall) {
|
|
Time.update(5.0 * 60.0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TAnimModel * state_manager::create_model(const std::string &src, const std::string &name, const glm::dvec3 &position) {
|
|
return m_serializer.create_model(src, name, position);
|
|
}
|
|
|
|
std::vector<TDynamicObject *> state_manager::insert_trainset(std::string const &Name, TTrack *Path, double const Offset, std::string const &Vehicles, bool const Reversed) {
|
|
return m_serializer.insert_trainset(Name, Path, Offset, Vehicles, Reversed);
|
|
}
|
|
|
|
TEventLauncher * state_manager::create_eventlauncher(const std::string &src, const std::string &name, const glm::dvec3 &position) {
|
|
return m_serializer.create_eventlauncher(src, name, position);
|
|
}
|
|
|
|
std::pair<int, int> state_manager::preview_include(std::string const &Directive, scene::layer_context const &Context, scene::layer_handle Layer, scene::instance_handle Instance) {
|
|
return m_serializer.preview_include(Directive, Context, Layer, Instance);
|
|
}
|
|
|
|
std::pair<int, int> state_manager::rebuild_include(scene::instance_handle const Instance, std::vector<TAnimModel *> &Retired) {
|
|
if (false == scene::Layers.tracked(Instance)) {
|
|
return { 0, 0 };
|
|
}
|
|
auto const &included { scene::Layers.instance(Instance) };
|
|
// the models the include was loaded with don't match it anymore.
|
|
// NOTE: done ahead of retiring the models made by the editor, which are told from the loaded ones by their preview flag
|
|
scene::Layers.rebuilt(Instance);
|
|
for (auto *model : Instances.sequence()) {
|
|
if (model == nullptr || model->m_instance != Instance || false == model->m_preview) {
|
|
continue;
|
|
}
|
|
if (false == included.removed) {
|
|
scene::Layers.count(model->layer(), scene::layer_item::model, -1);
|
|
}
|
|
Region->erase(model);
|
|
scene::Hierarchy.erase(model->uuid.to_string());
|
|
model->visible(false);
|
|
model->m_preview = false;
|
|
Retired.emplace_back(model);
|
|
}
|
|
if (included.dead) {
|
|
return { 0, 0 };
|
|
}
|
|
auto const layer { scene::Layers.resolve(included.layer) };
|
|
auto const result { m_serializer.preview_include(scene::Layers.directive(Instance), included.context, layer, Instance) };
|
|
// what's shown for an include which is out of sight at the moment goes out of sight as well
|
|
auto const layerhidden { scene::Layers.valid(layer) && false == scene::Layers.layer(layer).visible };
|
|
if (included.removed || layerhidden) {
|
|
for (auto *model : Instances.sequence()) {
|
|
if (model == nullptr || model->m_instance != Instance || false == model->m_preview) {
|
|
continue;
|
|
}
|
|
if (included.removed) {
|
|
scene::Layers.count(model->layer(), scene::layer_item::model, -1);
|
|
}
|
|
if (model->visible()) {
|
|
model->visible(false);
|
|
model->m_layerhidden = true;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void state_manager::delete_model(TAnimModel *model) {
|
|
if (GfxRenderer) {
|
|
GfxRenderer->Forget_Node(model);
|
|
}
|
|
scene::Layers.count(model->layer(), scene::layer_item::model, -1);
|
|
scene::Layers.forget(model);
|
|
Region->erase(model);
|
|
Instances.purge(model);
|
|
}
|
|
|
|
bool state_manager::rename_model(TAnimModel *model, std::string const &name) {
|
|
if (model == nullptr || false == Instances.rename(model, name)) {
|
|
return false;
|
|
}
|
|
model->m_name = (name == "none" ? std::string{} : name);
|
|
scene::Layers.renamed(model);
|
|
return true;
|
|
}
|
|
|
|
void state_manager::delete_eventlauncher(TEventLauncher *launcher) {
|
|
launcher->dRadius = 0.0f; // disable it
|
|
}
|
|
|
|
// passes specified sound to all vehicles within range as a radio message broadcasted on specified channel
|
|
void
|
|
radio_message( sound_source *Message, int const Channel ) {
|
|
|
|
if( Train != nullptr ) {
|
|
Train->radio_message( Message, Channel );
|
|
}
|
|
}
|
|
|
|
} // simulation
|
|
|
|
//---------------------------------------------------------------------------
|