forked from teamnwah/openmw-tes3coop
- Add support for multiple plugins trying to modify the same reference
- Fix a small signed/unsigned warning
This commit is contained in:
parent
d6377fb2e3
commit
a8e02779b2
6 changed files with 203 additions and 27 deletions
|
@ -96,6 +96,11 @@ namespace MWWorld
|
|||
// Get each reference in turn
|
||||
while(mCell->getNextRef(esm[index], ref))
|
||||
{
|
||||
// Don't load reference if it was moved to a different cell.
|
||||
if (mCell->mMovedRefs.find(ref.mRefnum) != mCell->mMovedRefs.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string lowerCase;
|
||||
|
||||
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||
|
@ -139,5 +144,56 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load moved references, from separately tracked list.
|
||||
for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); it++)
|
||||
{
|
||||
// Doesn't seem to work in one line... huh? Too sleepy to check...
|
||||
//const ESM::CellRef &ref0 = it->second;
|
||||
ESM::CellRef &ref = const_cast<ESM::CellRef&>(it->second);
|
||||
|
||||
std::string lowerCase;
|
||||
|
||||
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||
(int(*)(int)) std::tolower);
|
||||
|
||||
int rec = store.find(ref.mRefID);
|
||||
|
||||
ref.mRefID = lowerCase;
|
||||
|
||||
/* We can optimize this further by storing the pointer to the
|
||||
record itself in store.all, so that we don't need to look it
|
||||
up again here. However, never optimize. There are infinite
|
||||
opportunities to do that later.
|
||||
*/
|
||||
switch(rec)
|
||||
{
|
||||
case ESM::REC_ACTI: mActivators.load(ref, store); break;
|
||||
case ESM::REC_ALCH: mPotions.load(ref, store); break;
|
||||
case ESM::REC_APPA: mAppas.load(ref, store); break;
|
||||
case ESM::REC_ARMO: mArmors.load(ref, store); break;
|
||||
case ESM::REC_BOOK: mBooks.load(ref, store); break;
|
||||
case ESM::REC_CLOT: mClothes.load(ref, store); break;
|
||||
case ESM::REC_CONT: mContainers.load(ref, store); break;
|
||||
case ESM::REC_CREA: mCreatures.load(ref, store); break;
|
||||
case ESM::REC_DOOR: mDoors.load(ref, store); break;
|
||||
case ESM::REC_INGR: mIngreds.load(ref, store); break;
|
||||
case ESM::REC_LEVC: mCreatureLists.load(ref, store); break;
|
||||
case ESM::REC_LEVI: mItemLists.load(ref, store); break;
|
||||
case ESM::REC_LIGH: mLights.load(ref, store); break;
|
||||
case ESM::REC_LOCK: mLockpicks.load(ref, store); break;
|
||||
case ESM::REC_MISC: mMiscItems.load(ref, store); break;
|
||||
case ESM::REC_NPC_: mNpcs.load(ref, store); break;
|
||||
case ESM::REC_PROB: mProbes.load(ref, store); break;
|
||||
case ESM::REC_REPA: mRepairs.load(ref, store); break;
|
||||
case ESM::REC_STAT: mStatics.load(ref, store); break;
|
||||
case ESM::REC_WEAP: mWeapons.load(ref, store); break;
|
||||
|
||||
case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break;
|
||||
default:
|
||||
std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,13 +30,13 @@ void ESMStore::load(ESM::ESMReader &esm)
|
|||
// Cache parent esX files by tracking their indices in the global list of
|
||||
// all files/readers used by the engine. This will greaty help to accelerate
|
||||
// parsing of reference IDs.
|
||||
size_t index = ~0;
|
||||
int index = ~0;
|
||||
const ESM::ESMReader::MasterList &masters = esm.getMasters();
|
||||
std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList();
|
||||
for (size_t j = 0; j < masters.size(); j++) {
|
||||
ESM::MasterData &mast = const_cast<ESM::MasterData&>(masters[j]);
|
||||
std::string fname = mast.name;
|
||||
for (size_t i = 0; i < esm.getIndex(); i++) {
|
||||
for (int i = 0; i < esm.getIndex(); i++) {
|
||||
const std::string &candidate = allPlugins->at(i).getContext().filename;
|
||||
std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();
|
||||
if (fname == fnamecandidate) {
|
||||
|
@ -44,7 +44,7 @@ void ESMStore::load(ESM::ESMReader &esm)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (index == (size_t)~0) {
|
||||
if (index == (int)~0) {
|
||||
// Tried to load a parent file that has not been loaded yet. This is bad,
|
||||
// the launcher should have taken care of this.
|
||||
std::string fstring = "File " + fname + " asks for parent file " + masters[j].name
|
||||
|
|
|
@ -94,6 +94,9 @@ namespace MWWorld
|
|||
ESMStore()
|
||||
: mDynamicCount(0)
|
||||
{
|
||||
// Cell store needs access to this for tracking moved references
|
||||
mCells.mEsmStore = this;
|
||||
|
||||
mStores[ESM::REC_ACTI] = &mActivators;
|
||||
mStores[ESM::REC_ALCH] = &mPotions;
|
||||
mStores[ESM::REC_APPA] = &mAppas;
|
||||
|
|
|
@ -399,8 +399,7 @@ namespace MWWorld
|
|||
|
||||
DynamicInt mDynamicInt;
|
||||
DynamicExt mDynamicExt;
|
||||
|
||||
|
||||
|
||||
const ESM::Cell *search(const ESM::Cell &cell) const {
|
||||
if (cell.isExterior()) {
|
||||
return search(cell.getGridX(), cell.getGridY());
|
||||
|
@ -409,6 +408,8 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
public:
|
||||
ESMStore *mEsmStore;
|
||||
|
||||
typedef SharedIterator<ESM::Cell> iterator;
|
||||
|
||||
Store<ESM::Cell>()
|
||||
|
@ -450,6 +451,30 @@ namespace MWWorld
|
|||
return 0;
|
||||
}
|
||||
|
||||
const ESM::Cell *searchOrCreate(int x, int y) {
|
||||
ESM::Cell cell;
|
||||
cell.mData.mX = x, cell.mData.mY = y;
|
||||
|
||||
std::pair<int, int> key(x, y);
|
||||
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key);
|
||||
if (it != mExt.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
|
||||
DynamicExt::const_iterator dit = mDynamicExt.find(key);
|
||||
if (dit != mDynamicExt.end()) {
|
||||
return &dit->second;
|
||||
}
|
||||
|
||||
ESM::Cell *newCell = new ESM::Cell;
|
||||
newCell->mData.mX = x;
|
||||
newCell->mData.mY = y;
|
||||
mExt[std::make_pair(x, y)] = *newCell;
|
||||
delete newCell;
|
||||
|
||||
return &mExt[std::make_pair(x, y)];
|
||||
}
|
||||
|
||||
const ESM::Cell *find(const std::string &id) const {
|
||||
const ESM::Cell *ptr = search(id);
|
||||
if (ptr == 0) {
|
||||
|
@ -500,7 +525,7 @@ namespace MWWorld
|
|||
cell->mName = id;
|
||||
|
||||
// The cell itself takes care of all the hairy details
|
||||
cell->load(esm);
|
||||
cell->load(esm, *mEsmStore);
|
||||
|
||||
if(cell->mData.mFlags & ESM::Cell::Interior)
|
||||
{
|
||||
|
@ -515,7 +540,6 @@ namespace MWWorld
|
|||
*oldcell = *cell;
|
||||
} else
|
||||
mInt[idLower] = *cell;
|
||||
delete cell;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -526,12 +550,23 @@ namespace MWWorld
|
|||
oldcell->mContextList.push_back(cell->mContextList.at(0));
|
||||
// copy list into new cell
|
||||
cell->mContextList = oldcell->mContextList;
|
||||
// merge lists of leased references, use newer data in case of conflict
|
||||
for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); it++) {
|
||||
// remove reference from current leased ref tracker and add it to new cell
|
||||
if (oldcell->mMovedRefs.find(it->second.mRefnum) != oldcell->mMovedRefs.end()) {
|
||||
ESM::MovedCellRef target0 = oldcell->mMovedRefs[it->second.mRefnum];
|
||||
ESM::Cell *wipecell = const_cast<ESM::Cell*>(search(target0.mTarget[0], target0.mTarget[1]));
|
||||
wipecell->mLeasedRefs.erase(it->second.mRefnum);
|
||||
}
|
||||
oldcell->mMovedRefs[it->second.mRefnum] = it->second;
|
||||
}
|
||||
cell->mMovedRefs = oldcell->mMovedRefs;
|
||||
// have new cell replace old cell
|
||||
*oldcell = *cell;
|
||||
} else
|
||||
mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell;
|
||||
delete cell;
|
||||
}
|
||||
delete cell;
|
||||
}
|
||||
|
||||
iterator intBegin() const {
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <c++/4.6/list>
|
||||
#include <list>
|
||||
#include <boost/concept_check.hpp>
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
#include <apps/openmw/mwworld/store.hpp>
|
||||
#include <apps/openmw/mwworld/cellstore.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
|
@ -64,10 +68,11 @@ void CellRef::save(ESMWriter &esm)
|
|||
}
|
||||
}
|
||||
|
||||
void Cell::load(ESMReader &esm)
|
||||
void Cell::load(ESMReader &esm, MWWorld::ESMStore &store)
|
||||
{
|
||||
// Ignore this for now, it might mean we should delete the entire
|
||||
// cell?
|
||||
// TODO: treat the special case "another plugin moved this ref, but we want to delete it"!
|
||||
if (esm.isNextSub("DELE")) {
|
||||
esm.skipHSub();
|
||||
}
|
||||
|
@ -110,6 +115,33 @@ void Cell::load(ESMReader &esm)
|
|||
if (esm.isNextSub("NAM0")) {
|
||||
esm.getHT(mNAM0);
|
||||
}
|
||||
|
||||
// preload moved references
|
||||
while (esm.isNextSub("MVRF")) {
|
||||
CellRef ref;
|
||||
MovedCellRef cMRef;
|
||||
getNextMVRF(esm, cMRef);
|
||||
|
||||
MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(store.get<ESM::Cell>());
|
||||
ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1]));
|
||||
|
||||
// Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following
|
||||
// implementation when the oher implementation works as well.
|
||||
getNextRef(esm, ref);
|
||||
std::string lowerCase;
|
||||
|
||||
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||
(int(*)(int)) std::tolower);
|
||||
|
||||
// Add data required to make reference appear in the correct cell.
|
||||
/*
|
||||
std::cout << "Moving refnumber! First cell: " << mData.mX << " " << mData.mY << std::endl;
|
||||
std::cout << " New cell: " << cMRef.mTarget[0] << " " << cMRef.mTarget[0] << std::endl;
|
||||
std::cout << "Refnumber (MVRF): " << cMRef.mRefnum << " (FRMR) " << ref.mRefnum << std::endl;
|
||||
*/
|
||||
mMovedRefs[cMRef.mRefnum] = cMRef;
|
||||
cellAlt->mLeasedRefs[ref.mRefnum] = ref;
|
||||
}
|
||||
|
||||
// Save position of the cell references and move on
|
||||
mContextList.push_back(esm.getContext());
|
||||
|
@ -171,23 +203,15 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
|||
// TODO: Try and document reference numbering, I don't think this has been done anywhere else.
|
||||
if (!esm.hasMoreSubs())
|
||||
return false;
|
||||
|
||||
|
||||
// NOTE: We should not need this check. It is a safety check until we have checked
|
||||
// more plugins, and how they treat these moved references.
|
||||
if (esm.isNextSub("MVRF")) {
|
||||
// Moved existing reference across cell boundaries, so interpret the blocks correctly.
|
||||
// FIXME: Right now, we don't do anything with this data. This might result in weird behaviour,
|
||||
// where a moved reference does not appear because the owning cell (i.e. this cell) is not
|
||||
// loaded in memory.
|
||||
int movedRefnum = 0;
|
||||
int destCell[2];
|
||||
esm.getHT(movedRefnum);
|
||||
esm.getHNT(destCell, "CNDT");
|
||||
// TODO: Figure out what happens when a reference has moved into an interior cell. This might
|
||||
// be required for NPCs following the player.
|
||||
esm.skipRecord(); // skip MVRF
|
||||
esm.skipRecord(); // skip CNDT
|
||||
// That should be it, I haven't seen any other fields yet.
|
||||
}
|
||||
// If we have just parsed a MVRF entry, there should be a regular FRMR entry following right there.
|
||||
// With the exception that this bock technically belongs to a different cell than this one.
|
||||
// TODO: Figure out a way to handle these weird references that do not belong to this cell.
|
||||
// This may require some not-so-small behing-the-scenes updates.
|
||||
|
||||
esm.getHNT(ref.mRefnum, "FRMR");
|
||||
ref.mRefID = esm.getHNString("NAME");
|
||||
|
||||
|
@ -293,4 +317,20 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
|
||||
{
|
||||
esm.getHT(mref.mRefnum);
|
||||
esm.getHNOT(mref.mTarget, "CNDT");
|
||||
|
||||
// Identify references belonging to a parent file and adapt the ID accordingly.
|
||||
int local = (mref.mRefnum & 0xff000000) >> 24;
|
||||
size_t global = esm.getIndex() + 1;
|
||||
mref.mRefnum &= 0x00ffffff; // delete old plugin ID
|
||||
const ESM::ESMReader::MasterList &masters = esm.getMasters();
|
||||
global = masters[local-1].index + 1;
|
||||
mref.mRefnum |= global << 24; // insert global plugin ID
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,14 @@
|
|||
|
||||
#include "esmcommon.hpp"
|
||||
#include "defs.hpp"
|
||||
#include <apps/openmw/mwbase/world.hpp>
|
||||
|
||||
/*
|
||||
namespace MWWorld {
|
||||
class ESMStore;
|
||||
class CellStore;
|
||||
}
|
||||
*/
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -86,6 +94,26 @@ public:
|
|||
void save(ESMWriter &esm);
|
||||
};
|
||||
|
||||
/* Moved cell reference tracking object. This mainly stores the target cell
|
||||
of the reference, so we can easily know where it has been moved when another
|
||||
plugin tries to move it independently.
|
||||
*/
|
||||
class MovedCellRef
|
||||
{
|
||||
public:
|
||||
int mRefnum;
|
||||
|
||||
// Target cell (if exterior)
|
||||
int mTarget[2];
|
||||
|
||||
// TODO: Support moving references between exterior and interior cells!
|
||||
// This may happen in saves, when an NPC follows the player. Tribunal
|
||||
// introduces a henchman (which no one uses), so we may need this as well.
|
||||
};
|
||||
|
||||
typedef std::map<int, MovedCellRef> MovedCellRefTracker;
|
||||
typedef std::map<int, CellRef> CellRefTracker;
|
||||
|
||||
/* Cells hold data about objects, creatures, statics (rocks, walls,
|
||||
buildings) and landscape (for exterior cells). Cells frequently
|
||||
also has other associated LAND and PGRD records. Combined, all this
|
||||
|
@ -131,8 +159,17 @@ struct Cell
|
|||
bool mWaterInt;
|
||||
int mMapColor;
|
||||
int mNAM0;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
|
||||
// References "leased" from another cell (i.e. a different cell
|
||||
// introduced this ref, and it has been moved here by a plugin)
|
||||
CellRefTracker mLeasedRefs;
|
||||
MovedCellRefTracker mMovedRefs;
|
||||
|
||||
void load(ESMReader &esm, MWWorld::ESMStore &store);
|
||||
|
||||
// This method is left in for compatibility with esmtool. Parsing moved references currently requires
|
||||
// passing ESMStore, bit it does not know about this parameter, so we do it this way.
|
||||
void load(ESMReader &esm) {};
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
bool isExterior() const
|
||||
|
@ -167,6 +204,11 @@ struct Cell
|
|||
reuse one memory location without blanking it between calls.
|
||||
*/
|
||||
static bool getNextRef(ESMReader &esm, CellRef &ref);
|
||||
|
||||
/* This fetches an MVRF record, which is used to track moved references.
|
||||
* Since they are comparably rare, we use a separate method for this.
|
||||
*/
|
||||
static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue