#ifndef GAME_MWWORLD_CELLSTORE_H #define GAME_MWWORLD_CELLSTORE_H #include #include #include #include #include #include #include #include namespace MWWorld { /// A reference to one object (of any type) in a cell. /// /// Constructing this with a CellRef instance in the constructor means that /// in practice (where D is RefData) the possibly mutable data is copied /// across to mData. If later adding data (such as position) to CellRef /// this would have to be manually copied across. template struct LiveCellRef { LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) : base(b), ref(cref), mData(ref) {} LiveCellRef(const X* b = NULL) : base(b), mData(ref) {} // The object that this instance is based on. const X* base; /* Information about this instance, such as 3D location and rotation and individual type-dependent data. */ ESM::CellRef ref; /// runtime-data D mData; }; /// A list of cell references template struct CellRefList { typedef LiveCellRef LiveRef; typedef std::list List; List list; // Search for the given reference in the given reclist from // ESMStore. Insert the reference into the list if a match is // found. If not, throw an exception. template void find(ESM::CellRef &ref, const Y& recList) { const X* obj = recList.find(ref.refID); if(obj == NULL) throw std::runtime_error("Error resolving cell reference " + ref.refID); list.push_back(LiveRef(ref, obj)); } LiveRef *find (const std::string& name) { for (typename std::list::iterator iter (list.begin()); iter!=list.end(); ++iter) { if (iter->ref.refID==name) return &*iter; } return 0; } }; /// A storage struct for one single cell reference. template class CellStore { public: enum State { State_Unloaded, State_Preloaded, State_Loaded }; CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) { mWaterLevel = cell->water; } const ESM::Cell *cell; State mState; std::vector mIds; float mWaterLevel; // Lists for each individual object type CellRefList activators; CellRefList potions; CellRefList appas; CellRefList armors; CellRefList books; CellRefList clothes; CellRefList containers; CellRefList creatures; CellRefList doors; CellRefList ingreds; CellRefList creatureLists; CellRefList itemLists; CellRefList lights; CellRefList lockpicks; CellRefList miscItems; CellRefList npcs; CellRefList probes; CellRefList repairs; CellRefList statics; CellRefList weapons; void load (const ESMS::ESMStore &store, ESM::ESMReader &esm) { if (mState!=State_Loaded) { if (mState==State_Preloaded) mIds.clear(); std::cout << "loading cell " << cell->getDescription() << std::endl; loadRefs (store, esm); mState = State_Loaded; } } void preload (const ESMS::ESMStore &store, ESM::ESMReader &esm) { if (mState==State_Unloaded) { listRefs (store, esm); mState = State_Preloaded; } } /// Call functor (ref) for each reference. functor must return a bool. Returning /// false will abort the iteration. /// \return Iteration completed? template bool forEach (Functor& functor) { return forEachImp (functor, activators) && forEachImp (functor, potions) && forEachImp (functor, appas) && forEachImp (functor, armors) && forEachImp (functor, books) && forEachImp (functor, clothes) && forEachImp (functor, containers) && forEachImp (functor, creatures) && forEachImp (functor, doors) && forEachImp (functor, ingreds) && forEachImp (functor, creatureLists) && forEachImp (functor, itemLists) && forEachImp (functor, lights) && forEachImp (functor, lockpicks) && forEachImp (functor, miscItems) && forEachImp (functor, npcs) && forEachImp (functor, probes) && forEachImp (functor, repairs) && forEachImp (functor, statics) && forEachImp (functor, weapons); } private: template bool forEachImp (Functor& functor, List& list) { for (typename List::List::iterator iter (list.list.begin()); iter!=list.list.end(); ++iter) if (!functor (iter->ref, iter->mData)) return false; return true; } /// Run through references and store IDs void listRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm) { assert (cell); if (cell->context.filename.empty()) return; // this is a dynamically generated cell -> skipping. // Reopen the ESM reader and seek to the right position. cell->restore (esm); ESM::CellRef ref; // Get each reference in turn while (cell->getNextRef (esm, ref)) { std::string lowerCase; std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase), (int(*)(int)) std::tolower); mIds.push_back (lowerCase); } std::sort (mIds.begin(), mIds.end()); } void loadRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm) { assert (cell); if (cell->context.filename.empty()) return; // this is a dynamically generated cell -> skipping. // Reopen the ESM reader and seek to the right position. cell->restore(esm); ESM::CellRef ref; // Get each reference in turn while(cell->getNextRef(esm, ref)) { std::string lowerCase; std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase), (int(*)(int)) std::tolower); int rec = store.find(ref.refID); ref.refID = 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: activators.find(ref, store.activators); break; case ESM::REC_ALCH: potions.find(ref, store.potions); break; case ESM::REC_APPA: appas.find(ref, store.appas); break; case ESM::REC_ARMO: armors.find(ref, store.armors); break; case ESM::REC_BOOK: books.find(ref, store.books); break; case ESM::REC_CLOT: clothes.find(ref, store.clothes); break; case ESM::REC_CONT: containers.find(ref, store.containers); break; case ESM::REC_CREA: creatures.find(ref, store.creatures); break; case ESM::REC_DOOR: doors.find(ref, store.doors); break; case ESM::REC_INGR: ingreds.find(ref, store.ingreds); break; case ESM::REC_LEVC: creatureLists.find(ref, store.creatureLists); break; case ESM::REC_LEVI: itemLists.find(ref, store.itemLists); break; case ESM::REC_LIGH: lights.find(ref, store.lights); break; case ESM::REC_LOCK: lockpicks.find(ref, store.lockpicks); break; case ESM::REC_MISC: miscItems.find(ref, store.miscItems); break; case ESM::REC_NPC_: npcs.find(ref, store.npcs); break; case ESM::REC_PROB: probes.find(ref, store.probes); break; case ESM::REC_REPA: repairs.find(ref, store.repairs); break; case ESM::REC_STAT: statics.find(ref, store.statics); break; case ESM::REC_WEAP: weapons.find(ref, store.weapons); break; case 0: std::cout << "Cell reference " + ref.refID + " not found!\n"; break; default: std::cout << "WARNING: Ignoring reference '" << ref.refID << "' of unhandled type\n"; } } } }; } #endif