From bc7d0c1c51b5f3fad5aa5a6f01eb1f83d17892d1 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sat, 15 Aug 2015 23:29:21 +0200 Subject: [PATCH] Rewritten program storage, closes #534 --- src/level/robotmain.cpp | 341 +++--------------- src/level/robotmain.h | 8 +- .../implementation/program_storage_impl.cpp | 182 ++++++++-- .../implementation/program_storage_impl.h | 14 +- .../implementation/programmable_impl.cpp | 11 - src/object/implementation/programmable_impl.h | 5 - src/object/interface/program_storage_object.h | 18 +- src/object/interface/programmable_object.h | 3 - src/ui/object_interface.cpp | 6 +- 9 files changed, 239 insertions(+), 349 deletions(-) diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 026faae0..4580bf54 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -3266,10 +3266,11 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) if (line->GetParam("script")->IsDefined()) { - Program* program = dynamic_cast(m_controller)->AddProgram(); - program->filename = "../" + line->GetParam("script")->AsPath("ai"); + CProgramStorageObject* programStorage = dynamic_cast(m_controller); + Program* program = programStorage->AddProgram(); + programStorage->ReadProgram(program, "../" + line->GetParam("script")->AsPath("ai")); program->readOnly = true; - dynamic_cast(m_controller)->SetScriptRun(program); + dynamic_cast(m_controller)->RunProgram(program); } continue; } @@ -3308,41 +3309,22 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) if (obj->Implements(ObjectInterfaceType::Old)) dynamic_cast(obj)->SetDefRank(rankObj); // TODO: do we really need this? - std::map loadedPrograms; if (obj->Implements(ObjectInterfaceType::ProgramStorage)) { - bool allFilled = true; - for (int i = 0; i < 10 || allFilled; i++) - { - std::string op = "script" + StrUtils::ToString(i+1); // script1..script10 - std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString(i+1); // scriptReadOnly1..scriptReadOnly10 - std::string opRunnable = "scriptRunnable" + StrUtils::ToString(i+1); // scriptRunnable1..scriptRunnable10 - if (line->GetParam(op)->IsDefined()) - { - Program* program = dynamic_cast(obj)->AddProgram(); - program->filename = "../" + line->GetParam(op)->AsPath("ai"); - program->readOnly = line->GetParam(opReadOnly)->AsBool(true); - program->runnable = line->GetParam(opRunnable)->AsBool(true); - loadedPrograms[i] = program; - } - else - { - allFilled = false; - } - } - } + CProgramStorageObject* programStorage = dynamic_cast(obj); - if (obj->Implements(ObjectInterfaceType::Programmable)) - { - int i = line->GetParam("run")->AsInt(0); - if (i != 0) + if (obj->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(obj)->GetSelectable() && obj->GetType() != OBJECT_HUMAN) { - dynamic_cast(obj)->SetScriptRun(loadedPrograms[i-1]); + programStorage->SetProgramStorageIndex(rankObj); } - } - if (soluce && obj->Implements(ObjectInterfaceType::ProgramStorage) && line->GetParam("soluce")->IsDefined()) - dynamic_cast(obj)->SetSoluceName(line->GetParam("soluce")->AsPath("ai")); + char categoryChar = GetLevelCategoryDir(m_levelCategory)[0]; + programStorage->LoadAllProgramsForLevel( + line.get(), + m_playerProfile->GetSaveFile(StrUtils::Format("%c%.3d%.3d", categoryChar, m_levelChap, m_levelRank)), + soluce + ); + } } catch (const CObjectCreateException& e) { @@ -3567,9 +3549,6 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) throw CLevelParserException("Unknown command: '" + line->GetCommand() + "' in " + line->GetLevelFilename() + ":" + boost::lexical_cast(line->GetLineNumber())); } - if (m_sceneReadPath.empty()) - CompileScript(soluce); // compiles all scripts - m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED); if (m_ui->GetLoadingScreen()->IsVisible()) { @@ -4293,124 +4272,6 @@ void CRobotMain::FrameShowLimit(float rTime) } } - -//! Compiles all scripts of robots -void CRobotMain::CompileScript(bool soluce) -{ - m_ui->GetLoadingScreen()->SetProgress(0.75f, RT_LOADING_PROGRAMS); - - int numObjects = m_objMan->CountObjectsImplementing(ObjectInterfaceType::Programmable); - int objCounter = 0; - - for (CObject* obj : m_objMan->GetAllObjects()) - { - if (! obj->Implements(ObjectInterfaceType::ProgramStorage)) continue; - - float objectProgress = static_cast(objCounter) / static_cast(numObjects); - m_ui->GetLoadingScreen()->SetProgress(0.75f+objectProgress*0.25f, RT_LOADING_PROGRAMS, "for object "+StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects)); - objCounter++; - - CProgramStorageObject* programStorage = dynamic_cast(obj); - for (auto& prog : programStorage->GetPrograms()) - { - Program* program = prog.get(); - - if (program->filename.empty()) continue; - - std::string name = "ai/" + program->filename; - if (! programStorage->ReadProgram(program, const_cast(name.c_str()))) - { - GetLogger()->Error("Unable to read script from file \"%s\"\n", name.c_str()); - } - //? if (!programStorage->GetCompile(program)) nbError++; - } - - if (soluce) - { - std::string name = programStorage->GetSoluceName(); - if (!name.empty()) - { - programStorage->ReadSoluce(name); // load solution - } - } - - LoadOneScript(obj); - } - - // Start all programs according to the command "run". - for (CObject* obj : m_objMan->GetAllObjects()) - { - if (! obj->Implements(ObjectInterfaceType::Programmable)) continue; - - CProgrammableObject* programmable = dynamic_cast(obj); - Program* program = programmable->GetScriptRun(); - if (program != nullptr) - { - programmable->RunProgram(program); // starts the program - } - } -} - -//! Load all programs of the robot -void CRobotMain::LoadOneScript(CObject *obj) -{ - if (! obj->Implements(ObjectInterfaceType::ProgramStorage)) return; - - CProgramStorageObject* programStorage = dynamic_cast(obj); - - if (!IsSelectable(obj)) return; - - ObjectType type = obj->GetType(); - if (type == OBJECT_HUMAN) return; - - int objRank = obj->GetDefRank(); - if (objRank == -1) return; - - char categoryChar = GetLevelCategoryDir(m_levelCategory)[0]; - for (unsigned int i = 0; i <= 999; i++) - { - char file[MAX_FNAME]; - sprintf(file, "%c%.3d%.3d%.3d%.3d.txt", categoryChar, m_levelChap, m_levelRank, objRank, i); - std::string filename = m_playerProfile->GetSaveFile(file); - - if (CResourceManager::Exists(filename)) - { - Program* program = programStorage->GetOrAddProgram(i); - if(programStorage->GetCompile(program)) continue; // If already loaded (e.g. from level file), skip - programStorage->ReadProgram(program, filename.c_str()); - //? if (!programStorage->GetCompile(program)) nbError++; - } - } -} - -//! Load all programs of the robot -void CRobotMain::LoadFileScript(CObject *obj, const char* filename, int objRank) -{ - if (objRank == -1) return; - - if (! obj->Implements(ObjectInterfaceType::ProgramStorage)) return; - - CProgramStorageObject* programStorage = dynamic_cast(obj); - - ObjectType type = obj->GetType(); - if (type == OBJECT_HUMAN) return; - - std::string dirname = filename; - dirname = dirname.substr(0, dirname.find_last_of("/")); - - char fn[MAX_FNAME]; //TODO: Refactor to std::string - for (unsigned int i = 0; i <= 999; i++) - { - sprintf(fn, "%s/prog%.3d%.3d.txt", dirname.c_str(), objRank, i); - if (CResourceManager::Exists(fn)) - { - Program* program = programStorage->GetOrAddProgram(i); - programStorage->ReadProgram(program, fn); - //? if (!programStorage->GetCompile(program)) nbError++; - } - } -} - //! Saves all programs of all the robots void CRobotMain::SaveAllScript() { @@ -4428,65 +4289,8 @@ void CRobotMain::SaveOneScript(CObject *obj) CProgramStorageObject* programStorage = dynamic_cast(obj); - if (!IsSelectable(obj)) return; - - ObjectType type = obj->GetType(); - if (type == OBJECT_HUMAN) return; - - int objRank = obj->GetDefRank(); - if (objRank == -1) return; - char categoryChar = GetLevelCategoryDir(m_levelCategory)[0]; - auto& programs = programStorage->GetPrograms(); - // TODO: Find a better way to do that - for (unsigned int i = 0; i <= 999; i++) - { - char file[MAX_FNAME]; - sprintf(file, "%c%.3d%.3d%.3d%.3d.txt", categoryChar, m_levelChap, m_levelRank, objRank, i); - std::string filename = m_playerProfile->GetSaveFile(file); - - if (i < programs.size()) - { - programStorage->WriteProgram(programs[i].get(), filename.c_str()); - } - else - { - CResourceManager::Remove(filename); - } - } -} - -//! Saves all programs of the robot. -//! If a program does not exist, the corresponding file is destroyed. -void CRobotMain::SaveFileScript(CObject *obj, const char* filename, int objRank) -{ - if (objRank == -1) return; - - if (! obj->Implements(ObjectInterfaceType::ProgramStorage)) return; - - CProgramStorageObject* programStorage = dynamic_cast(obj); - - ObjectType type = obj->GetType(); - if (type == OBJECT_HUMAN) return; - - std::string dirname = filename; - dirname = dirname.substr(0, dirname.find_last_of("/")); - - char fn[MAX_FNAME]; //TODO: Refactor to std::string - auto& programs = programStorage->GetPrograms(); - // TODO: Find a better way to do that - for (unsigned int i = 0; i <= 999; i++) - { - sprintf(fn, "%s/prog%.3d%.3d.txt", dirname.c_str(), objRank, i); - if (i < programs.size()) - { - programStorage->WriteProgram(programs[i].get(), fn); - } - else - { - CResourceManager::Remove(fn); - } - } + programStorage->SaveAllUserPrograms(m_playerProfile->GetSaveFile(StrUtils::Format("%c%.3d%.3d", categoryChar, m_levelChap, m_levelRank))); } //! Saves the stack of the program in execution of a robot @@ -4579,7 +4383,7 @@ bool CRobotMain::IOIsBusy() } //! Writes an object into the backup file -void CRobotMain::IOWriteObject(CLevelParserLine* line, CObject* obj) +void CRobotMain::IOWriteObject(CLevelParserLine* line, CObject* obj, const std::string& programDir, int objRank) { if (obj->GetType() == OBJECT_FIX) return; @@ -4634,23 +4438,29 @@ void CRobotMain::IOWriteObject(CLevelParserLine* line, CObject* obj) if (obj->GetType() == OBJECT_BASE) line->AddParam("run", MakeUnique(3)); // stops and open (PARAM_FIXSCENE) - if (obj->Implements(ObjectInterfaceType::Programmable) && obj->Implements(ObjectInterfaceType::ProgramStorage)) - { - int run = dynamic_cast(obj)->GetProgramIndex(dynamic_cast(obj)->GetCurrentProgram()); - if (run != -1) - { - line->AddParam("run", MakeUnique(run+1)); - } - } if (obj->Implements(ObjectInterfaceType::ProgramStorage)) { - auto& programs = dynamic_cast(obj)->GetPrograms(); - for (unsigned int i = 0; i < programs.size(); i++) + CProgramStorageObject* programStorage = dynamic_cast(obj); + if(programStorage->GetProgramStorageIndex() >= 0) { - if (programs[i]->readOnly) + programStorage->SaveAllProgramsForSavedScene(line, programDir); + } + else + { + // Probably an object created after the scene started, not loaded from level file + // This means it doesn't normally store programs so it doesn't have program storage id assigned + programStorage->SetProgramStorageIndex(999-objRank); // Set something that won't collide with normal programs + programStorage->SaveAllProgramsForSavedScene(line, programDir); + programStorage->SetProgramStorageIndex(-1); // Disable again + } + + if (obj->Implements(ObjectInterfaceType::Programmable)) + { + int run = dynamic_cast(obj)->GetProgramIndex(dynamic_cast(obj)->GetCurrentProgram()); + if (run != -1) { - line->AddParam("scriptReadOnly" + boost::lexical_cast(i+1), MakeUnique(true)); + line->AddParam("run", MakeUnique(run+1)); } } } @@ -4663,6 +4473,8 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s ShowSaveIndicator(true); m_app->Render(); // update + std::string dirname = filename.substr(0, filename.find_last_of("/")); + CLevelParser levelParser(filename); CLevelParserLineUPtr line; @@ -4727,7 +4539,7 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s if (cargo != nullptr) // object transported? { line = MakeUnique("CreateFret"); - IOWriteObject(line.get(), cargo); + IOWriteObject(line.get(), cargo, dirname, objRank++); levelParser.AddLine(std::move(line)); } } @@ -4738,17 +4550,15 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s if (power != nullptr) // battery transported? { line = MakeUnique("CreatePower"); - IOWriteObject(line.get(), power); + IOWriteObject(line.get(), power, dirname, objRank++); levelParser.AddLine(std::move(line)); } } line = MakeUnique("CreateObject"); - IOWriteObject(line.get(), obj); + IOWriteObject(line.get(), obj, dirname, objRank++); levelParser.AddLine(std::move(line)); - - SaveFileScript(obj, filename.c_str(), objRank++); } try { @@ -4809,7 +4619,7 @@ void CRobotMain::IOWriteSceneFinished() } //! Resumes the game -CObject* CRobotMain::IOReadObject(CLevelParserLine *line, const char* filename, const std::string& objCounterText, float objectProgress, int objRank) +CObject* CRobotMain::IOReadObject(CLevelParserLine *line, const std::string& programDir, const std::string& objCounterText, float objectProgress, int objRank) { ObjectCreateParams params = CObject::ReadCreateParams(line); params.power = -1.0f; @@ -4828,10 +4638,8 @@ CObject* CRobotMain::IOReadObject(CLevelParserLine *line, const char* filename, { COldObject* oldObj = dynamic_cast(obj); - oldObj->SetRotation(line->GetParam("angle")->AsPoint() * Math::DEG_TO_RAD); // Who decided to call this argument differently than in main scene files? :/ - // TODO: Also, why doesn't CStaticObject implement SetRotation? - - oldObj->SetDefRank(objRank); + oldObj->SetPosition(line->GetParam("pos")->AsPoint() * g_unit); + oldObj->SetRotation(line->GetParam("angle")->AsPoint() * Math::DEG_TO_RAD); for (int i = 1; i < OBJECTMAXPART; i++) { @@ -4869,25 +4677,12 @@ CObject* CRobotMain::IOReadObject(CLevelParserLine *line, const char* filename, automat->Start(run); // starts the film } - if (obj->Implements(ObjectInterfaceType::Programmable)) - { - if (run != -1) - { - Program* program = dynamic_cast(obj)->GetOrAddProgram(run-1); - dynamic_cast(obj)->SetScriptRun(program); // marks the program to be started - } - } - if (obj->Implements(ObjectInterfaceType::ProgramStorage)) { - for (unsigned int i = 0; i <= 999; i++) - { - if (line->GetParam("scriptReadOnly" + boost::lexical_cast(i+1))->AsBool(false)) - { - Program* prog = dynamic_cast(obj)->GetOrAddProgram(i); - prog->readOnly = true; - } - } + CProgramStorageObject* programStorage = dynamic_cast(obj); + if (!line->GetParam("programStorageIndex")->IsDefined()) // Backwards combatibility + programStorage->SetProgramStorageIndex(objRank); + programStorage->LoadAllProgramsForSavedScene(line, programDir); } return obj; @@ -4896,6 +4691,8 @@ CObject* CRobotMain::IOReadObject(CLevelParserLine *line, const char* filename, //! Resumes some part of the game CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) { + std::string dirname = filename.substr(0, filename.find_last_of("/")); + CLevelParser levelParser(filename); levelParser.Load(); int numObjects = levelParser.CountLines("CreateObject") + levelParser.CountLines("CreatePower") + levelParser.CountLines("CreateFret"); @@ -4926,19 +4723,19 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) if (line->GetCommand() == "CreateFret") { - cargo = IOReadObject(line.get(), filename.c_str(), StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects), -1); + cargo = IOReadObject(line.get(), dirname, StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects)); objCounter++; } if (line->GetCommand() == "CreatePower") { - power = IOReadObject(line.get(), filename.c_str(), StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects), -1); + power = IOReadObject(line.get(), dirname, StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects)); objCounter++; } if (line->GetCommand() == "CreateObject") { - CObject* obj = IOReadObject(line.get(), filename.c_str(), StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects), objRank++); + CObject* obj = IOReadObject(line.get(), dirname, StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects), objRank++); if (line->GetParam("select")->AsBool(false)) sel = obj; @@ -4966,42 +4763,6 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) } } - // Compiles scripts. - m_ui->GetLoadingScreen()->SetProgress(0.75f, RT_LOADING_PROGRAMS); - numObjects = m_objMan->CountObjectsImplementing(ObjectInterfaceType::Programmable); - objCounter = 0; - - for (CObject* obj : m_objMan->GetAllObjects()) - { - if (! obj->Implements(ObjectInterfaceType::Programmable)) continue; - - float objectProgress = static_cast(objCounter) / static_cast(numObjects); - m_ui->GetLoadingScreen()->SetProgress(0.75f+objectProgress*0.2f, RT_LOADING_PROGRAMS, "for object "+StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects)); - objCounter++; - - if (IsObjectBeingTransported(obj)) continue; // TODO: WTF, programmable transportable objects? - - objRank = obj->GetDefRank(); - if (objRank == -1) continue; - - LoadFileScript(obj, filename.c_str(), objRank); - } - - // Starts scripts - for (CObject* obj : m_objMan->GetAllObjects()) - { - if (! obj->Implements(ObjectInterfaceType::Programmable)) continue; - if (obj->GetDefRank() == -1) continue; - - CProgrammableObject* programmable = dynamic_cast(obj); - - Program* program = programmable->GetScriptRun(); - if (program != nullptr) - { - programmable->RunProgram(program); // starts the program - } - } - m_ui->GetLoadingScreen()->SetProgress(0.95f, RT_LOADING_CBOT_SAVE); // Reads the file of stacks of execution. diff --git a/src/level/robotmain.h b/src/level/robotmain.h index ac460b9b..df1b511d 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -273,12 +273,8 @@ public: void StartShowLimit(); void FrameShowLimit(float rTime); - void CompileScript(bool soluce); - void LoadOneScript(CObject *pObj); - void LoadFileScript(CObject *pObj, const char* filename, int objRank); void SaveAllScript(); void SaveOneScript(CObject *pObj); - void SaveFileScript(CObject *pObj, const char* filename, int objRank); bool SaveFileStack(CObject *pObj, FILE *file, int objRank); bool ReadFileStack(CObject *pObj, FILE *file, int objRank); @@ -293,8 +289,8 @@ public: bool IOWriteScene(std::string filename, std::string filecbot, std::string filescreenshot, char *info); void IOWriteSceneFinished(); CObject* IOReadScene(std::string filename, std::string filecbot); - void IOWriteObject(CLevelParserLine *line, CObject* obj); - CObject* IOReadObject(CLevelParserLine *line, const char* filename, const std::string& objCounterText, float objectProgress, int objRank); + void IOWriteObject(CLevelParserLine *line, CObject* obj, const std::string& programDir, int objRank); + CObject* IOReadObject(CLevelParserLine *line, const std::string& programDir, const std::string& objCounterText, float objectProgress, int objRank = -1); int CreateSpot(Math::Vector pos, Gfx::Color color); diff --git a/src/object/implementation/program_storage_impl.cpp b/src/object/implementation/program_storage_impl.cpp index 7df8d5e0..c4802e8f 100644 --- a/src/object/implementation/program_storage_impl.cpp +++ b/src/object/implementation/program_storage_impl.cpp @@ -20,9 +20,15 @@ #include "object/implementation/programmable_impl.h" #include "common/global.h" +#include "common/logger.h" +#include "common/stringutils.h" + +#include "common/resources/resourcemanager.h" #include "level/robotmain.h" +#include "level/parser/parserline.h" + #include "math/all.h" #include "object/object.h" @@ -48,7 +54,9 @@ CProgramStorageObjectImpl::CProgramStorageObjectImpl(ObjectInterfaceTypes& types m_object(object), m_program(), m_activeVirus(false), - m_soluceName("") + m_soluceName(""), + m_programStorageIndex(-1), + m_allowProgramSave(true) { } @@ -193,33 +201,6 @@ const std::string& CProgramStorageObjectImpl::GetSoluceName() return m_soluceName; } - -// Load a script solution, in the first free script. -// If there is already an identical script, nothing is loaded. - -bool CProgramStorageObjectImpl::ReadSoluce(const std::string& filename) -{ - Program* prog = AddProgram(); - - if ( !ReadProgram(prog, filename) ) return false; // load solution - prog->readOnly = true; - - for(unsigned int i = 0; i < m_program.size(); i++) - { - if(m_program[i].get() == prog) continue; - - //TODO: This is bad. It's very sensitive to things like \n vs \r\n etc. - if ( m_program[i]->script->Compare(prog->script.get()) ) // the same already? - { - m_program[i]->readOnly = true; // Mark is as read-only - RemoveProgram(prog); - return false; - } - } - - return true; -} - // Load a script with a text file. bool CProgramStorageObjectImpl::ReadProgram(Program* program, const std::string& filename) @@ -244,3 +225,148 @@ bool CProgramStorageObjectImpl::WriteProgram(Program* program, const std::string return false; } + +void CProgramStorageObjectImpl::SetProgramStorageIndex(int programStorageIndex) +{ + m_programStorageIndex = programStorageIndex; +} + +int CProgramStorageObjectImpl::GetProgramStorageIndex() +{ + return m_programStorageIndex; +} + +void CProgramStorageObjectImpl::SaveAllUserPrograms(const std::string& userSource) +{ + if (!m_allowProgramSave) return; + if (m_programStorageIndex < 0) return; + GetLogger()->Debug("Saving user programs to '%s%.3d___.txt'\n", userSource.c_str(), m_programStorageIndex); + + for (unsigned int i = 0; i <= 999; i++) + { + std::string filename = userSource + StrUtils::Format("%.3d%.3d.txt", m_programStorageIndex, i); + + if (i < m_program.size() && !m_program[i]->loadedFromLevel) + { + GetLogger()->Trace("Loading program '%s' into user directory\n", filename.c_str()); + WriteProgram(m_program[i].get(), filename); + } + else + { + CResourceManager::Remove(filename); + } + } +} + +void CProgramStorageObjectImpl::LoadAllProgramsForLevel(CLevelParserLine* levelSource, const std::string& userSource, bool loadSoluce) +{ + int run = levelSource->GetParam("run")->AsInt(0)-1; + bool allFilled = true; + for (int i = 0; i < 10 || allFilled; i++) + { + std::string op = "script" + StrUtils::ToString(i+1); // script1..script10 + std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString(i+1); // scriptReadOnly1..scriptReadOnly10 + std::string opRunnable = "scriptRunnable" + StrUtils::ToString(i+1); // scriptRunnable1..scriptRunnable10 + if (levelSource->GetParam(op)->IsDefined()) + { + std::string filename = levelSource->GetParam(op)->AsPath("ai"); + GetLogger()->Trace("Loading program '%s' from level file\n", filename.c_str()); + Program* program = AddProgram(); + ReadProgram(program, filename); + program->readOnly = levelSource->GetParam(opReadOnly)->AsBool(true); + program->runnable = levelSource->GetParam(opRunnable)->AsBool(true); + program->loadedFromLevel = true; + + if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run) + { + dynamic_cast(m_object)->RunProgram(program); + } + } + else + { + allFilled = false; + } + } + + if (loadSoluce && levelSource->GetParam("soluce")->IsDefined()) + { + std::string filename = levelSource->GetParam("soluce")->AsPath("ai"); + GetLogger()->Trace("Loading program '%s' as soluce file\n", filename.c_str()); + Program* program = AddProgram(); + ReadProgram(program, filename); + program->readOnly = true; + program->runnable = false; + program->loadedFromLevel = true; + } + + if (m_programStorageIndex >= 0) + { + GetLogger()->Debug("Loading user programs from '%s%.3d___.txt'\n", userSource.c_str(), m_programStorageIndex); + for (unsigned int i = 0; i <= 999; i++) + { + std::string filename = userSource + StrUtils::Format("%.3d%.3d.txt", m_programStorageIndex, i); + if (CResourceManager::Exists(filename)) + { + Program* program = GetOrAddProgram(i); + if(GetCompile(program)) continue; // If already loaded from level file, skip + GetLogger()->Trace("Loading program '%s' from user directory\n", filename.c_str()); + ReadProgram(program, filename); + } + } + } +} + +void CProgramStorageObjectImpl::SaveAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource) +{ + levelSourceLine->AddParam("programStorageIndex", MakeUnique(m_programStorageIndex)); + assert(m_programStorageIndex != -1); + + GetLogger()->Debug("Saving saved scene programs to '%s/prog%.3d___.txt'\n", levelSource.c_str(), m_programStorageIndex); + for (int i = 0; i < 999; i++) + { + std::string filename = levelSource + StrUtils::Format("/prog%.3d%.3d.txt", m_programStorageIndex, i); + if (i >= static_cast(m_program.size())) + { + CResourceManager::Remove(filename); + continue; + } + + levelSourceLine->AddParam("scriptReadOnly" + StrUtils::ToString(i+1), MakeUnique(m_program[i]->readOnly)); + levelSourceLine->AddParam("scriptRunnable" + StrUtils::ToString(i+1), MakeUnique(m_program[i]->runnable)); + GetLogger()->Trace("Saving program '%s' to saved scene\n", filename.c_str()); + WriteProgram(m_program[i].get(), filename); + } +} + +void CProgramStorageObjectImpl::LoadAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource) +{ + m_programStorageIndex = levelSourceLine->GetParam("programStorageIndex")->AsInt(-1); + if(m_programStorageIndex == -1) return; + + GetLogger()->Debug("Loading saved scene programs from '%s/prog%.3d___.txt'\n", levelSource.c_str(), m_programStorageIndex); + int run = levelSourceLine->GetParam("run")->AsInt(0)-1; + for (int i = 0; i <= 999; i++) + { + std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString(i+1); // scriptReadOnly1..scriptReadOnly10 + std::string opRunnable = "scriptRunnable" + StrUtils::ToString(i+1); // scriptRunnable1..scriptRunnable10 + + std::string filename = levelSource + StrUtils::Format("/prog%.3d%.3d.txt", m_programStorageIndex, i); + if (CResourceManager::Exists(filename)) + { + GetLogger()->Trace("Loading program '%s' from saved scene\n", filename.c_str()); + Program* program = GetOrAddProgram(i); + ReadProgram(program, filename); + program->readOnly = levelSourceLine->GetParam(opReadOnly)->AsBool(true); + program->runnable = levelSourceLine->GetParam(opRunnable)->AsBool(true); + + if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run) + { + dynamic_cast(m_object)->RunProgram(program); + } + } + } + + // Disable automatic user program storage now!! + // This is to prevent overwriting auto-saved user programs with older versions from saved scenes + m_allowProgramSave = false; +} diff --git a/src/object/implementation/program_storage_impl.h b/src/object/implementation/program_storage_impl.h index a0725a6c..9b6d15a2 100644 --- a/src/object/implementation/program_storage_impl.h +++ b/src/object/implementation/program_storage_impl.h @@ -40,7 +40,6 @@ public: void SetSoluceName(const std::string& name) override; const std::string& GetSoluceName() override; - bool ReadSoluce(const std::string& filename) override; bool ReadProgram(Program* program, const std::string& filename) override; bool GetCompile(Program* program) override; bool WriteProgram(Program* program, const std::string& filename) override; @@ -55,6 +54,16 @@ public: Program* GetProgram(int index) override; Program* GetOrAddProgram(int index) override; int GetProgramIndex(Program* program) override; + + void SetProgramStorageIndex(int programStorageIndex) override; + int GetProgramStorageIndex() override; + + void SaveAllUserPrograms(const std::string& userSource) override; + void LoadAllProgramsForLevel(CLevelParserLine* levelSource, const std::string& userSource, bool loadSoluce) override; + + void SaveAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource) override; + void LoadAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource) override; + private: CObject* m_object; @@ -64,4 +73,7 @@ private: bool m_activeVirus; std::string m_soluceName; + + int m_programStorageIndex; + bool m_allowProgramSave; }; diff --git a/src/object/implementation/programmable_impl.cpp b/src/object/implementation/programmable_impl.cpp index 5fbef135..8976ebd9 100644 --- a/src/object/implementation/programmable_impl.cpp +++ b/src/object/implementation/programmable_impl.cpp @@ -49,7 +49,6 @@ CProgrammableObjectImpl::CProgrammableObjectImpl(ObjectInterfaceTypes& types, CO m_activity(true), m_cmdLine(), m_currentProgram(nullptr), - m_scriptRun(nullptr), m_traceRecord(false), m_traceOper(TO_STOP), m_traceAngle(0.0f), @@ -137,16 +136,6 @@ bool CProgrammableObjectImpl::IsProgram() } -void CProgrammableObjectImpl::SetScriptRun(Program* program) -{ - m_scriptRun = program; -} - -Program* CProgrammableObjectImpl::GetScriptRun() -{ - return m_scriptRun; -} - // Load a stack of script implementation from a file. bool CProgrammableObjectImpl::ReadStack(FILE *file) diff --git a/src/object/implementation/programmable_impl.h b/src/object/implementation/programmable_impl.h index 4704c646..66afb77e 100644 --- a/src/object/implementation/programmable_impl.h +++ b/src/object/implementation/programmable_impl.h @@ -57,9 +57,6 @@ public: Program* GetCurrentProgram() override; void StopProgram() override; - void SetScriptRun(Program* rank) override; - Program* GetScriptRun() override; - bool ReadStack(FILE *file) override; bool WriteStack(FILE *file) override; @@ -92,8 +89,6 @@ private: Program* m_currentProgram; - Program* m_scriptRun; - bool m_traceRecord; TraceOper m_traceOper; Math::Vector m_tracePos; diff --git a/src/object/interface/program_storage_object.h b/src/object/interface/program_storage_object.h index 5b16c538..3b595b29 100644 --- a/src/object/interface/program_storage_object.h +++ b/src/object/interface/program_storage_object.h @@ -26,13 +26,14 @@ #include class CScript; +class CLevelParserLine; struct Program { std::unique_ptr script; - std::string filename; bool readOnly = false; bool runnable = true; + bool loadedFromLevel = false; }; /** @@ -56,7 +57,6 @@ public: virtual void SetSoluceName(const std::string& name) = 0; virtual const std::string& GetSoluceName() = 0; - virtual bool ReadSoluce(const std::string& filename) = 0; virtual bool ReadProgram(Program* program, const std::string& filename) = 0; virtual bool GetCompile(Program* program) = 0; virtual bool WriteProgram(Program* program, const std::string& filename) = 0; @@ -71,4 +71,18 @@ public: virtual Program* GetProgram(int index) = 0; virtual Program* GetOrAddProgram(int index) = 0; virtual int GetProgramIndex(Program* program) = 0; + + //! Set index for use in filename for saved programs (-1 to disable) + virtual void SetProgramStorageIndex(int programStorageIndex) = 0; + virtual int GetProgramStorageIndex() = 0; + + //! Save all user programs + virtual void SaveAllUserPrograms(const std::string& userSource) = 0; + //! Load all programs when loading the level including previously saved user programs + virtual void LoadAllProgramsForLevel(CLevelParserLine* levelSource, const std::string& userSource, bool loadSoluce) = 0; + + //! Save all programs when saving the saved scene + virtual void SaveAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource) = 0; + //! Load all programs when loading the saved scene + virtual void LoadAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource) = 0; }; diff --git a/src/object/interface/programmable_object.h b/src/object/interface/programmable_object.h index 7df7ee45..3a293985 100644 --- a/src/object/interface/programmable_object.h +++ b/src/object/interface/programmable_object.h @@ -48,9 +48,6 @@ public: virtual Program* GetCurrentProgram() = 0; virtual void StopProgram() = 0; - virtual void SetScriptRun(Program* rank) = 0; - virtual Program* GetScriptRun() = 0; - virtual bool ReadStack(FILE *file) = 0; virtual bool WriteStack(FILE *file) = 0; diff --git a/src/ui/object_interface.cpp b/src/ui/object_interface.cpp index 87a4514d..ad83fc5f 100644 --- a/src/ui/object_interface.cpp +++ b/src/ui/object_interface.cpp @@ -1623,10 +1623,10 @@ void CObjectInterface::UpdateInterface() EnableInterface(pw, EVENT_OBJECT_PROGEDIT, !m_programmable->IsTraceRecord() && m_selScript < m_programStorage->GetProgramCount() && m_main->CanPlayerInteract()); EnableInterface(pw, EVENT_OBJECT_PROGLIST, bEnable && !m_programmable->IsTraceRecord()); EnableInterface(pw, EVENT_OBJECT_PROGADD, !m_programmable->IsProgram() && m_main->CanPlayerInteract()); - EnableInterface(pw, EVENT_OBJECT_PROGREMOVE, !m_programmable->IsProgram() && m_selScript < m_programStorage->GetProgramCount() && !m_programStorage->GetProgram(m_selScript)->readOnly && m_main->CanPlayerInteract()); - EnableInterface(pw, EVENT_OBJECT_PROGCLONE, !m_programmable->IsProgram() && m_selScript < m_programStorage->GetProgramCount() && m_programStorage->GetProgram(m_selScript)->runnable && m_main->CanPlayerInteract()); + EnableInterface(pw, EVENT_OBJECT_PROGREMOVE, !m_programmable->IsProgram() && m_selScript >= 0 && m_selScript < m_programStorage->GetProgramCount() && !m_programStorage->GetProgram(m_selScript)->readOnly && m_main->CanPlayerInteract()); + EnableInterface(pw, EVENT_OBJECT_PROGCLONE, !m_programmable->IsProgram() && m_selScript >= 0 && m_selScript < m_programStorage->GetProgramCount() && m_programStorage->GetProgram(m_selScript)->runnable && m_main->CanPlayerInteract()); EnableInterface(pw, EVENT_OBJECT_PROGMOVEUP, !m_programmable->IsProgram() && m_programStorage->GetProgramCount() >= 2 && m_selScript > 0 && m_main->CanPlayerInteract()); - EnableInterface(pw, EVENT_OBJECT_PROGMOVEDOWN,!m_programmable->IsProgram() && m_programStorage->GetProgramCount() >= 2 && m_selScript < m_programStorage->GetProgramCount()-1 && m_main->CanPlayerInteract()); + EnableInterface(pw, EVENT_OBJECT_PROGMOVEDOWN,!m_programmable->IsProgram() && m_programStorage->GetProgramCount() >= 2 && m_selScript >= 0 && m_selScript < m_programStorage->GetProgramCount()-1 && m_main->CanPlayerInteract()); EnableInterface(pw, EVENT_OBJECT_LEFT, bEnable); EnableInterface(pw, EVENT_OBJECT_RIGHT, bEnable); EnableInterface(pw, EVENT_OBJECT_UP, bEnable);