Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
Savegame.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5 Copyright 2013-2020 Petr Ohlidal
6
7 For more information, see http://www.rigsofrods.org/
8
9 Rigs of Rods is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 3, as
11 published by the Free Software Foundation.
12
13 Rigs of Rods is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "Application.h"
23
24#include "AeroEngine.h"
25#include "Application.h"
26#include "Actor.h"
27#include "Buoyance.h"
28#include "CacheSystem.h"
29#include "ContentManager.h"
30#include "Console.h"
31#include "Engine.h"
32#include "GameContext.h"
33#include "GUIManager.h"
34#include "GUI_MessageBox.h"
35#include "InputEngine.h"
36#include "Language.h"
37#include "PlatformUtils.h"
38#include "ScrewProp.h"
39#include "Skidmark.h"
40#include "SkyManager.h"
41#include "Terrain.h"
42#include "TuneupFileFormat.h"
43#include "Utils.h"
44
45#include <rapidjson/rapidjson.h>
46#include <fstream>
47
48#define SAVEGAME_FILE_FORMAT 3
49
50using namespace Ogre;
51using namespace RoR;
52
53// --------------------------------
54// GameContext functions
55
57{
58 std::string terrain_name = App::sim_terrain_name->getStr();
59 std::string mp = (App::mp_state->getEnum<MpState>() == RoR::MpState::CONNECTED) ? "_mp" : "";
60
61 return "quicksave_" + StringUtil::replaceAll(terrain_name, ".terrn2", "") + mp + ".sav";
62}
63
64void GameContext::LoadScene(std::string const& filename)
65{
67 m_actor_manager.LoadScene(filename);
68}
69
70void GameContext::SaveScene(std::string const& filename)
71{
72 m_actor_manager.SaveScene(filename);
73}
74
75std::string GameContext::ExtractSceneName(std::string const& filename)
76{
77 // Read from disk
78 rapidjson::Document j_doc;
79 if (!App::GetContentManager()->LoadAndParseJson(filename, RGN_SAVEGAMES, j_doc) ||
80 !j_doc.IsObject() || !j_doc.HasMember("format_version") || !j_doc["format_version"].IsNumber() ||
81 !j_doc.HasMember("scene_name") || !j_doc["scene_name"].IsString())
82 return "";
83
84 return j_doc["scene_name"].GetString();
85}
86
87std::string GameContext::ExtractSceneTerrain(std::string const& filename)
88{
89 // Read from disk
90 rapidjson::Document j_doc;
91 if (!App::GetContentManager()->LoadAndParseJson(filename, RGN_SAVEGAMES, j_doc) ||
92 !j_doc.IsObject() || !j_doc.HasMember("format_version") || !j_doc["format_version"].IsNumber() ||
93 !j_doc.HasMember("terrain_name") || !j_doc["terrain_name"].IsString())
94 return "";
95
96 return j_doc["terrain_name"].GetString();
97}
98
100{
101 // Global savegames
102 int slot = -1;
103 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_01, 1.0f))
104 {
105 slot = 1;
106 }
107 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_02, 1.0f))
108 {
109 slot = 2;
110 }
111 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_03, 1.0f))
112 {
113 slot = 3;
114 }
115 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_04, 1.0f))
116 {
117 slot = 4;
118 }
119 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_05, 1.0f))
120 {
121 slot = 5;
122 }
123 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_06, 1.0f))
124 {
125 slot = 6;
126 }
127 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_07, 1.0f))
128 {
129 slot = 7;
130 }
131 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_08, 1.0f))
132 {
133 slot = 8;
134 }
135 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_09, 1.0f))
136 {
137 slot = 9;
138 }
139 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD_10, 1.0f))
140 {
141 slot = 0;
142 }
143 if (slot != -1)
144 {
145 Ogre::String filename = Ogre::StringUtil::format("quicksave-%d.sav", slot);
147 }
148
149 if (App::sim_terrain_name->getStr() == "" || App::sim_state->getEnum<SimState>() != SimState::RUNNING)
150 return;
151
152 slot = -1;
153 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_01, 1.0f))
154 {
155 slot = 1;
156 }
157 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_02, 1.0f))
158 {
159 slot = 2;
160 }
161 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_03, 1.0f))
162 {
163 slot = 3;
164 }
165 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_04, 1.0f))
166 {
167 slot = 4;
168 }
169 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_05, 1.0f))
170 {
171 slot = 5;
172 }
173 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_06, 1.0f))
174 {
175 slot = 6;
176 }
177 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_07, 1.0f))
178 {
179 slot = 7;
180 }
181 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_08, 1.0f))
182 {
183 slot = 8;
184 }
185 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_09, 1.0f))
186 {
187 slot = 9;
188 }
189 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE_10, 1.0f))
190 {
191 slot = 0;
192 }
193 if (slot != -1)
194 {
195 Ogre::String filename = Ogre::StringUtil::format("quicksave-%d.sav", slot);
196 App::GetGameContext()->SaveScene(filename);
197 }
198
199 // Terrain local savegames
200
201 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKLOAD, 1.0f))
202 {
203 if (App::sim_quickload_dialog->getBool())
204 {
206 dialog->mbc_title = _LC("QuickloadDialog", "Load game?");
207 dialog->mbc_text = _LC("QuickloadDialog", "You will lose all unsaved progress!");
209
211 ok_btn.mbb_caption = _LC("QuickloadDialog", "Load");
214 dialog->mbc_buttons.push_back(ok_btn);
215
216 GUI::MessageBoxButton cancel_btn;
217 cancel_btn.mbb_caption = _LC("QuickloadDialog", "Cancel");
218 dialog->mbc_buttons.push_back(cancel_btn); // No action - just close the dialog.
219
222 }
223 else
224 {
228 }
229 }
230
231 if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_QUICKSAVE))
232 {
234 }
235}
236
237// --------------------------------
238// ActorManager functions
239
240bool ActorManager::LoadScene(Ogre::String save_filename)
241{
242 // Read from disk
243 rapidjson::Document j_doc;
244 if (!App::GetContentManager()->LoadAndParseJson(save_filename, RGN_SAVEGAMES, j_doc) ||
245 !j_doc.IsObject() || !j_doc.HasMember("format_version") || !j_doc["format_version"].IsNumber())
246 {
248 Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR, _L("Error while loading scene: File invalid or missing"));
249 return false;
250 }
251 if (j_doc["format_version"].GetInt() != SAVEGAME_FILE_FORMAT)
252 {
254 Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR, _L("Error while loading scene: File format mismatch"));
255 return false;
256 }
257
258 // Terrain
259 String terrain_name = j_doc["terrain_name"].GetString();
260
261 if (App::mp_state->getEnum<MpState>() == RoR::MpState::CONNECTED)
262 {
263 if (save_filename == "autosave.sav")
264 return false;
265 if (terrain_name != App::sim_terrain_name->getStr())
266 {
268 Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR, _L("Error while loading scene: Terrain mismatch"));
269 return false;
270 }
271 if (j_doc["actors"].GetArray().Size() > 3)
272 {
274 Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR, _L("Error while loading scene: Too many vehicles"));
275 return false;
276 }
277 }
278
279 m_forced_awake = j_doc["forced_awake"].GetBool();
280
281 App::GetGameContext()->GetActorManager()->SetSimulationPaused(j_doc["physics_paused"].GetBool());
282
283#ifdef USE_CAELUM
284 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::CAELUM)
285 {
286 if (j_doc.HasMember("daytime"))
287 {
288 App::GetGameContext()->GetTerrain()->getSkyManager()->SetTime(j_doc["daytime"].GetDouble());
289 }
290 }
291#endif // USE_CAELUM
292
293 // Character
294 auto data = j_doc["player_position"].GetArray();
295 Vector3 position = Vector3(data[0].GetFloat(), data[1].GetFloat(), data[2].GetFloat());
297 App::GetGameContext()->GetPlayerCharacter()->setRotation(Radian(j_doc["player_rotation"].GetFloat()));
298
299 // Actors
300 auto actors_changed = false;
301 auto player_actor = App::GetGameContext()->GetPlayerActor();
302 auto prev_player_actor = App::GetGameContext()->GetPrevPlayerActor();
303 std::vector<ActorPtr> actors;
304 std::vector<ActorPtr> x_actors = GetLocalActors();
305 for (rapidjson::Value& j_entry: j_doc["actors"].GetArray())
306 {
307 // NOTE: The filename is by default in "Bundle-qualified" format, i.e. "mybundle.zip:myactor.truck"
308 String rigdef_filename_maybe_bundle_qualified = j_entry["filename"].GetString();
309 std::string filename;
310 std::string bundlename;
311 SplitBundleQualifiedFilename(rigdef_filename_maybe_bundle_qualified, bundlename, filename);
312 CacheEntryPtr actor_entry = App::GetCacheSystem()->FindEntryByFilename(LT_AllBeam, /*partial:*/false, rigdef_filename_maybe_bundle_qualified);
313
314 CacheEntryPtr skin = nullptr;
315 if (j_entry.HasMember("skin"))
316 {
317 skin = App::GetCacheSystem()->FetchSkinByName(j_entry["skin"].GetString());
318 }
319
320 TuneupDefPtr working_tuneup = nullptr;
321 if (j_entry.HasMember("tuneup_document"))
322 {
323 const char* tuneup_str = j_entry["tuneup_document"].GetString();
324 size_t tuneup_len = j_entry["tuneup_document"].GetStringLength();
325 Ogre::DataStreamPtr datastream(new Ogre::MemoryDataStream((void*)tuneup_str, tuneup_len));
326 std::vector<TuneupDefPtr> tuneups = TuneupUtil::ParseTuneups(datastream);
327 ROR_ASSERT(tuneups.size() > 0);
328 working_tuneup = tuneups[0];
329 }
330
331 String section_config = j_entry["section_config"].GetString();
332
333 ActorPtr actor = nullptr;
334 int index = static_cast<int>(actors.size());
335 if (index < x_actors.size())
336 {
337 if (filename != x_actors[index]->ar_filename ||
338 (skin != nullptr && skin->dname != x_actors[index]->m_used_skin_entry->dname) ||
339 section_config != x_actors[index]->getSectionConfig())
340 {
341 if (x_actors[index] == player_actor)
342 {
344 }
345 else if (x_actors[index] == prev_player_actor)
346 {
348 }
349 App::GetGameContext()->PushMessage(Message(MSG_SIM_DELETE_ACTOR_REQUESTED, static_cast<void*>(new ActorPtr(x_actors[index]))));
350 actors_changed = true;
351 }
352 else
353 {
354 actor = x_actors[index];
355 actor->SyncReset(false);
356 }
357 }
358
359 // If a 'preloaded' actor isn't loaded at this point, it means it's not installed.
360 if (actor == nullptr && !j_entry["preloaded_with_terrain"].GetBool())
361 {
363 rq->asr_filename = rigdef_filename_maybe_bundle_qualified;
364 rq->asr_position.x = j_entry["position"][0].GetFloat();
365 rq->asr_position.y = j_entry["min_height"].GetFloat();
366 rq->asr_position.z = j_entry["position"][2].GetFloat();
367 rq->asr_rotation = Quaternion(Degree(270) - Radian(j_entry["rotation"].GetFloat()), Vector3::UNIT_Y);
368 rq->asr_skin_entry = skin;
369 rq->asr_working_tuneup = working_tuneup;
370 rq->asr_config = section_config;
372 // Copy saved state
373 rq->asr_saved_state = std::shared_ptr<rapidjson::Document>(new rapidjson::Document());
374 rq->asr_saved_state->CopyFrom(j_entry, rq->asr_saved_state->GetAllocator());
375
377 actors_changed = true;
378 // CAUTION: `actor` remains nullptr!
379 }
380
381 actors.push_back(actor);
382 }
383 for (size_t index = actors.size(); index < x_actors.size(); index++)
384 {
385 if (x_actors[index] == player_actor)
386 {
388 }
389 else if (x_actors[index] == prev_player_actor)
390 {
392 }
393 App::GetGameContext()->PushMessage(Message(MSG_SIM_DELETE_ACTOR_REQUESTED, static_cast<void*>(new ActorPtr(x_actors[index]))));
394 actors_changed = true;
395 }
396
397 const int num_actors = static_cast<int>(j_doc["actors"].Size());
398 for (int index = 0; index < num_actors; index++)
399 {
400 if (actors[index] == nullptr)
401 continue;
402
403 ActorPtr actor = actors[index];
404 rapidjson::Value& j_entry = j_doc["actors"][index];
405
406 this->RestoreSavedState(actor, j_entry);
407 }
408
409 if (save_filename != "autosave.sav")
410 {
413 }
414
415 return true;
416}
417
418bool ActorManager::SaveScene(Ogre::String filename)
419{
420 std::vector<ActorPtr> x_actors = GetLocalActors();
421
422 if (App::mp_state->getEnum<MpState>() == RoR::MpState::CONNECTED)
423 {
424 if (filename == "autosave.sav")
425 return false;
426 if (x_actors.size() > 3)
427 {
429 Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR, _L("Error while saving scene: Too many vehicles"));
430 return false;
431 }
432 }
433
434 rapidjson::Document j_doc;
435 j_doc.SetObject();
436 j_doc.AddMember("format_version", SAVEGAME_FILE_FORMAT, j_doc.GetAllocator());
437
438 // Pretty name
439 String pretty_name = App::GetCacheSystem()->GetPrettyName(App::sim_terrain_name->getStr());
440 String scene_name = StringUtil::format("%s [%d]", pretty_name.c_str(), x_actors.size());
441 j_doc.AddMember("scene_name", rapidjson::StringRef(scene_name.c_str()), j_doc.GetAllocator());
442
443 // Terrain
444 j_doc.AddMember("terrain_name", rapidjson::StringRef(App::sim_terrain_name->getStr().c_str()), j_doc.GetAllocator());
445
446#ifdef USE_CAELUM
447 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::CAELUM)
448 {
449 j_doc.AddMember("daytime", App::GetGameContext()->GetTerrain()->getSkyManager()->GetTime(), j_doc.GetAllocator());
450 }
451#endif // USE_CAELUM
452
453 j_doc.AddMember("forced_awake", m_forced_awake, j_doc.GetAllocator());
454
455 j_doc.AddMember("physics_paused", App::GetGameContext()->GetActorManager()->IsSimulationPaused(), j_doc.GetAllocator());
456
457 // Character
458 rapidjson::Value j_player_position(rapidjson::kArrayType);
459 j_player_position.PushBack(App::GetGameContext()->GetPlayerCharacter()->getPosition().x, j_doc.GetAllocator());
460 j_player_position.PushBack(App::GetGameContext()->GetPlayerCharacter()->getPosition().y, j_doc.GetAllocator());
461 j_player_position.PushBack(App::GetGameContext()->GetPlayerCharacter()->getPosition().z, j_doc.GetAllocator());
462 j_doc.AddMember("player_position", j_player_position, j_doc.GetAllocator());
463 j_doc.AddMember("player_rotation", App::GetGameContext()->GetPlayerCharacter()->getRotation().valueRadians(), j_doc.GetAllocator());
464
465 std::map<int, int> vector_index_lookup;
466 for (ActorPtr& actor : m_actors)
467 {
468 vector_index_lookup[actor->ar_vector_index] = -1;
469 auto search = std::find_if(x_actors.begin(), x_actors.end(), [actor](ActorPtr b)
470 { return actor->ar_instance_id == b->ar_instance_id; });
471 if (search != x_actors.end())
472 {
473 vector_index_lookup[actor->ar_vector_index] = std::distance(x_actors.begin(), search);
474 }
475 }
476
477 // Actors
478 rapidjson::Value j_actors(rapidjson::kArrayType);
479 for (ActorPtr actor : x_actors)
480 {
481 rapidjson::Value j_entry(rapidjson::kObjectType);
482
483 // Save the filename in "Bundle-qualified" format, i.e. "mybundle.zip:myactor.truck"
484 std::string bname;
485 std::string bpath;
486 Ogre::StringUtil::splitFilename(actor->getUsedActorEntry()->resource_bundle_path, bname, bpath);
487 std::string bq_filename = fmt::format("{}:{}", bname, actor->ar_filename);
488 rapidjson::Value j_bq_filename(bq_filename.c_str(), j_doc.GetAllocator());
489 j_entry.AddMember("filename", j_bq_filename, j_doc.GetAllocator());
490
491 rapidjson::Value j_actor_position(rapidjson::kArrayType);
492 j_actor_position.PushBack(actor->ar_nodes[0].AbsPosition.x, j_doc.GetAllocator());
493 j_actor_position.PushBack(actor->ar_nodes[0].AbsPosition.y, j_doc.GetAllocator());
494 j_actor_position.PushBack(actor->ar_nodes[0].AbsPosition.z, j_doc.GetAllocator());
495 j_entry.AddMember("position", j_actor_position, j_doc.GetAllocator());
496 j_entry.AddMember("rotation", actor->getRotation(), j_doc.GetAllocator());
497 j_entry.AddMember("min_height", actor->getMinHeight(), j_doc.GetAllocator());
498 j_entry.AddMember("spawn_rotation", actor->m_spawn_rotation, j_doc.GetAllocator());
499 j_entry.AddMember("preloaded_with_terrain", actor->isPreloadedWithTerrain(), j_doc.GetAllocator());
500 j_entry.AddMember("sim_state", static_cast<int>(actor->ar_state), j_doc.GetAllocator());
501 j_entry.AddMember("physics_paused", actor->ar_physics_paused, j_doc.GetAllocator());
502 j_entry.AddMember("player_actor", actor==App::GetGameContext()->GetPlayerActor(), j_doc.GetAllocator());
503 j_entry.AddMember("prev_player_actor", actor==App::GetGameContext()->GetPrevPlayerActor(), j_doc.GetAllocator());
504
505 if (actor->m_used_skin_entry)
506 {
507 j_entry.AddMember("skin", rapidjson::StringRef(actor->m_used_skin_entry->dname.c_str()), j_doc.GetAllocator());
508 }
509
510 if (actor->getWorkingTuneupDef())
511 {
512 // Include the entire .tuneup file in the savegame.
513 rapidjson::Value j_tuneup_document(rapidjson::kStringType);
514 Str<TUNEUP_BUF_SIZE> tuneup_buf; // `Ogre::MemoryDataStream` doesn't zero-out the buffer it creates; we must supply our own zeroed memory.
515 Ogre::DataStreamPtr datastream(new Ogre::MemoryDataStream(tuneup_buf.GetBuffer(), tuneup_buf.GetCapacity()));
516 TuneupUtil::ExportTuneup(datastream, actor->getWorkingTuneupDef());
517 j_tuneup_document.SetString(datastream->getAsString().c_str(), j_doc.GetAllocator());
518 j_entry.AddMember("tuneup_document", j_tuneup_document, j_doc.GetAllocator());
519 }
520
521 j_entry.AddMember("section_config", rapidjson::StringRef(actor->m_section_config.c_str()), j_doc.GetAllocator());
522
523 // Engine, anti-lock brake, traction control
524 if (actor->ar_engine)
525 {
526 j_entry.AddMember("engine_gear", actor->ar_engine->getGear(), j_doc.GetAllocator());
527 j_entry.AddMember("engine_rpm", actor->ar_engine->getRPM(), j_doc.GetAllocator());
528 j_entry.AddMember("engine_auto_mode", static_cast<int>(actor->ar_engine->getAutoMode()), j_doc.GetAllocator());
529 j_entry.AddMember("engine_auto_select", actor->ar_engine->getAutoShift(), j_doc.GetAllocator());
530 j_entry.AddMember("engine_is_running", actor->ar_engine->isRunning(), j_doc.GetAllocator());
531 j_entry.AddMember("engine_has_contact", actor->ar_engine->hasContact(), j_doc.GetAllocator());
532 j_entry.AddMember("engine_wheel_spin", actor->ar_wheel_spin, j_doc.GetAllocator());
533 j_entry.AddMember("alb_mode", actor->alb_mode, j_doc.GetAllocator());
534 j_entry.AddMember("tc_mode", actor->tc_mode, j_doc.GetAllocator());
535 j_entry.AddMember("cc_mode", actor->cc_mode, j_doc.GetAllocator());
536 j_entry.AddMember("cc_target_rpm", actor->cc_target_rpm, j_doc.GetAllocator());
537 j_entry.AddMember("cc_target_speed", actor->cc_target_speed, j_doc.GetAllocator());
538 }
539
540 j_entry.AddMember("hydro_dir_state", actor->ar_hydro_dir_state, j_doc.GetAllocator());
541 j_entry.AddMember("hydro_aileron_state", actor->ar_hydro_aileron_state, j_doc.GetAllocator());
542 j_entry.AddMember("hydro_rudder_state", actor->ar_hydro_rudder_state, j_doc.GetAllocator());
543 j_entry.AddMember("hydro_elevator_state", actor->ar_hydro_elevator_state, j_doc.GetAllocator());
544 j_entry.AddMember("parking_brake", actor->ar_parking_brake, j_doc.GetAllocator());
545 j_entry.AddMember("trailer_parking_brake", actor->ar_trailer_parking_brake, j_doc.GetAllocator());
546 j_entry.AddMember("avg_wheel_speed", actor->ar_avg_wheel_speed, j_doc.GetAllocator());
547 j_entry.AddMember("wheel_speed", actor->ar_wheel_speed, j_doc.GetAllocator());
548 j_entry.AddMember("wheel_spin", actor->ar_wheel_spin, j_doc.GetAllocator());
549
550 j_entry.AddMember("custom_particles", actor->ar_cparticles_active, j_doc.GetAllocator());
551
552 // Flares
553 j_entry.AddMember("lights", (int)actor->getHeadlightsVisible(), j_doc.GetAllocator());
554 j_entry.AddMember("blink_type", (int)actor->getBlinkType(), j_doc.GetAllocator());
555 // "beacon_light" was "pp_beacon_light" since release 2021.02 (savegame file format 2).
556 // It was caused by find-&-replace derp in commit 5a159ad9c0d0ffb1fa3e6f4f9c4577fab3910e3e.
557 j_entry.AddMember("beacon_light", actor->getBeaconMode(), j_doc.GetAllocator());
558 j_entry.AddMember("high_beams_on", actor->getHighBeamsVisible(), j_doc.GetAllocator());
559 j_entry.AddMember("fog_lights_on", actor->getFogLightsVisible(), j_doc.GetAllocator());
560
561 // User-defined flares
562 rapidjson::Value j_custom_lights(rapidjson::kArrayType);
563 for (int i = 0; i < MAX_CLIGHTS; i++)
564 {
565 j_custom_lights.PushBack(actor->getCustomLightVisible(i), j_doc.GetAllocator());
566 }
567 j_entry.AddMember("custom_lights", j_custom_lights, j_doc.GetAllocator());
568
569 // Buoyance
570 if (actor->m_buoyance)
571 {
572 j_entry.AddMember("buoyance_sink", actor->m_buoyance->sink, j_doc.GetAllocator());
573 }
574
575 // Turboprops / Turbojets
576 rapidjson::Value j_aeroengines(rapidjson::kArrayType);
577 for (int i = 0; i < actor->ar_num_aeroengines; i++)
578 {
579 rapidjson::Value j_aeroengine(rapidjson::kObjectType);
580 j_aeroengine.AddMember("rpm", actor->ar_aeroengines[i]->getRPM(), j_doc.GetAllocator());
581 j_aeroengine.AddMember("reverse", actor->ar_aeroengines[i]->getReverse(), j_doc.GetAllocator());
582 j_aeroengine.AddMember("ignition", actor->ar_aeroengines[i]->getIgnition(), j_doc.GetAllocator());
583 j_aeroengine.AddMember("throttle", actor->ar_aeroengines[i]->getThrottle(), j_doc.GetAllocator());
584 j_aeroengines.PushBack(j_aeroengine, j_doc.GetAllocator());
585 }
586 j_entry.AddMember("aeroengines", j_aeroengines, j_doc.GetAllocator());
587
588 // Screwprops
589 rapidjson::Value j_screwprops(rapidjson::kArrayType);
590 for (int i = 0; i < actor->ar_num_screwprops; i++)
591 {
592 rapidjson::Value j_screwprop(rapidjson::kObjectType);
593 j_screwprop.AddMember("rudder", actor->ar_screwprops[i]->getRudder(), j_doc.GetAllocator());
594 j_screwprop.AddMember("throttle", actor->ar_screwprops[i]->getThrottle(), j_doc.GetAllocator());
595 j_screwprops.PushBack(j_screwprop, j_doc.GetAllocator());
596 }
597 j_entry.AddMember("screwprops", j_screwprops, j_doc.GetAllocator());
598
599 // Rotators
600 rapidjson::Value j_rotators(rapidjson::kArrayType);
601 for (int i = 0; i < actor->ar_num_rotators; i++)
602 {
603 j_rotators.PushBack(actor->ar_rotators[i].angle, j_doc.GetAllocator());
604 }
605 j_entry.AddMember("rotators", j_rotators, j_doc.GetAllocator());
606
607 // Wheels
608 rapidjson::Value j_wheels(rapidjson::kArrayType);
609 for (int i = 0; i < actor->ar_num_wheels; i++)
610 {
611 j_wheels.PushBack(actor->ar_wheels[i].wh_is_detached, j_doc.GetAllocator());
612 }
613 j_entry.AddMember("wheels", j_wheels, j_doc.GetAllocator());
614
615 // Wheel differentials
616 rapidjson::Value j_wheel_diffs(rapidjson::kArrayType);
617 for (int i = 0; i < actor->m_num_wheel_diffs; i++)
618 {
619 j_wheel_diffs.PushBack(actor->m_wheel_diffs[i]->GetActiveDiffType(), j_doc.GetAllocator());
620 }
621 j_entry.AddMember("wheel_diffs", j_wheel_diffs, j_doc.GetAllocator());
622
623 // Axle differentials
624 rapidjson::Value j_axle_diffs(rapidjson::kArrayType);
625 for (int i = 0; i < actor->m_num_axle_diffs; i++)
626 {
627 j_axle_diffs.PushBack(actor->m_axle_diffs[i]->GetActiveDiffType(), j_doc.GetAllocator());
628 }
629 j_entry.AddMember("axle_diffs", j_axle_diffs, j_doc.GetAllocator());
630
631 // Transfercase
632 if (actor->m_transfer_case)
633 {
634 rapidjson::Value j_transfer_case(rapidjson::kObjectType);
635 j_transfer_case.AddMember("4WD", actor->m_transfer_case->tr_4wd_mode, j_doc.GetAllocator());
636 j_transfer_case.AddMember("GearRatio", actor->m_transfer_case->tr_gear_ratios[0], j_doc.GetAllocator());
637 j_entry.AddMember("transfercase", j_transfer_case, j_doc.GetAllocator());
638 }
639
640 // Commands
641 rapidjson::Value j_commands(rapidjson::kArrayType);
642 for (int i = 0; i < MAX_COMMANDS; i++) // BEWARE: commandkeys are indexed 1-MAX_COMMANDS! - but to preserve compatibility we omit the last commandkey in savegames.
643 {
644 rapidjson::Value j_command(rapidjson::kArrayType);
645 j_command.PushBack(actor->ar_command_key[i].commandValue, j_doc.GetAllocator());
646 j_command.PushBack(actor->ar_command_key[i].triggerInputValue, j_doc.GetAllocator());
647
648 rapidjson::Value j_command_beams(rapidjson::kArrayType);
649 for (int j = 0; j < (int)actor->ar_command_key[i].beams.size(); j++)
650 {
651 rapidjson::Value j_cmb(rapidjson::kArrayType);
652 auto& beam = actor->ar_command_key[i].beams[j];
653 j_cmb.PushBack(beam.cmb_state->auto_moving_mode, j_doc.GetAllocator());
654 j_cmb.PushBack(beam.cmb_state->pressed_center_mode, j_doc.GetAllocator());
655 j_command_beams.PushBack(j_cmb, j_doc.GetAllocator());
656 }
657 j_command.PushBack(j_command_beams, j_doc.GetAllocator());
658
659 j_commands.PushBack(j_command, j_doc.GetAllocator());
660 }
661 j_entry.AddMember("commands", j_commands, j_doc.GetAllocator());
662
663 // Hooks
664 rapidjson::Value j_hooks(rapidjson::kArrayType);
665 for (const auto& h : actor->ar_hooks)
666 {
667 rapidjson::Value j_hook(rapidjson::kObjectType);
668 int lock_node = h.hk_lock_node ? h.hk_lock_node->pos : -1;
669 int locked_actor = h.hk_locked_actor ? vector_index_lookup[h.hk_locked_actor->ar_vector_index] : -1;
670 j_hook.AddMember("locked", h.hk_locked, j_doc.GetAllocator());
671 j_hook.AddMember("lock_node", lock_node, j_doc.GetAllocator());
672 j_hook.AddMember("locked_actor", locked_actor, j_doc.GetAllocator());
673 j_hooks.PushBack(j_hook, j_doc.GetAllocator());
674 }
675 j_entry.AddMember("hooks", j_hooks, j_doc.GetAllocator());
676
677 // Ropes
678 rapidjson::Value j_ropes(rapidjson::kArrayType);
679 for (const auto& r : actor->ar_ropes)
680 {
681 rapidjson::Value j_rope(rapidjson::kObjectType);
682 int locked_ropable = r.rp_locked_ropable ? r.rp_locked_ropable->pos : -1;
683 int locked_actor = r.rp_locked_actor ? vector_index_lookup[r.rp_locked_actor->ar_vector_index] : -1;
684 j_rope.AddMember("locked", r.rp_locked, j_doc.GetAllocator());
685 j_rope.AddMember("locked_ropable", locked_ropable, j_doc.GetAllocator());
686 j_rope.AddMember("locked_actor", locked_actor, j_doc.GetAllocator());
687 j_ropes.PushBack(j_rope, j_doc.GetAllocator());
688 }
689 j_entry.AddMember("ropes", j_ropes, j_doc.GetAllocator());
690
691 // Ties
692 rapidjson::Value j_ties(rapidjson::kArrayType);
693 for (const auto& t : actor->ar_ties)
694 {
695 rapidjson::Value j_tie(rapidjson::kObjectType);
696 int locked_ropable = t.ti_locked_ropable ? t.ti_locked_ropable->pos : -1;
697 int locked_actor = t.ti_locked_actor ? vector_index_lookup[t.ti_locked_actor->ar_vector_index] : -1;
698 j_tie.AddMember("tied", t.ti_tied, j_doc.GetAllocator());
699 j_tie.AddMember("tying", t.ti_tying, j_doc.GetAllocator());
700 j_tie.AddMember("locked_ropable", locked_ropable, j_doc.GetAllocator());
701 j_tie.AddMember("locked_actor", locked_actor, j_doc.GetAllocator());
702 j_ties.PushBack(j_tie, j_doc.GetAllocator());
703 }
704 j_entry.AddMember("ties", j_ties, j_doc.GetAllocator());
705
706 // Ropables
707 rapidjson::Value j_ropables(rapidjson::kArrayType);
708 for (const auto& r : actor->ar_ropables)
709 {
710 rapidjson::Value j_ropable(rapidjson::kObjectType);
711 j_ropable.AddMember("attached_ties", r.attached_ties, j_doc.GetAllocator());
712 j_ropable.AddMember("attached_ropes", r.attached_ropes, j_doc.GetAllocator());
713 j_ropables.PushBack(j_ropable, j_doc.GetAllocator());
714 }
715 j_entry.AddMember("ropables", j_ropables, j_doc.GetAllocator());
716
717 j_entry.AddMember("slidenodes_locked", actor->m_slidenodes_locked, j_doc.GetAllocator());
718
719 // Nodes
720 rapidjson::Value j_nodes(rapidjson::kArrayType);
721 for (int i = 0; i < actor->ar_num_nodes; i++)
722 {
723 rapidjson::Value j_node(rapidjson::kArrayType);
724
725 // Position
726 j_node.PushBack(actor->ar_nodes[i].AbsPosition.x, j_doc.GetAllocator());
727 j_node.PushBack(actor->ar_nodes[i].AbsPosition.y, j_doc.GetAllocator());
728 j_node.PushBack(actor->ar_nodes[i].AbsPosition.z, j_doc.GetAllocator());
729
730 // Velocity
731 j_node.PushBack(actor->ar_nodes[i].Velocity.x, j_doc.GetAllocator());
732 j_node.PushBack(actor->ar_nodes[i].Velocity.y, j_doc.GetAllocator());
733 j_node.PushBack(actor->ar_nodes[i].Velocity.z, j_doc.GetAllocator());
734
735 // Initial Position
736 j_node.PushBack(actor->ar_initial_node_positions[i].x, j_doc.GetAllocator());
737 j_node.PushBack(actor->ar_initial_node_positions[i].y, j_doc.GetAllocator());
738 j_node.PushBack(actor->ar_initial_node_positions[i].z, j_doc.GetAllocator());
739
740 j_nodes.PushBack(j_node, j_doc.GetAllocator());
741 }
742 j_entry.AddMember("nodes", j_nodes, j_doc.GetAllocator());
743
744 // Beams
745 rapidjson::Value j_beams(rapidjson::kArrayType);
746 for (int i = 0; i < actor->ar_num_beams; i++)
747 {
748 rapidjson::Value j_beam(rapidjson::kArrayType);
749
750 j_beam.PushBack(actor->ar_beams[i].maxposstress, j_doc.GetAllocator());
751 j_beam.PushBack(actor->ar_beams[i].maxnegstress, j_doc.GetAllocator());
752 j_beam.PushBack(actor->ar_beams[i].minmaxposnegstress, j_doc.GetAllocator());
753 j_beam.PushBack(actor->ar_beams[i].strength, j_doc.GetAllocator());
754 j_beam.PushBack(actor->ar_beams[i].L, j_doc.GetAllocator());
755 j_beam.PushBack(actor->ar_beams[i].bm_broken, j_doc.GetAllocator());
756 j_beam.PushBack(actor->ar_beams[i].bm_disabled, j_doc.GetAllocator());
757 j_beam.PushBack(actor->ar_beams[i].bm_inter_actor, j_doc.GetAllocator());
758 ActorPtr locked_actor = actor->ar_beams[i].bm_locked_actor;
759 j_beam.PushBack(locked_actor ? vector_index_lookup[locked_actor->ar_vector_index] : -1, j_doc.GetAllocator());
760
761 j_beams.PushBack(j_beam, j_doc.GetAllocator());
762 }
763 j_entry.AddMember("beams", j_beams, j_doc.GetAllocator());
764
765 j_actors.PushBack(j_entry, j_doc.GetAllocator());
766 }
767 j_doc.AddMember("actors", j_actors, j_doc.GetAllocator());
768
769 // Write to disk
771 {
772 // Error already logged
775 return false;
776 }
777
778 if (filename != "autosave.sav")
779 {
782 }
783
784 return true;
785}
786
787void ActorManager::RestoreSavedState(ActorPtr actor, rapidjson::Value const& j_entry)
788{
789 actor->m_spawn_rotation = j_entry["spawn_rotation"].GetFloat();
790 actor->ar_state = static_cast<ActorState>(j_entry["sim_state"].GetInt());
791 actor->ar_physics_paused = j_entry["physics_paused"].GetBool();
792
793 if (j_entry["player_actor"].GetBool())
794 {
796 }
797 else if (j_entry["prev_player_actor"].GetBool())
798 {
800 }
801
802 if (actor->ar_engine)
803 {
804 int gear = j_entry["engine_gear"].GetInt();
805 float rpm = j_entry["engine_rpm"].GetFloat();
806 int automode = j_entry["engine_auto_mode"].GetInt();
807 int autoselect = j_entry["engine_auto_select"].GetInt();
808 bool running = j_entry["engine_is_running"].GetBool();
809 bool contact = j_entry["engine_has_contact"].GetBool();
810 if (running != actor->ar_engine->isRunning())
811 {
812 if (running)
813 actor->ar_engine->startEngine();
814 else
815 actor->ar_engine->stopEngine();
816 }
817 actor->ar_engine->pushNetworkState(rpm, 0.0f, 0.0f, gear, running, contact, automode, autoselect);
818 actor->ar_engine->setWheelSpin(j_entry["wheel_spin"].GetFloat() * RAD_PER_SEC_TO_RPM);
819 actor->alb_mode = j_entry["alb_mode"].GetBool();
820 actor->tc_mode = j_entry["tc_mode"].GetBool();
821 actor->cc_mode = j_entry["cc_mode"].GetBool();
822 actor->cc_target_rpm = j_entry["cc_target_rpm"].GetFloat();
823 actor->cc_target_speed = j_entry["cc_target_speed"].GetFloat();
824 }
825
826 actor->ar_hydro_dir_state = j_entry["hydro_dir_state"].GetFloat();
827 actor->ar_hydro_aileron_state = j_entry["hydro_aileron_state"].GetFloat();
828 actor->ar_hydro_rudder_state = j_entry["hydro_rudder_state"].GetFloat();
829 actor->ar_hydro_elevator_state = j_entry["hydro_elevator_state"].GetFloat();
830 actor->ar_parking_brake = j_entry["parking_brake"].GetBool();
831 actor->ar_trailer_parking_brake = j_entry["trailer_parking_brake"].GetBool();
832 actor->ar_avg_wheel_speed = j_entry["avg_wheel_speed"].GetFloat();
833 actor->ar_wheel_speed = j_entry["wheel_speed"].GetFloat();
834 actor->ar_wheel_spin = j_entry["wheel_spin"].GetFloat();
835
836 if (actor->ar_cparticles_active != j_entry["custom_particles"].GetBool())
837 {
838 actor->toggleCustomParticles();
839 }
840
841 // Flares
842 actor->setHeadlightsVisible((bool)j_entry["lights"].GetInt()); // legacy name
843 actor->setBlinkType(BlinkType(j_entry["blink_type"].GetInt()));
844 // "beacon_light" was "pp_beacon_light" since release 2021.02 (savegame file format 2).
845 // It was caused by find-&-replace derp in commit 5a159ad9c0d0ffb1fa3e6f4f9c4577fab3910e3e.
846 actor->setBeaconMode(j_entry.HasMember("beacon_light") ? j_entry["beacon_light"].GetBool() : j_entry["pp_beacon_light"].GetBool());
847 actor->setHighBeamsVisible(j_entry.HasMember("high_beams_on") ? j_entry["high_beams_on"].GetBool() : false); // (added to savegame file format 3)
848 actor->setFogLightsVisible(j_entry.HasMember("fog_lights_on") ? j_entry["fog_lights_on"].GetBool() : false); // (added to savegame file format 3)
849
850 // User-defined flares
851 if (j_entry.HasMember("custom_lights"))
852 {
853 auto flares = j_entry["custom_lights"].GetArray();
854 for (int i = 0; i < MAX_CLIGHTS; i++)
855 {
856 actor->setCustomLightVisible(i, flares[i].GetBool());
857 }
858 }
859
860 if (actor->m_buoyance)
861 {
862 actor->m_buoyance->sink = j_entry["buoyance_sink"].GetBool();
863 }
864
865 auto aeroengines = j_entry["aeroengines"].GetArray();
866 for (int i = 0; i < actor->ar_num_aeroengines; i++)
867 {
868 actor->ar_aeroengines[i]->setRPM(aeroengines[i]["rpm"].GetFloat());
869 actor->ar_aeroengines[i]->setReverse(aeroengines[i]["reverse"].GetBool());
870 actor->ar_aeroengines[i]->setIgnition(aeroengines[i]["ignition"].GetBool());
871 actor->ar_aeroengines[i]->setThrottle(aeroengines[i]["throttle"].GetFloat());
872 }
873
874 auto screwprops = j_entry["screwprops"].GetArray();
875 for (int i = 0; i < actor->ar_num_screwprops; i++)
876 {
877 actor->ar_screwprops[i]->setRudder(screwprops[i]["rudder"].GetFloat());
878 actor->ar_screwprops[i]->setThrottle(screwprops[i]["throttle"].GetFloat());
879 }
880
881 for (int i = 0; i < actor->ar_num_rotators; i++)
882 {
883 actor->ar_rotators[i].angle = j_entry["rotators"][i].GetFloat();
884 }
885
886 for (int i = 0; i < actor->ar_num_wheels; i++)
887 {
888 if (actor->m_skid_trails[i])
889 {
890 actor->m_skid_trails[i]->reset();
891 }
892 actor->ar_wheels[i].wh_is_detached = j_entry["wheels"][i].GetBool();
893 }
894
895 for (int i = 0; i < actor->m_num_wheel_diffs; i++)
896 {
897 for (int k = 0; k < actor->m_wheel_diffs[i]->GetNumDiffTypes(); k++)
898 {
899 if (actor->m_wheel_diffs[i]->GetActiveDiffType() != j_entry["wheel_diffs"][i].GetInt())
901 }
902 }
903
904 for (int i = 0; i < actor->m_num_axle_diffs; i++)
905 {
906 for (int k = 0; k < actor->m_axle_diffs[i]->GetNumDiffTypes(); k++)
907 {
908 if (actor->m_axle_diffs[i]->GetActiveDiffType() != j_entry["axle_diffs"][i].GetInt())
910 }
911 }
912
913 if (actor->m_transfer_case)
914 {
915 actor->m_transfer_case->tr_4wd_mode = j_entry["transfercase"]["4WD"].GetBool();
916 for (int k = 0; k < actor->m_transfer_case->tr_gear_ratios.size(); k++)
917 {
918 if (actor->m_transfer_case->tr_gear_ratios[0] != j_entry["transfercase"]["GearRatio"].GetFloat())
920 }
921 }
922
923 auto commands = j_entry["commands"].GetArray();
924 for (int i = 0; i < MAX_COMMANDS; i++) // BEWARE: commandkeys are indexed 1-MAX_COMMANDS! - but to preserve compatibility we omit the last commandkey in savegames.
925 {
926 auto& command_key = actor->ar_command_key[i];
927 command_key.commandValue = commands[i][0].GetFloat();
928 command_key.triggerInputValue = commands[i][1].GetFloat();
929 auto command_beams = commands[i][2].GetArray();
930 for (int j = 0; j < (int)command_key.beams.size(); j++)
931 {
932 command_key.beams[j].cmb_state->auto_moving_mode = command_beams[j][0].GetInt();
933 command_key.beams[j].cmb_state->pressed_center_mode = command_beams[j][1].GetBool();
934 }
935 }
936
937 auto nodes = j_entry["nodes"].GetArray();
938 for (rapidjson::SizeType i = 0; i < nodes.Size(); i++)
939 {
940 auto data = nodes[i].GetArray();
941 actor->ar_nodes[i].AbsPosition = Vector3(data[0].GetFloat(), data[1].GetFloat(), data[2].GetFloat());
942 actor->ar_nodes[i].RelPosition = actor->ar_nodes[i].AbsPosition - actor->ar_origin;
943 actor->ar_nodes[i].Velocity = Vector3(data[3].GetFloat(), data[4].GetFloat(), data[5].GetFloat());
944 actor->ar_initial_node_positions[i] = Vector3(data[6].GetFloat(), data[7].GetFloat(), data[8].GetFloat());
945 }
946
947 std::vector<ActorPtr> actors = this->GetLocalActors();
948
949 auto beams = j_entry["beams"].GetArray();
950 for (rapidjson::SizeType i = 0; i < beams.Size(); i++)
951 {
952 auto data = beams[i].GetArray();
953 actor->ar_beams[i].maxposstress = data[0].GetFloat();
954 actor->ar_beams[i].maxnegstress = data[1].GetFloat();
955 actor->ar_beams[i].minmaxposnegstress = data[2].GetFloat();
956 actor->ar_beams[i].strength = data[3].GetFloat();
957 actor->ar_beams[i].L = data[4].GetFloat();
958 actor->ar_beams[i].bm_broken = data[5].GetBool();
959 actor->ar_beams[i].bm_disabled = data[6].GetBool();
960 actor->ar_beams[i].bm_inter_actor = data[7].GetBool();
961 int locked_actor = data[8].GetInt();
962 if (locked_actor != -1 &&
963 locked_actor < (int)actors.size() &&
964 actors[locked_actor] != nullptr)
965 {
966 actor->AddInterActorBeam(&actor->ar_beams[i], actors[locked_actor], ActorLinkingRequestType::LOAD_SAVEGAME); // OK to be invoked here - RestoreSavedState() - processing MSG_SIM_MODIFY_ACTOR_REQUESTED
967 }
968 }
969
970 auto hooks = j_entry["hooks"].GetArray();
971 for (int i = 0; i < actor->ar_hooks.size(); i++)
972 {
973 int lock_node = hooks[i]["lock_node"].GetInt();
974 int locked_actor = hooks[i]["locked_actor"].GetInt();
975 if (lock_node != -1 &&
976 locked_actor != -1 &&
977 locked_actor < (int)actors.size() &&
978 actors[locked_actor] != nullptr)
979 {
980 actor->ar_hooks[i].hk_locked = HookState(hooks[i]["locked"].GetInt());
981 actor->ar_hooks[i].hk_locked_actor = actors[locked_actor];
982 actor->ar_hooks[i].hk_lock_node = &actors[locked_actor]->ar_nodes[lock_node];
983 if (actor->ar_hooks[i].hk_beam->bm_inter_actor)
984 {
985 actor->ar_hooks[i].hk_beam->p2 = actor->ar_hooks[i].hk_lock_node;
986 }
987 }
988 }
989
990 auto ropes = j_entry["ropes"].GetArray();
991 for (int i = 0; i < actor->ar_ropes.size(); i++)
992 {
993 int ropable = ropes[i]["locked_ropable"].GetInt();
994 int locked_actor = ropes[i]["locked_actor"].GetInt();
995 if (ropable != -1 &&
996 locked_actor != -1 &&
997 locked_actor < (int)actors.size() &&
998 actors[locked_actor] != nullptr)
999 {
1000 actor->ar_ropes[i].rp_locked = ropes[i]["locked"].GetInt();
1001 actor->ar_ropes[i].rp_locked_actor = actors[locked_actor];
1002 actor->ar_ropes[i].rp_locked_ropable = &actors[locked_actor]->ar_ropables[ropable];
1003 }
1004 }
1005
1006 auto ties = j_entry["ties"].GetArray();
1007 for (int i = 0; i < actor->ar_ties.size(); i++)
1008 {
1009 int ropable = ties[i]["locked_ropable"].GetInt();
1010 int locked_actor = ties[i]["locked_actor"].GetInt();
1011 if (ropable != -1 &&
1012 locked_actor != -1 &&
1013 locked_actor < (int)actors.size() &&
1014 actors[locked_actor] != nullptr)
1015 {
1016 actor->ar_ties[i].ti_tied = ties[i]["tied"].GetBool();
1017 actor->ar_ties[i].ti_tying = ties[i]["tying"].GetBool();
1018 actor->ar_ties[i].ti_locked_actor = actors[locked_actor];
1019 actor->ar_ties[i].ti_locked_ropable = &actors[locked_actor]->ar_ropables[ropable];
1020 if (actor->ar_ties[i].ti_beam->bm_inter_actor)
1021 {
1022 actor->ar_ties[i].ti_beam->p2 = actor->ar_ties[i].ti_locked_ropable->node;
1023 }
1024 }
1025 }
1026
1027 auto ropables = j_entry["ropables"].GetArray();
1028 for (int i = 0; i < actor->ar_ropables.size(); i++)
1029 {
1030 actor->ar_ropables[i].attached_ties = ropables[i]["attached_ties"].GetInt();
1031 actor->ar_ropables[i].attached_ropes = ropables[i]["attached_ropes"].GetInt();
1032 }
1033
1034 actor->resetSlideNodes();
1035 if (actor->m_slidenodes_locked != j_entry["slidenodes_locked"].GetBool())
1036 {
1037 actor->toggleSlideNodeLock(); // OK to be invoked here - RestoreSavedState() - processing MSG_SIM_MODIFY_ACTOR_REQUESTED
1038 }
1039
1040 actor->UpdateBoundingBoxes();
1041 actor->calculateAveragePosition();
1043}
Central state/object manager and communications hub.
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
#define RGN_SAVEGAMES
Definition Application.h:51
A database of user-installed content alias 'mods' (vehicles, terrains...)
#define _L
Game state manager and message-queue provider.
Handles controller inputs from player.
#define _LC(ctx, str)
Definition Language.h:38
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
#define SAVEGAME_FILE_FORMAT
Definition Savegame.cpp:48
static const int MAX_CLIGHTS
See RoRnet::Lightmask and enum events in InputEngine.h.
static const int MAX_COMMANDS
maximum number of commands per actor
static const float RAD_PER_SEC_TO_RPM
Convert radian/second to RPM (60/2*PI)
The vehicle tuning system; applies addonparts and user overrides to vehicles.
bool ar_parking_brake
Definition Actor.h:467
bool alb_mode
Anti-lock brake state; Enabled? {1/0}.
Definition Actor.h:410
int ar_num_screwprops
Definition Actor.h:391
void setHighBeamsVisible(bool val)
Definition Actor.h:221
EnginePtr ar_engine
Definition Actor.h:432
TransferCase * m_transfer_case
Physics.
Definition Actor.h:643
void toggleTransferCaseGearRatio()
Definition Actor.cpp:1522
float ar_hydro_elevator_state
Definition Actor.h:464
void toggleSlideNodeLock()
void resetSlideNodes()
Reset all the SlideNodes.
float ar_wheel_speed
Physics state; wheel speed in m/s.
Definition Actor.h:453
std::vector< ropable_t > ar_ropables
Definition Actor.h:364
CmdKeyArray ar_command_key
BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
Definition Actor.h:369
wheel_t ar_wheels[MAX_WHEELS]
Definition Actor.h:384
std::vector< rope_t > ar_ropes
Definition Actor.h:363
node_t * ar_nodes
Definition Actor.h:330
int m_num_wheel_diffs
Physics attr.
Definition Actor.h:642
std::vector< tie_t > ar_ties
Definition Actor.h:365
float ar_hydro_rudder_state
Definition Actor.h:462
int m_num_axle_diffs
Physics attr.
Definition Actor.h:640
bool m_slidenodes_locked
Physics state; Are SlideNodes locked?
Definition Actor.h:697
Differential * m_axle_diffs[1+MAX_WHEELS/2]
Physics.
Definition Actor.h:639
ScrewpropPtr ar_screwprops[MAX_SCREWPROPS]
Definition Actor.h:390
unsigned int ar_vector_index
Sim attr; actor element index in std::vector<m_actors>
Definition Actor.h:430
float m_spawn_rotation
Definition Actor.h:631
bool ar_physics_paused
Actor physics individually paused by user.
Definition Actor.h:521
std::vector< Ogre::Vector3 > ar_initial_node_positions
Absolute world positions, for resetting to pristine state.
Definition Actor.h:340
float ar_wheel_spin
Physics state; wheel speed in radians/s.
Definition Actor.h:454
bool tc_mode
Enabled?
Definition Actor.h:504
std::unique_ptr< Buoyance > m_buoyance
Definition Actor.h:490
void setHeadlightsVisible(bool val)
Definition Actor.h:219
float ar_hydro_dir_state
Definition Actor.h:457
Ogre::Vector3 ar_origin
Physics state; base position for softbody nodes.
Definition Actor.h:438
ActorState ar_state
Definition Actor.h:518
AeroEnginePtr ar_aeroengines[MAX_AEROENGINES]
Definition Actor.h:388
float ar_hydro_aileron_state
Definition Actor.h:460
int ar_num_aeroengines
Definition Actor.h:389
void setCustomLightVisible(int number, bool visible)
Definition Actor.cpp:4561
void SyncReset(bool reset_position)
this one should be called only synchronously (without physics running in background)
Definition Actor.cpp:1657
float ar_avg_wheel_speed
Physics state; avg wheel speed in m/s.
Definition Actor.h:455
rotator_t * ar_rotators
Definition Actor.h:358
bool ar_cparticles_active
Gfx state.
Definition Actor.h:559
float cc_target_rpm
Cruise Control.
Definition Actor.h:419
bool cc_mode
Cruise Control.
Definition Actor.h:417
Differential * m_wheel_diffs[MAX_WHEELS/2]
Physics.
Definition Actor.h:641
Skidmark * m_skid_trails[MAX_WHEELS *2]
Definition Actor.h:655
void setBlinkType(BlinkType blink)
Definition Actor.cpp:3161
void calculateAveragePosition()
Definition Actor.cpp:1166
std::vector< hook_t > ar_hooks
Definition Actor.h:366
void UpdateBoundingBoxes()
Definition Actor.cpp:1201
int ar_num_wheels
Definition Actor.h:385
void AddInterActorBeam(beam_t *beam, ActorPtr other, ActorLinkingRequestType type)
Do not call directly - use MSG_SIM_ACTOR_LINKING_REQUESTED
Definition Actor.cpp:3364
Ogre::Vector3 m_avg_node_position
average node position
Definition Actor.h:622
void toggleCustomParticles()
Definition Actor.cpp:3257
void setFogLightsVisible(bool val)
Definition Actor.h:223
beam_t * ar_beams
Definition Actor.h:348
bool ar_trailer_parking_brake
Definition Actor.h:468
float cc_target_speed
Cruise Control.
Definition Actor.h:420
int ar_num_rotators
Definition Actor.h:359
Ogre::Vector3 m_avg_node_position_prev
Definition Actor.h:624
void setBeaconMode(bool val)
Definition Actor.h:224
bool LoadScene(Ogre::String filename)
Definition Savegame.cpp:240
void RestoreSavedState(ActorPtr actor, rapidjson::Value const &j_entry)
Definition Savegame.cpp:787
bool SaveScene(Ogre::String filename)
Definition Savegame.cpp:418
ActorPtrVec m_actors
Use MSG_SIM_{SPAWN/DELETE}_ACTOR_REQUESTED
bool m_forced_awake
disables sleep counters
void SetSimulationPaused(bool v)
bool IsSimulationPaused() const
std::vector< ActorPtr > GetLocalActors()
virtual void setIgnition(bool val)=0
virtual void setThrottle(float val)=0
virtual void setRPM(float _rpm)=0
virtual void setReverse(bool val)=0
T getEnum() const
Definition CVar.h:99
std::string const & getStr() const
Definition CVar.h:95
Ogre::String dname
name parsed from the file
Definition CacheSystem.h:70
CacheEntryPtr FindEntryByFilename(RoR::LoaderType type, bool partial, const std::string &_filename_maybe_bundlequalified)
Returns NULL if none found; "Bundle-qualified" format also specifies the ZIP/directory in modcache,...
Ogre::String GetPrettyName(Ogre::String fname)
CacheEntryPtr FetchSkinByName(std::string const &skin_name)
void setPosition(Ogre::Vector3 position)
Definition Character.cpp:85
void setRotation(Ogre::Radian rotation)
Definition Character.cpp:98
@ CONSOLE_MSGTYPE_INFO
Generic message.
Definition Console.h:60
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition Console.cpp:103
@ CONSOLE_SYSTEM_ERROR
Definition Console.h:52
@ CONSOLE_SYSTEM_NOTICE
Definition Console.h:51
bool SerializeAndWriteJson(std::string const &filename, std::string const &rg_name, rapidjson::Document &j_doc)
DiffType GetActiveDiffType() const
void setWheelSpin(float rpm)
Definition Engine.cpp:933
void pushNetworkState(float engine_rpm, float acc, float clutch, int gear, bool running, bool contact, char auto_mode, char auto_select=-1)
Definition Engine.cpp:885
void startEngine()
Quick engine start. Plays sounds.
Definition Engine.cpp:995
void stopEngine()
stall engine
Definition Engine.cpp:1059
bool isRunning()
Definition Engine.h:101
void SetPrevPlayerActor(ActorPtr actor)
void LoadScene(std::string const &filename)
Matching terrain must be already loaded.
Definition Savegame.cpp:64
void HandleSavegameHotkeys()
Definition Savegame.cpp:99
Character * GetPlayerCharacter()
const ActorPtr & GetPlayerActor()
const TerrainPtr & GetTerrain()
const ActorPtr & GetPrevPlayerActor()
ActorManager m_actor_manager
std::string ExtractSceneTerrain(std::string const &filename)
Returns terrain filename.
Definition Savegame.cpp:87
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
void SaveScene(std::string const &filename)
Definition Savegame.cpp:70
std::string GetQuicksaveFilename()
For currently loaded terrain (cvar 'sim_terrain_name')
Definition Savegame.cpp:56
ActorManager * GetActorManager()
std::string ExtractSceneName(std::string const &filename)
Definition Savegame.cpp:75
void setRudder(float val)
Definition ScrewProp.cpp:94
void setThrottle(float val)
Definition ScrewProp.cpp:81
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition Str.h:36
char * GetBuffer()
Definition Str.h:48
size_t GetCapacity() const
Definition Str.h:49
SkyManager * getSkyManager()
Definition Terrain.cpp:522
bool tr_4wd_mode
Enables 4WD mode.
std::vector< float > tr_gear_ratios
Gear reduction ratios.
static void ExportTuneup(Ogre::DataStreamPtr &stream, TuneupDefPtr &tuneup)
static std::vector< TuneupDefPtr > ParseTuneups(Ogre::DataStreamPtr &stream)
BlinkType
< Turn signal
Definition SimData.h:114
HookState
Definition SimData.h:74
@ EV_COMMON_QUICKSAVE_08
@ EV_COMMON_QUICKSAVE
quicksave scene to the disk
@ EV_COMMON_QUICKSAVE_05
@ EV_COMMON_QUICKLOAD_10
@ EV_COMMON_QUICKLOAD_07
@ EV_COMMON_QUICKLOAD_04
@ EV_COMMON_QUICKLOAD_02
@ EV_COMMON_QUICKSAVE_07
@ EV_COMMON_QUICKLOAD_03
@ EV_COMMON_QUICKLOAD_09
@ EV_COMMON_QUICKSAVE_09
@ EV_COMMON_QUICKSAVE_06
@ EV_COMMON_QUICKLOAD
quickload scene from the disk
@ EV_COMMON_QUICKSAVE_01
@ EV_COMMON_QUICKLOAD_06
@ EV_COMMON_QUICKSAVE_02
@ EV_COMMON_QUICKSAVE_10
@ EV_COMMON_QUICKLOAD_05
@ EV_COMMON_QUICKSAVE_03
@ EV_COMMON_QUICKLOAD_08
@ EV_COMMON_QUICKLOAD_01
@ EV_COMMON_QUICKSAVE_04
@ MSG_SIM_SPAWN_ACTOR_REQUESTED
Payload = RoR::ActorSpawnRequest* (owner)
@ MSG_SIM_SEAT_PLAYER_REQUESTED
Payload = RoR::ActorPtr (owner) | nullptr.
@ MSG_SIM_LOAD_SAVEGAME_REQUESTED
@ MSG_GUI_SHOW_MESSAGE_BOX_REQUESTED
Payload = MessageBoxConfig* (owner)
@ MSG_SIM_DELETE_ACTOR_REQUESTED
Payload = RoR::ActorPtr* (owner)
ActorState
Definition SimData.h:239
CVar * gfx_sky_mode
ContentManager * GetContentManager()
CVar * sim_state
InputEngine * GetInputEngine()
CVar * sim_terrain_name
GameContext * GetGameContext()
CVar * mp_state
Console * GetConsole()
CacheSystem * GetCacheSystem()
CVar * sim_quickload_dialog
@ LT_AllBeam
RefCountingObjectPtr< Actor > ActorPtr
@ CAELUM
Caelum (best looking, slower)
void SplitBundleQualifiedFilename(const std::string &bundleQualifiedFilename, std::string &out_bundleName, std::string &out_filename)
Definition Utils.cpp:239
Ogre::String asr_config
Definition SimData.h:836
TuneupDefPtr asr_working_tuneup
Only filled when editing tuneup via Tuning menu.
Definition SimData.h:842
Ogre::Vector3 asr_position
Definition SimData.h:837
CacheEntryPtr asr_skin_entry
Definition SimData.h:840
std::string asr_filename
Can be in "Bundle-qualified" format, i.e. "mybundle.zip:myactor.truck".
Definition SimData.h:835
Ogre::Quaternion asr_rotation
Definition SimData.h:838
@ SAVEGAME
User spawned and part of a savegame.
std::shared_ptr< rapidjson::Document > asr_saved_state
Pushes msg MODIFY_ACTOR (type RESTORE_SAVED) after spawn.
Definition SimData.h:854
MsgType mbb_mq_message
Message to queue on click.
std::string mbb_mq_description
Message argument to queue on click.
std::vector< MessageBoxButton > mbc_buttons
CVar * mbc_always_ask_conf
If set, displays classic checkbox "[x] Always ask".
Unified game event system - all requests and state changes are reported using a message.
Definition GameContext.h:52
bool bm_broken
Definition SimData.h:326
ActorPtr bm_locked_actor
in case p2 is on another actor
Definition SimData.h:324
float minmaxposnegstress
Definition SimData.h:314
float maxnegstress
Definition SimData.h:316
float strength
Definition SimData.h:317
bool bm_inter_actor
in case p2 is on another actor
Definition SimData.h:323
float L
length
Definition SimData.h:313
bool bm_disabled
Definition SimData.h:325
float maxposstress
Definition SimData.h:315
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition SimData.h:267
Ogre::Vector3 Velocity
Definition SimData.h:268
Ogre::Vector3 RelPosition
relative to the local physics origin (one origin per actor) (shaky)
Definition SimData.h:266
bool wh_is_detached
Definition SimData.h:423