mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-03 23:26:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef _GAME_CELL_STORE_H
 | 
						|
#define _GAME_CELL_STORE_H
 | 
						|
 | 
						|
/*
 | 
						|
  Cell storage.
 | 
						|
 | 
						|
  Used to load, look up and store all references in a single cell.
 | 
						|
 | 
						|
  Depends on esm/loadcell.hpp (loading from ESM) and esm_store.hpp
 | 
						|
  (looking up references.) Neither of these modules depend on us.
 | 
						|
 */
 | 
						|
 | 
						|
#include "store.hpp"
 | 
						|
#include "components/esm/records.hpp"
 | 
						|
 | 
						|
#include <list>
 | 
						|
#include <string>
 | 
						|
#include <vector>
 | 
						|
#include <iostream>
 | 
						|
#include <stdexcept>
 | 
						|
#include <algorithm>
 | 
						|
 | 
						|
namespace ESMS
 | 
						|
{
 | 
						|
  using namespace ESM;
 | 
						|
 | 
						|
  /// 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 <typename X, typename D>
 | 
						|
  struct LiveCellRef
 | 
						|
  {
 | 
						|
    LiveCellRef(const 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.
 | 
						|
    */
 | 
						|
    CellRef ref;
 | 
						|
 | 
						|
    /// runtime-data
 | 
						|
    D mData;
 | 
						|
  };
 | 
						|
 | 
						|
  /// A list of cell references
 | 
						|
  template <typename X, typename D>
 | 
						|
  struct CellRefList
 | 
						|
  {
 | 
						|
    typedef LiveCellRef<X, D> LiveRef;
 | 
						|
    typedef std::list<LiveRef> 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 <typename Y>
 | 
						|
    void find(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<LiveRef>::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 <typename D>
 | 
						|
  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<std::string> mIds;
 | 
						|
 | 
						|
    float mWaterLevel;
 | 
						|
 | 
						|
    // Lists for each individual object type
 | 
						|
    CellRefList<Activator, D>         activators;
 | 
						|
    CellRefList<Potion, D>            potions;
 | 
						|
    CellRefList<Apparatus, D>         appas;
 | 
						|
    CellRefList<Armor, D>             armors;
 | 
						|
    CellRefList<Book, D>              books;
 | 
						|
    CellRefList<Clothing, D>          clothes;
 | 
						|
    CellRefList<Container, D>         containers;
 | 
						|
    CellRefList<Creature, D>          creatures;
 | 
						|
    CellRefList<Door, D>              doors;
 | 
						|
    CellRefList<Ingredient, D>        ingreds;
 | 
						|
    CellRefList<CreatureLevList, D>   creatureLists;
 | 
						|
    CellRefList<ItemLevList, D>       itemLists;
 | 
						|
    CellRefList<ESM::Light, D>        lights;
 | 
						|
    CellRefList<Tool, D>              lockpicks;
 | 
						|
    CellRefList<Miscellaneous, D>              miscItems;
 | 
						|
    CellRefList<NPC, D>               npcs;
 | 
						|
    CellRefList<Probe, D>             probes;
 | 
						|
    CellRefList<Repair, D>            repairs;
 | 
						|
    CellRefList<Static, D>            statics;
 | 
						|
    CellRefList<Weapon, D>            weapons;
 | 
						|
 | 
						|
    void load (const ESMStore &store, 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 ESMStore &store, 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<class Functor>
 | 
						|
    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<class Functor, class List>
 | 
						|
    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 ESMStore &store, 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);
 | 
						|
 | 
						|
        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 ESMStore &store, 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);
 | 
						|
 | 
						|
      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 REC_ACTI: activators.find(ref, store.activators); break;
 | 
						|
            case REC_ALCH: potions.find(ref, store.potions); break;
 | 
						|
            case REC_APPA: appas.find(ref, store.appas); break;
 | 
						|
            case REC_ARMO: armors.find(ref, store.armors); break;
 | 
						|
            case REC_BOOK: books.find(ref, store.books); break;
 | 
						|
            case REC_CLOT: clothes.find(ref, store.clothes); break;
 | 
						|
            case REC_CONT: containers.find(ref, store.containers); break;
 | 
						|
            case REC_CREA: creatures.find(ref, store.creatures); break;
 | 
						|
            case REC_DOOR: doors.find(ref, store.doors); break;
 | 
						|
            case REC_INGR: ingreds.find(ref, store.ingreds); break;
 | 
						|
            case REC_LEVC: creatureLists.find(ref, store.creatureLists); break;
 | 
						|
            case REC_LEVI: itemLists.find(ref, store.itemLists); break;
 | 
						|
            case REC_LIGH: lights.find(ref, store.lights); break;
 | 
						|
            case REC_LOCK: lockpicks.find(ref, store.lockpicks); break;
 | 
						|
            case REC_MISC: miscItems.find(ref, store.miscItems); break;
 | 
						|
            case REC_NPC_: npcs.find(ref, store.npcs); break;
 | 
						|
            case REC_PROB: probes.find(ref, store.probes); break;
 | 
						|
            case REC_REPA: repairs.find(ref, store.repairs); break;
 | 
						|
            case REC_STAT: statics.find(ref, store.statics); break;
 | 
						|
            case 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
 |