mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 05:26:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			284 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			284 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "cellref.hpp"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <limits>
 | |
| 
 | |
| #include <components/debug/debuglog.hpp>
 | |
| 
 | |
| #include "esmreader.hpp"
 | |
| #include "esmwriter.hpp"
 | |
| 
 | |
| namespace
 | |
| {
 | |
|     constexpr int ZeroLock = std::numeric_limits<int>::max();
 | |
| }
 | |
| 
 | |
| namespace ESM
 | |
| {
 | |
|     namespace
 | |
|     {
 | |
|         template <bool load>
 | |
|         void loadIdImpl(ESMReader& esm, bool wideRefNum, CellRef& cellRef)
 | |
|         {
 | |
|             // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that
 | |
|             // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved
 | |
|             // references system. Its only purpose is a performance optimization for "immovable" things. We don't need
 | |
|             // this, and it's problematic anyway, because any item can theoretically be moved by a script.
 | |
|             if (esm.isNextSub("NAM0"))
 | |
|                 esm.skipHSub();
 | |
| 
 | |
|             if constexpr (load)
 | |
|             {
 | |
|                 cellRef.blank();
 | |
|                 cellRef.mRefNum = esm.getFormId(wideRefNum);
 | |
|                 cellRef.mRefID = esm.getHNORefId("NAME");
 | |
| 
 | |
|                 if (cellRef.mRefID.empty())
 | |
|                     Log(Debug::Warning) << "Warning: got CellRef with empty RefId in " << esm.getName() << " 0x"
 | |
|                                         << std::hex << esm.getFileOffset();
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 esm.getFormId(wideRefNum);
 | |
|                 esm.skipHNORefId("NAME");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         template <bool load>
 | |
|         void loadDataImpl(ESMReader& esm, bool& isDeleted, CellRef& cellRef)
 | |
|         {
 | |
|             const auto getRefIdOrSkip = [&](ESM::RefId& refId) {
 | |
|                 if constexpr (load)
 | |
|                     refId = esm.getRefId();
 | |
|                 else
 | |
|                     esm.skipHRefId();
 | |
|             };
 | |
| 
 | |
|             const auto getHStringOrSkip = [&](std::string& value) {
 | |
|                 if constexpr (load)
 | |
|                     value = esm.getHString();
 | |
|                 else
 | |
|                     esm.skipHString();
 | |
|             };
 | |
| 
 | |
|             const auto getHTOrSkip = [&](auto& value) {
 | |
|                 if constexpr (load)
 | |
|                     esm.getHT(value);
 | |
|                 else
 | |
|                     esm.skipHT<std::decay_t<decltype(value)>>();
 | |
|             };
 | |
| 
 | |
|             if constexpr (load)
 | |
|                 isDeleted = false;
 | |
| 
 | |
|             bool isLoaded = false;
 | |
|             while (!isLoaded && esm.hasMoreSubs())
 | |
|             {
 | |
|                 esm.getSubName();
 | |
|                 switch (esm.retSubName().toInt())
 | |
|                 {
 | |
|                     case fourCC("UNAM"):
 | |
|                         getHTOrSkip(cellRef.mReferenceBlocked);
 | |
|                         break;
 | |
|                     case fourCC("XSCL"):
 | |
|                         getHTOrSkip(cellRef.mScale);
 | |
|                         if constexpr (load)
 | |
|                             cellRef.mScale = std::clamp(cellRef.mScale, 0.5f, 2.0f);
 | |
|                         break;
 | |
|                     case fourCC("ANAM"):
 | |
|                         getRefIdOrSkip(cellRef.mOwner);
 | |
|                         break;
 | |
|                     case fourCC("BNAM"):
 | |
|                         getHStringOrSkip(cellRef.mGlobalVariable);
 | |
|                         break;
 | |
|                     case fourCC("XSOL"):
 | |
|                         getRefIdOrSkip(cellRef.mSoul);
 | |
|                         break;
 | |
|                     case fourCC("CNAM"):
 | |
|                         getRefIdOrSkip(cellRef.mFaction);
 | |
|                         break;
 | |
|                     case fourCC("INDX"):
 | |
|                         getHTOrSkip(cellRef.mFactionRank);
 | |
|                         break;
 | |
|                     case fourCC("XCHG"):
 | |
|                         getHTOrSkip(cellRef.mEnchantmentCharge);
 | |
|                         break;
 | |
|                     case fourCC("INTV"):
 | |
|                         getHTOrSkip(cellRef.mChargeInt);
 | |
|                         break;
 | |
|                     case fourCC("NAM9"):
 | |
|                         getHTOrSkip(cellRef.mGoldValue);
 | |
|                         break;
 | |
|                     case fourCC("DODT"):
 | |
|                         getHTOrSkip(cellRef.mDoorDest);
 | |
|                         if constexpr (load)
 | |
|                             cellRef.mTeleport = true;
 | |
|                         break;
 | |
|                     case fourCC("DNAM"):
 | |
|                         getHStringOrSkip(cellRef.mDestCell);
 | |
|                         break;
 | |
|                     case fourCC("FLTV"):
 | |
|                         getHTOrSkip(cellRef.mLockLevel);
 | |
|                         break;
 | |
|                     case fourCC("KNAM"):
 | |
|                         getRefIdOrSkip(cellRef.mKey);
 | |
|                         break;
 | |
|                     case fourCC("TNAM"):
 | |
|                         getRefIdOrSkip(cellRef.mTrap);
 | |
|                         break;
 | |
|                     case fourCC("DATA"):
 | |
|                         if constexpr (load)
 | |
|                             esm.getHTSized<24>(cellRef.mPos);
 | |
|                         else
 | |
|                             esm.skipHTSized<24, decltype(cellRef.mPos)>();
 | |
|                         break;
 | |
|                     case fourCC("NAM0"):
 | |
|                     {
 | |
|                         esm.skipHSub();
 | |
|                         break;
 | |
|                     }
 | |
|                     case SREC_DELE:
 | |
|                         esm.skipHSub();
 | |
|                         if constexpr (load)
 | |
|                             isDeleted = true;
 | |
|                         break;
 | |
|                     default:
 | |
|                         esm.cacheSubName();
 | |
|                         isLoaded = true;
 | |
|                         break;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if constexpr (load)
 | |
|             {
 | |
|                 if (esm.getFormatVersion() == DefaultFormatVersion) // loading a content file
 | |
|                     cellRef.mIsLocked = !cellRef.mKey.empty() || cellRef.mLockLevel > 0;
 | |
|                 else
 | |
|                     cellRef.mIsLocked = cellRef.mLockLevel > 0;
 | |
|                 if (cellRef.mLockLevel == ZeroLock)
 | |
|                     cellRef.mLockLevel = 0;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void CellRef::load(ESMReader& esm, bool& isDeleted, bool wideRefNum)
 | |
|     {
 | |
|         loadId(esm, wideRefNum);
 | |
|         loadData(esm, isDeleted);
 | |
|     }
 | |
| 
 | |
|     void CellRef::loadId(ESMReader& esm, bool wideRefNum)
 | |
|     {
 | |
|         loadIdImpl<true>(esm, wideRefNum, *this);
 | |
|     }
 | |
| 
 | |
|     void CellRef::loadData(ESMReader& esm, bool& isDeleted)
 | |
|     {
 | |
|         loadDataImpl<true>(esm, isDeleted, *this);
 | |
|     }
 | |
| 
 | |
|     void CellRef::save(ESMWriter& esm, bool wideRefNum, bool inInventory, bool isDeleted) const
 | |
|     {
 | |
|         esm.writeFormId(mRefNum, wideRefNum);
 | |
| 
 | |
|         esm.writeHNCRefId("NAME", mRefID);
 | |
| 
 | |
|         if (isDeleted)
 | |
|         {
 | |
|             esm.writeHNString("DELE", "", 3);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (mScale != 1.0)
 | |
|         {
 | |
|             esm.writeHNT("XSCL", std::clamp(mScale, 0.5f, 2.0f));
 | |
|         }
 | |
| 
 | |
|         if (!inInventory)
 | |
|             esm.writeHNOCRefId("ANAM", mOwner);
 | |
| 
 | |
|         esm.writeHNOCString("BNAM", mGlobalVariable);
 | |
|         esm.writeHNOCRefId("XSOL", mSoul);
 | |
| 
 | |
|         if (!inInventory)
 | |
|         {
 | |
|             esm.writeHNOCRefId("CNAM", mFaction);
 | |
|             if (mFactionRank != -2)
 | |
|             {
 | |
|                 esm.writeHNT("INDX", mFactionRank);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (mEnchantmentCharge != -1)
 | |
|             esm.writeHNT("XCHG", mEnchantmentCharge);
 | |
| 
 | |
|         if (mChargeInt != -1)
 | |
|             esm.writeHNT("INTV", mChargeInt);
 | |
| 
 | |
|         if (mGoldValue > 1)
 | |
|             esm.writeHNT("NAM9", mGoldValue);
 | |
| 
 | |
|         if (!inInventory && mTeleport)
 | |
|         {
 | |
|             esm.writeHNT("DODT", mDoorDest);
 | |
|             esm.writeHNOCString("DNAM", mDestCell);
 | |
|         }
 | |
| 
 | |
|         if (!inInventory)
 | |
|         {
 | |
|             int lockLevel = mLockLevel;
 | |
|             if (lockLevel == 0 && mIsLocked)
 | |
|                 lockLevel = ZeroLock;
 | |
|             if (lockLevel != 0)
 | |
|                 esm.writeHNT("FLTV", lockLevel);
 | |
|             esm.writeHNOCRefId("KNAM", mKey);
 | |
|             esm.writeHNOCRefId("TNAM", mTrap);
 | |
|         }
 | |
| 
 | |
|         if (mReferenceBlocked != -1)
 | |
|             esm.writeHNT("UNAM", mReferenceBlocked);
 | |
| 
 | |
|         if (!inInventory)
 | |
|             esm.writeHNT("DATA", mPos, 24);
 | |
|     }
 | |
| 
 | |
|     void CellRef::blank()
 | |
|     {
 | |
|         mRefNum = RefNum{};
 | |
|         mRefID = ESM::RefId();
 | |
|         mScale = 1;
 | |
|         mOwner = ESM::RefId();
 | |
|         mGlobalVariable.clear();
 | |
|         mSoul = ESM::RefId();
 | |
|         mFaction = ESM::RefId();
 | |
|         mFactionRank = -2;
 | |
|         mChargeInt = -1;
 | |
|         mChargeIntRemainder = 0.0f;
 | |
|         mEnchantmentCharge = -1;
 | |
|         mGoldValue = 1;
 | |
|         mDestCell.clear();
 | |
|         mLockLevel = 0;
 | |
|         mIsLocked = false;
 | |
|         mKey = ESM::RefId();
 | |
|         mTrap = ESM::RefId();
 | |
|         mReferenceBlocked = -1;
 | |
|         mTeleport = false;
 | |
| 
 | |
|         for (int i = 0; i < 3; ++i)
 | |
|         {
 | |
|             mDoorDest.pos[i] = 0;
 | |
|             mDoorDest.rot[i] = 0;
 | |
|             mPos.pos[i] = 0;
 | |
|             mPos.rot[i] = 0;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void skipLoadCellRef(ESMReader& esm, bool wideRefNum)
 | |
|     {
 | |
|         CellRef cellRef;
 | |
|         loadIdImpl<false>(esm, wideRefNum, cellRef);
 | |
|         bool isDeleted;
 | |
|         loadDataImpl<false>(esm, isDeleted, cellRef);
 | |
|     }
 | |
| 
 | |
| }
 |