MWWorld::Ptr CellStore::moveTo(const Ptr &object, CellStore *cellToMoveTo) { if (cellToMoveTo == this) throw std::runtime_error("moveTo: object is already in this cell"); // We assume that *this is in State_Loaded since we could hardly have reference to a live object otherwise. if (mState != State_Loaded) throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); // Ensure that the object actually exists in the cell SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); forEach(searchVisitor); if (!searchVisitor.mFound) throw std::runtime_error("moveTo: object is not in this cell"); // Objects with no refnum can't be handled correctly in the merging process that happens // on a save/load, so do a simple copy & delete for these objects. if (!object.getCellRef().getRefNum().hasContentFile()) { MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo, object.getRefData().getCount()); object.getRefData().setCount(0); object.getRefData().setBaseNode(NULL); return copied; } MovedRefTracker::iterator found = mMovedHere.find(object.getBase()); if (found != mMovedHere.end()) { // Special case - object didn't originate in this cell // Move it back to its original cell first CellStore* originalCell = found->second; assert (originalCell != this); originalCell->moveFrom(object, this); mMovedHere.erase(found); // Now that object is back to its rightful owner, we can move it if (cellToMoveTo != originalCell) { originalCell->moveTo(object, cellToMoveTo); } updateMergedRefs(); return MWWorld::Ptr(object.getBase(), cellToMoveTo); } cellToMoveTo->moveFrom(object, this); mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo)); updateMergedRefs(); return MWWorld::Ptr(object.getBase(), cellToMoveTo); }
void CellStore::loadRefs() { std::vector<ESM::ESMReader>& esm = mReader; assert (mCell); if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. std::map<ESM::RefNum, std::string> refNumToID; // used to detect refID modifications // Load references from all plugins that do something with this cell. for (size_t i = 0; i < mCell->mContextList.size(); i++) { try { // Reopen the ESM reader and seek to the right position. int index = mCell->mContextList.at(i).index; mCell->restore (esm[index], i); ESM::CellRef ref; ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; // Get each reference in turn bool deleted = false; while(mCell->getNextRef(esm[index], ref, deleted)) { // Don't load reference if it was moved to a different cell. ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); if (iter != mCell->mMovedRefs.end()) { continue; } loadRef (ref, deleted, refNumToID); } } catch (std::exception& e) { std::cerr << "An error occurred loading references for cell " << getCell()->getDescription() << ": " << e.what() << std::endl; } } // Load moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { ESM::CellRef &ref = const_cast<ESM::CellRef&>(it->first); bool deleted = it->second; loadRef (ref, deleted, refNumToID); } updateMergedRefs(); }
void CellStore::loadRefs() { std::vector<ESM::ESMReader>& esm = mReader; assert (mCell); if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. // Load references from all plugins that do something with this cell. for (size_t i = 0; i < mCell->mContextList.size(); i++) { // Reopen the ESM reader and seek to the right position. int index = mCell->mContextList.at(i).index; mCell->restore (esm[index], i); ESM::CellRef ref; ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; // Get each reference in turn bool deleted = false; while(mCell->getNextRef(esm[index], ref, deleted)) { // Don't load reference if it was moved to a different cell. ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); if (iter != mCell->mMovedRefs.end()) { continue; } loadRef (ref, deleted); } } // Load moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { ESM::CellRef &ref = const_cast<ESM::CellRef&>(*it); loadRef (ref, false); } updateMergedRefs(); }
void CellStore::moveFrom(const Ptr &object, CellStore *from) { if (mState != State_Loaded) load(); mHasState = true; MovedRefTracker::iterator found = mMovedToAnotherCell.find(object.getBase()); if (found != mMovedToAnotherCell.end()) { // A cell we had previously moved an object to is returning it to us. assert (found->second == from); mMovedToAnotherCell.erase(found); } else { mMovedHere.insert(std::make_pair(object.getBase(), from)); } updateMergedRefs(); }
void CellStore::readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback) { mHasState = true; while (reader.isNextSub ("OBJE")) { unsigned int unused; reader.getHT (unused); // load the RefID first so we know what type of object it is ESM::CellRef cref; cref.loadId(reader, true); int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); if (type == 0) { std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; reader.skipHSubUntil("OBJE"); continue; } switch (type) { case ESM::REC_ACTI: readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap); break; case ESM::REC_ALCH: readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap); break; case ESM::REC_APPA: readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap); break; case ESM::REC_ARMO: readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap); break; case ESM::REC_BOOK: readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap); break; case ESM::REC_CLOT: readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap); break; case ESM::REC_CONT: readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap); break; case ESM::REC_CREA: readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap); break; case ESM::REC_DOOR: readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap); break; case ESM::REC_INGR: readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap); break; case ESM::REC_LEVC: readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap); break; case ESM::REC_LEVI: readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap); break; case ESM::REC_LIGH: readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap); break; case ESM::REC_LOCK: readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap); break; case ESM::REC_MISC: readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap); break; case ESM::REC_NPC_: readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap); break; case ESM::REC_PROB: readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap); break; case ESM::REC_REPA: readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap); break; case ESM::REC_STAT: readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap); break; case ESM::REC_WEAP: readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap); break; case ESM::REC_BODY: readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap); break; default: throw std::runtime_error ("unknown type in cell reference section"); } } // Do another update here to make sure objects referred to by MVRF tags can be found // This update is only needed for old saves that used the old copy&delete way of moving objects updateMergedRefs(); while (reader.isNextSub("MVRF")) { reader.cacheSubName(); ESM::RefNum refnum; ESM::CellId movedTo; refnum.load(reader, true, "MVRF"); movedTo.load(reader); // Search for the reference. It might no longer exist if its content file was removed. SearchByRefNumVisitor visitor(refnum); forEachInternal(visitor); if (!visitor.mFound) { std::cerr << "Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)" << std::endl; continue; } MWWorld::LiveCellRefBase* movedRef = visitor.mFound; CellStore* otherCell = callback->getCellStore(movedTo); if (otherCell == NULL) { std::cerr << "Dropping moved ref tag for " << movedRef->mRef.getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location." << std::endl; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: movedRef->mData.setPosition(movedRef->mRef.getPosition()); continue; } if (otherCell == this) { // Should never happen unless someone's tampering with files. std::cerr << "Found invalid moved ref, ignoring" << std::endl; continue; } moveTo(MWWorld::Ptr(movedRef, this), otherCell); } }