414 lines
15 KiB
C++
414 lines
15 KiB
C++
/*
|
|
* This file is part of the Colobot: Gold Edition source code
|
|
* Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam
|
|
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see http://gnu.org/licenses
|
|
*/
|
|
|
|
#include "object/implementation/program_storage_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"
|
|
#include "object/old_object.h"
|
|
|
|
#include "object/interface/controllable_object.h"
|
|
#include "object/interface/task_executor_object.h"
|
|
|
|
#include "object/motion/motion.h"
|
|
#include "object/motion/motionvehicle.h"
|
|
|
|
#include "physics/physics.h"
|
|
|
|
#include "script/script.h"
|
|
|
|
#include "ui/controls/edit.h"
|
|
|
|
#include <algorithm>
|
|
#include <iomanip>
|
|
#include <regex>
|
|
|
|
CProgramStorageObjectImpl::CProgramStorageObjectImpl(ObjectInterfaceTypes& types, CObject* object)
|
|
: CProgramStorageObject(types),
|
|
m_object(object),
|
|
m_program(),
|
|
m_activeVirus(false),
|
|
m_programStorageIndex(-1),
|
|
m_allowProgramSave(true)
|
|
{
|
|
}
|
|
|
|
CProgramStorageObjectImpl::~CProgramStorageObjectImpl()
|
|
{}
|
|
|
|
|
|
bool CProgramStorageObjectImpl::IntroduceVirus()
|
|
{
|
|
if(m_program.size() == 0) return false;
|
|
|
|
for ( int i=0 ; i<50 ; i++ )
|
|
{
|
|
int programIndex = rand() % m_program.size();
|
|
if ( m_program[programIndex]->script->IntroduceVirus() ) // tries to introduce
|
|
{
|
|
m_program[programIndex]->filename = ""; // The program is changed, so force it to save instead of just the filename
|
|
m_activeVirus = true; // active virus
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CProgramStorageObjectImpl::SetActiveVirus(bool bActive)
|
|
{
|
|
m_activeVirus = bActive;
|
|
|
|
if ( !m_activeVirus ) // virus disabled?
|
|
{
|
|
m_object->SetVirusMode(false); // chtites (??? - Programerus) letters also
|
|
}
|
|
}
|
|
|
|
bool CProgramStorageObjectImpl::GetActiveVirus()
|
|
{
|
|
return m_activeVirus;
|
|
}
|
|
|
|
|
|
Program* CProgramStorageObjectImpl::AddProgram()
|
|
{
|
|
assert(m_object->Implements(ObjectInterfaceType::Old)); //TODO
|
|
auto program = std::make_unique<Program>();
|
|
program->script = std::make_unique<CScript>(dynamic_cast<COldObject*>(this));
|
|
|
|
Program* prog = program.get();
|
|
AddProgram(std::move(program));
|
|
return prog;
|
|
}
|
|
|
|
void CProgramStorageObjectImpl::AddProgram(std::unique_ptr<Program> program)
|
|
{
|
|
m_program.push_back(std::move(program));
|
|
m_object->UpdateInterface();
|
|
}
|
|
|
|
void CProgramStorageObjectImpl::RemoveProgram(Program* program)
|
|
{
|
|
m_program.erase(
|
|
std::remove_if(m_program.begin(), m_program.end(),
|
|
[program](std::unique_ptr<Program>& prog) { return prog.get() == program; }),
|
|
m_program.end());
|
|
|
|
m_object->UpdateInterface();
|
|
}
|
|
|
|
Program* CProgramStorageObjectImpl::CloneProgram(Program* program)
|
|
{
|
|
Program* newprog = AddProgram();
|
|
|
|
// TODO: Is there any reason CScript doesn't have a function to get the program code directly?
|
|
auto edit = std::make_unique<Ui::CEdit>();
|
|
program->script->PutScript(edit.get(), "");
|
|
newprog->script->GetScript(edit.get());
|
|
|
|
return newprog;
|
|
}
|
|
|
|
std::vector<std::unique_ptr<Program>>& CProgramStorageObjectImpl::GetPrograms()
|
|
{
|
|
return m_program;
|
|
}
|
|
|
|
int CProgramStorageObjectImpl::GetProgramCount()
|
|
{
|
|
return static_cast<int>(m_program.size());
|
|
}
|
|
|
|
int CProgramStorageObjectImpl::GetProgramIndex(Program* program)
|
|
{
|
|
for(unsigned int i = 0; i < m_program.size(); i++)
|
|
{
|
|
if(m_program[i].get() == program)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
Program* CProgramStorageObjectImpl::GetProgram(int index)
|
|
{
|
|
if(index < 0 || index >= static_cast<int>(m_program.size()))
|
|
return nullptr;
|
|
|
|
return m_program[index].get();
|
|
}
|
|
|
|
Program* CProgramStorageObjectImpl::GetOrAddProgram(int index)
|
|
{
|
|
if(index < 0)
|
|
return nullptr;
|
|
|
|
if(index < static_cast<int>(m_program.size()))
|
|
return m_program[index].get();
|
|
|
|
for(int i = m_program.size(); i < index; i++)
|
|
{
|
|
AddProgram();
|
|
}
|
|
return AddProgram();
|
|
}
|
|
|
|
|
|
bool CProgramStorageObjectImpl::ReadProgram(Program* program, const std::string& filename)
|
|
{
|
|
if ( program->script->ReadScript(filename.c_str()) ) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CProgramStorageObjectImpl::WriteProgram(Program* program, const std::string& filename)
|
|
{
|
|
if ( program->script->WriteScript(filename.c_str()) ) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CProgramStorageObjectImpl::GetCompile(Program* program)
|
|
{
|
|
return program->script->GetCompile();
|
|
}
|
|
|
|
|
|
|
|
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 < m_program.size(); i++)
|
|
{
|
|
std::string filename = userSource + StrUtils::Format("%.3d%.3d.txt", m_programStorageIndex, i);
|
|
|
|
if (m_program[i]->filename.empty())
|
|
{
|
|
GetLogger()->Trace("Saving program '%s' into user directory\n", filename.c_str());
|
|
WriteProgram(m_program[i].get(), filename);
|
|
}
|
|
}
|
|
|
|
std::string dir = userSource.substr(0, userSource.find_last_of("/"));
|
|
std::string file = userSource.substr(userSource.find_last_of("/")+1) + StrUtils::Format("%.3d([0-9]{3})\\.txt", m_programStorageIndex);
|
|
std::regex regex(file);
|
|
for (const std::string& filename : CResourceManager::ListFiles(dir))
|
|
{
|
|
std::smatch matches;
|
|
if (std::regex_match(filename, matches, regex))
|
|
{
|
|
unsigned int id = std::stoul(matches[1]);
|
|
if (id >= m_program.size() || !m_program[id]->filename.empty())
|
|
{
|
|
GetLogger()->Trace("Removing old program '%s/%s'\n", dir.c_str(), filename.c_str());
|
|
CResourceManager::Remove(dir+"/"+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<int>(i+1); // script1..script10
|
|
std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString<int>(i+1); // scriptReadOnly1..scriptReadOnly10
|
|
std::string opRunnable = "scriptRunnable" + StrUtils::ToString<int>(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->filename = levelSource->GetParam(op)->AsString();
|
|
|
|
if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run)
|
|
{
|
|
dynamic_cast<CProgrammableObject&>(*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->filename = levelSource->GetParam("soluce")->AsString();
|
|
}
|
|
|
|
if (m_programStorageIndex >= 0)
|
|
{
|
|
GetLogger()->Debug("Loading user programs from '%s%.3d___.txt'\n", userSource.c_str(), m_programStorageIndex);
|
|
|
|
std::string dir = userSource.substr(0, userSource.find_last_of("/"));
|
|
std::string file = userSource.substr(userSource.find_last_of("/")+1) + StrUtils::Format("%.3d([0-9]{3})\\.txt", m_programStorageIndex);
|
|
std::regex regex(file);
|
|
for (const std::string& filename : CResourceManager::ListFiles(dir))
|
|
{
|
|
std::smatch matches;
|
|
if (std::regex_match(filename, matches, regex))
|
|
{
|
|
unsigned int i = std::stoul(matches[1]);
|
|
Program* program = GetOrAddProgram(i);
|
|
if(GetCompile(program)) program = AddProgram(); // If original slot is already used, get a new one
|
|
GetLogger()->Trace("Loading program '%s/%s' from user directory\n", dir.c_str(), filename.c_str());
|
|
ReadProgram(program, dir+"/"+filename);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CProgramStorageObjectImpl::SaveAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource)
|
|
{
|
|
levelSourceLine->AddParam("programStorageIndex", std::make_unique<CLevelParserParam>(m_programStorageIndex));
|
|
|
|
for (unsigned int i = 0; i < m_program.size(); i++)
|
|
{
|
|
if (!m_program[i]->filename.empty() && m_program[i]->readOnly)
|
|
{
|
|
levelSourceLine->AddParam("script" + StrUtils::ToString<int>(i+1), std::make_unique<CLevelParserParam>(m_program[i]->filename));
|
|
levelSourceLine->AddParam("scriptReadOnly" + StrUtils::ToString<int>(i+1), std::make_unique<CLevelParserParam>(m_program[i]->readOnly));
|
|
levelSourceLine->AddParam("scriptRunnable" + StrUtils::ToString<int>(i+1), std::make_unique<CLevelParserParam>(m_program[i]->runnable));
|
|
}
|
|
}
|
|
|
|
if (m_programStorageIndex < 0) return;
|
|
if (!m_object->Implements(ObjectInterfaceType::Controllable) || !dynamic_cast<CControllableObject&>(*m_object).GetSelectable() || m_object->GetType() == OBJECT_HUMAN) return;
|
|
|
|
GetLogger()->Debug("Saving saved scene programs to '%s/prog%.3d___.txt'\n", levelSource.c_str(), m_programStorageIndex);
|
|
for (unsigned int i = 0; i < m_program.size(); i++)
|
|
{
|
|
std::string filename = levelSource + StrUtils::Format("/prog%.3d%.3d.txt", m_programStorageIndex, i);
|
|
if (!m_program[i]->filename.empty() && m_program[i]->readOnly) continue;
|
|
|
|
GetLogger()->Trace("Saving program '%s' to saved scene\n", filename.c_str());
|
|
WriteProgram(m_program[i].get(), filename);
|
|
levelSourceLine->AddParam("scriptReadOnly" + StrUtils::ToString<int>(i+1), std::make_unique<CLevelParserParam>(m_program[i]->readOnly));
|
|
levelSourceLine->AddParam("scriptRunnable" + StrUtils::ToString<int>(i+1), std::make_unique<CLevelParserParam>(m_program[i]->runnable));
|
|
}
|
|
|
|
std::regex regex(StrUtils::Format("prog%.3d([0-9]{3})\\.txt", m_programStorageIndex));
|
|
for (const std::string& filename : CResourceManager::ListFiles(levelSource))
|
|
{
|
|
std::smatch matches;
|
|
if (std::regex_match(filename, matches, regex))
|
|
{
|
|
unsigned int id = std::stoul(matches[1]);
|
|
if (id >= m_program.size() || !m_program[id]->filename.empty())
|
|
{
|
|
GetLogger()->Trace("Removing old program '%s/%s' from saved scene\n", levelSource.c_str(), filename.c_str());
|
|
CResourceManager::Remove(levelSource+"/"+filename);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CProgramStorageObjectImpl::LoadAllProgramsForSavedScene(CLevelParserLine* levelSourceLine, const std::string& levelSource)
|
|
{
|
|
int run = levelSourceLine->GetParam("run")->AsInt(0)-1;
|
|
m_programStorageIndex = levelSourceLine->GetParam("programStorageIndex")->AsInt(-1);
|
|
|
|
for (int i = 0; i <= 999; i++)
|
|
{
|
|
std::string op = "script" + StrUtils::ToString<int>(i+1); // script1..script10
|
|
std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString<int>(i+1); // scriptReadOnly1..scriptReadOnly10
|
|
std::string opRunnable = "scriptRunnable" + StrUtils::ToString<int>(i+1); // scriptRunnable1..scriptRunnable10
|
|
if (levelSourceLine->GetParam(op)->IsDefined())
|
|
{
|
|
std::string filename = levelSourceLine->GetParam(op)->AsPath("ai");
|
|
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);
|
|
program->filename = levelSourceLine->GetParam(op)->AsString();
|
|
|
|
if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run)
|
|
{
|
|
dynamic_cast<CProgrammableObject&>(*m_object).RunProgram(program);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(m_programStorageIndex < 0) return;
|
|
|
|
GetLogger()->Debug("Loading saved scene programs from '%s/prog%.3d___.txt'\n", levelSource.c_str(), m_programStorageIndex);
|
|
for (int i = 0; i <= 999; i++)
|
|
{
|
|
std::string opReadOnly = "scriptReadOnly" + StrUtils::ToString<int>(i+1); // scriptReadOnly1..scriptReadOnly10
|
|
std::string opRunnable = "scriptRunnable" + StrUtils::ToString<int>(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<CProgrammableObject&>(*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;
|
|
}
|