mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 17:59:56 +00:00
store global script state in saved game files
This commit is contained in:
parent
2a35c7d33a
commit
3590fa40bd
14 changed files with 290 additions and 33 deletions
|
@ -4,6 +4,7 @@
|
|||
#include <cassert>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/esm/globalscript.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
|
@ -90,4 +91,61 @@ namespace MWScript
|
|||
addScript (iter->mScript);
|
||||
}
|
||||
}
|
||||
|
||||
int GlobalScripts::countSavedGameRecords() const
|
||||
{
|
||||
return mScripts.size();
|
||||
}
|
||||
|
||||
void GlobalScripts::write (ESM::ESMWriter& writer) const
|
||||
{
|
||||
for (std::map<std::string, std::pair<bool, Locals> >::const_iterator iter (mScripts.begin());
|
||||
iter!=mScripts.end(); ++iter)
|
||||
{
|
||||
ESM::GlobalScript script;
|
||||
|
||||
script.mId = iter->first;
|
||||
|
||||
iter->second.second.write (script.mLocals, iter->first);
|
||||
|
||||
script.mRunning = iter->second.first ? 1 : 0;
|
||||
|
||||
writer.startRecord (ESM::REC_GSCR);
|
||||
script.save (writer);
|
||||
writer.endRecord (ESM::REC_GSCR);
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalScripts::readRecord (ESM::ESMReader& reader, int32_t type)
|
||||
{
|
||||
if (type==ESM::REC_GSCR)
|
||||
{
|
||||
ESM::GlobalScript script;
|
||||
script.load (reader);
|
||||
|
||||
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
|
||||
mScripts.find (script.mId);
|
||||
|
||||
if (iter==mScripts.end())
|
||||
{
|
||||
if (const ESM::Script *scriptRecord = mStore.get<ESM::Script>().search (script.mId))
|
||||
{
|
||||
std::pair<bool, Locals> data (false, Locals());
|
||||
|
||||
data.second.configure (*scriptRecord);
|
||||
|
||||
iter = mScripts.insert (std::make_pair (script.mId, data)).first;
|
||||
}
|
||||
else // script does not exist anymore
|
||||
return true;
|
||||
}
|
||||
|
||||
iter->second.first = script.mRunning!=0;
|
||||
iter->second.second.read (script.mLocals, script.mId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,16 @@
|
|||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <libs/platform/stdint.h>
|
||||
|
||||
#include "locals.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMWriter;
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
struct ESMStore;
|
||||
|
@ -35,6 +43,15 @@ namespace MWScript
|
|||
|
||||
void addStartup();
|
||||
///< Add startup script
|
||||
|
||||
int countSavedGameRecords() const;
|
||||
|
||||
void write (ESM::ESMWriter& writer) const;
|
||||
|
||||
bool readRecord (ESM::ESMReader& reader, int32_t type);
|
||||
///< Records for variables that do not exist are dropped silently.
|
||||
///
|
||||
/// \return Known type?
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "locals.hpp"
|
||||
|
||||
#include <components/esm/loadscpt.hpp>
|
||||
#include <components/esm/variant.hpp>
|
||||
#include <components/esm/locals.hpp>
|
||||
|
||||
#include <components/compiler/locals.hpp>
|
||||
|
||||
|
@ -65,4 +67,68 @@ namespace MWScript
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Locals::write (ESM::Locals& locals, const std::string& script) const
|
||||
{
|
||||
const Compiler::Locals& declarations =
|
||||
MWBase::Environment::get().getScriptManager()->getLocals(script);
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
{
|
||||
char type = 0;
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0: type = 's'; break;
|
||||
case 1: type = 'l'; break;
|
||||
case 2: type = 'f'; break;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& names = declarations.get (type);
|
||||
|
||||
for (int i2=0; i2<static_cast<int> (names.size()); ++i2)
|
||||
{
|
||||
ESM::Variant value;
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0: value.setType (ESM::VT_Int); value.setInteger (mShorts.at (i2)); break;
|
||||
case 1: value.setType (ESM::VT_Int); value.setInteger (mLongs.at (i2)); break;
|
||||
case 2: value.setType (ESM::VT_Float); value.setFloat (mFloats.at (i2)); break;
|
||||
}
|
||||
|
||||
locals.mVariables.push_back (std::make_pair (names[i2], value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Locals::read (const ESM::Locals& locals, const std::string& script)
|
||||
{
|
||||
const Compiler::Locals& declarations =
|
||||
MWBase::Environment::get().getScriptManager()->getLocals(script);
|
||||
|
||||
for (std::vector<std::pair<std::string, ESM::Variant> >::const_iterator iter
|
||||
= locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter)
|
||||
{
|
||||
char type = declarations.getType (iter->first);
|
||||
char index = declarations.getIndex (iter->first);
|
||||
|
||||
try
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 's': mShorts.at (index) = iter->second.getInteger(); break;
|
||||
case 'l': mLongs.at (index) = iter->second.getInteger(); break;
|
||||
case 'f': mFloats.at (index) = iter->second.getFloat(); break;
|
||||
|
||||
// silently ignore locals that don't exist anymore
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// ignore type changes
|
||||
/// \todo write to log
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace ESM
|
||||
{
|
||||
struct Script;
|
||||
struct Locals;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
|
@ -23,6 +24,9 @@ namespace MWScript
|
|||
bool setVarByInt(const std::string& script, const std::string& var, int val);
|
||||
int getIntVar (const std::string& script, const std::string& var); ///< if var does not exist, returns 0
|
||||
|
||||
void write (ESM::Locals& locals, const std::string& script) const;
|
||||
|
||||
void read (const ESM::Locals& locals, const std::string& script);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
|||
1 // saved game header
|
||||
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
|
||||
);
|
||||
|
||||
writer.save (stream);
|
||||
|
@ -124,6 +125,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
|||
|
||||
MWBase::Environment::get().getJournal()->write (writer);
|
||||
MWBase::Environment::get().getWorld()->write (writer);
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer);
|
||||
|
||||
writer.close();
|
||||
|
||||
|
@ -173,6 +175,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
|
|||
MWBase::Environment::get().getWorld()->readRecord (reader, n.val);
|
||||
break;
|
||||
|
||||
case ESM::REC_GSCR:
|
||||
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
// ignore invalid records
|
||||
|
|
|
@ -40,7 +40,7 @@ add_component_dir (esm
|
|||
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
|
||||
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
|
||||
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
|
||||
savedgame journalentry queststate
|
||||
savedgame journalentry queststate locals globalscript
|
||||
)
|
||||
|
||||
add_component_dir (misc
|
||||
|
|
|
@ -15,27 +15,27 @@ namespace Compiler
|
|||
{
|
||||
case 's': return mShorts;
|
||||
case 'l': return mLongs;
|
||||
case 'f': return mFloats;
|
||||
case 'f': return mFloats;
|
||||
}
|
||||
|
||||
|
||||
throw std::logic_error ("unknown variable type");
|
||||
}
|
||||
|
||||
|
||||
int Locals::searchIndex (char type, const std::string& name) const
|
||||
{
|
||||
const std::vector<std::string>& collection = get (type);
|
||||
|
||||
|
||||
std::vector<std::string>::const_iterator iter =
|
||||
std::find (collection.begin(), collection.end(), name);
|
||||
|
||||
|
||||
if (iter==collection.end())
|
||||
return -1;
|
||||
|
||||
|
||||
return iter-collection.begin();
|
||||
}
|
||||
|
||||
|
||||
bool Locals::search (char type, const std::string& name) const
|
||||
{
|
||||
{
|
||||
return searchIndex (type, name)!=-1;
|
||||
}
|
||||
|
||||
|
@ -45,10 +45,10 @@ namespace Compiler
|
|||
{
|
||||
case 's': return mShorts;
|
||||
case 'l': return mLongs;
|
||||
case 'f': return mFloats;
|
||||
case 'f': return mFloats;
|
||||
}
|
||||
|
||||
throw std::logic_error ("unknown variable type");
|
||||
|
||||
throw std::logic_error ("unknown variable type");
|
||||
}
|
||||
|
||||
char Locals::getType (const std::string& name) const
|
||||
|
@ -58,35 +58,35 @@ namespace Compiler
|
|||
|
||||
if (search ('l', name))
|
||||
return 'l';
|
||||
|
||||
|
||||
if (search ('f', name))
|
||||
return 'f';
|
||||
|
||||
|
||||
return ' ';
|
||||
}
|
||||
|
||||
|
||||
int Locals::getIndex (const std::string& name) const
|
||||
{
|
||||
int index = searchIndex ('s', name);
|
||||
|
||||
|
||||
if (index!=-1)
|
||||
return index;
|
||||
|
||||
|
||||
index = searchIndex ('l', name);
|
||||
|
||||
if (index!=-1)
|
||||
return index;
|
||||
|
||||
return searchIndex ('f', name);
|
||||
return searchIndex ('f', name);
|
||||
}
|
||||
|
||||
|
||||
void Locals::write (std::ostream& localFile) const
|
||||
{
|
||||
localFile
|
||||
<< get ('s').size() << ' '
|
||||
<< get ('l').size() << ' '
|
||||
<< get ('f').size() << std::endl;
|
||||
|
||||
|
||||
std::copy (get ('s').begin(), get ('s').end(),
|
||||
std::ostream_iterator<std::string> (localFile, " "));
|
||||
std::copy (get ('l').begin(), get ('l').end(),
|
||||
|
@ -94,12 +94,12 @@ namespace Compiler
|
|||
std::copy (get ('f').begin(), get ('f').end(),
|
||||
std::ostream_iterator<std::string> (localFile, " "));
|
||||
}
|
||||
|
||||
|
||||
void Locals::declare (char type, const std::string& name)
|
||||
{
|
||||
get (type).push_back (name);
|
||||
}
|
||||
|
||||
|
||||
void Locals::clear()
|
||||
{
|
||||
get ('s').clear();
|
||||
|
|
|
@ -8,35 +8,35 @@
|
|||
namespace Compiler
|
||||
{
|
||||
/// \brief Local variable declarations
|
||||
|
||||
|
||||
class Locals
|
||||
{
|
||||
std::vector<std::string> mShorts;
|
||||
std::vector<std::string> mLongs;
|
||||
std::vector<std::string> mFloats;
|
||||
|
||||
|
||||
int searchIndex (char type, const std::string& name) const;
|
||||
|
||||
bool search (char type, const std::string& name) const;
|
||||
|
||||
std::vector<std::string>& get (char type);
|
||||
|
||||
|
||||
std::vector<std::string>& get (char type);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
char getType (const std::string& name) const;
|
||||
///< 's': short, 'l': long, 'f': float, ' ': does not exist.
|
||||
|
||||
|
||||
int getIndex (const std::string& name) const;
|
||||
///< return index for local variable \a name (-1: does not exist).
|
||||
|
||||
|
||||
const std::vector<std::string>& get (char type) const;
|
||||
|
||||
void write (std::ostream& localFile) const;
|
||||
///< write declarations to file.
|
||||
|
||||
|
||||
void declare (char type, const std::string& name);
|
||||
///< declares a variable.
|
||||
|
||||
|
||||
void clear();
|
||||
///< remove all declarations.
|
||||
};
|
||||
|
|
|
@ -87,6 +87,7 @@ enum RecNameInts
|
|||
REC_SAVE = 0x45564153,
|
||||
REC_JOUR = 0x524f55a4,
|
||||
REC_QUES = 0x53455551,
|
||||
REC_GSCR = 0x52435347,
|
||||
|
||||
// format 1
|
||||
REC_FILT = 0x544C4946
|
||||
|
|
25
components/esm/globalscript.cpp
Normal file
25
components/esm/globalscript.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
#include "globalscript.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
void ESM::GlobalScript::load (ESMReader &esm)
|
||||
{
|
||||
mId = esm.getHNString ("NAME");
|
||||
|
||||
mLocals.load (esm);
|
||||
|
||||
mRunning = 0;
|
||||
esm.getHNOT (mRunning, "RUN_");
|
||||
}
|
||||
|
||||
void ESM::GlobalScript::save (ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNString ("NAME", mId);
|
||||
|
||||
mLocals.save (esm);
|
||||
|
||||
if (mRunning)
|
||||
esm.writeHNT ("RUN_", mRunning);
|
||||
}
|
24
components/esm/globalscript.hpp
Normal file
24
components/esm/globalscript.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef OPENMW_ESM_GLOBALSCRIPT_H
|
||||
#define OPENMW_ESM_GLOBALSCRIPT_H
|
||||
|
||||
#include "locals.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/// \brief Storage structure for global script state (only used in saved games)
|
||||
|
||||
struct GlobalScript
|
||||
{
|
||||
std::string mId;
|
||||
Locals mLocals;
|
||||
int mRunning;
|
||||
|
||||
void load (ESMReader &esm);
|
||||
void save (ESMWriter &esm) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
28
components/esm/locals.cpp
Normal file
28
components/esm/locals.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
#include "locals.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
void ESM::Locals::load (ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub ("LOCA"))
|
||||
{
|
||||
std::string id = esm.getHString();
|
||||
|
||||
Variant value;
|
||||
value.read (esm, Variant::Format_Info);
|
||||
|
||||
mVariables.push_back (std::make_pair (id, value));
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::Locals::save (ESMWriter &esm) const
|
||||
{
|
||||
for (std::vector<std::pair<std::string, Variant> >::const_iterator iter (mVariables.begin());
|
||||
iter!=mVariables.end(); ++iter)
|
||||
{
|
||||
esm.writeHNString ("LOCA", iter->first);
|
||||
iter->second.write (esm, Variant::Format_Info);
|
||||
}
|
||||
}
|
27
components/esm/locals.hpp
Normal file
27
components/esm/locals.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef OPENMW_ESM_LOCALS_H
|
||||
#define OPENMW_ESM_LOCALS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "variant.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/// \brief Storage structure for local variables (only used in saved games)
|
||||
///
|
||||
/// \note This is not a top-level record.
|
||||
|
||||
struct Locals
|
||||
{
|
||||
std::vector<std::pair<std::string, Variant> > mVariables;
|
||||
|
||||
void load (ESMReader &esm);
|
||||
void save (ESMWriter &esm) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -33,7 +33,7 @@ namespace ESM
|
|||
{
|
||||
Format_Global,
|
||||
Format_Gmst,
|
||||
Format_Info
|
||||
Format_Info // also used for local variables in saved game files
|
||||
};
|
||||
|
||||
Variant();
|
||||
|
|
Loading…
Reference in a new issue