#include "aisequence.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include #include #include namespace ESM { template T> void decompose(T&& v, const auto& f) { f(v.mDistance, v.mDuration, v.mTimeOfDay, v.mIdle, v.mShouldRepeat); } template T> void decompose(T&& v, const auto& f) { std::uint32_t unused = 0; f(v.mRemainingDuration, unused); } template T> void decompose(T&& v, const auto& f) { f(v.mX, v.mY, v.mZ); } template T> void decompose(T&& v, const auto& f) { f(v.mX, v.mY, v.mZ, v.mDuration); } namespace AiSequence { void AiWander::load(ESMReader& esm) { esm.getNamedComposite("DATA", mData); esm.getNamedComposite("STAR", mDurationData); // was mStartTime mStoredInitialActorPosition = esm.getHNOT("POS_", mInitialActorPosition.mValues); } void AiWander::save(ESMWriter& esm) const { esm.writeNamedComposite("DATA", mData); esm.writeNamedComposite("STAR", mDurationData); // was mStartTime if (mStoredInitialActorPosition) esm.writeHNT("POS_", mInitialActorPosition.mValues); } void AiTravel::load(ESMReader& esm) { esm.getNamedComposite("DATA", mData); esm.getHNT(mHidden, "HIDD"); mRepeat = false; esm.getHNOT(mRepeat, "REPT"); } void AiTravel::save(ESMWriter& esm) const { esm.writeNamedComposite("DATA", mData); esm.writeHNT("HIDD", mHidden); if (mRepeat) esm.writeHNT("REPT", mRepeat); } void AiEscort::load(ESMReader& esm) { esm.getNamedComposite("DATA", mData); mTargetId = esm.getHNRefId("TARG"); mTargetActorId = -1; esm.getHNOT(mTargetActorId, "TAID"); esm.getHNT(mRemainingDuration, "DURA"); mCellId = esm.getHNOString("CELL"); mRepeat = false; esm.getHNOT(mRepeat, "REPT"); if (esm.getFormatVersion() <= MaxOldAiPackageFormatVersion) { // mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration. // The exact value of mDuration only matters for repeating packages. // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should // fix old saves. mData.mDuration = std::max(mRemainingDuration > 0, mRemainingDuration); } } void AiEscort::save(ESMWriter& esm) const { esm.writeNamedComposite("DATA", mData); esm.writeHNRefId("TARG", mTargetId); esm.writeHNT("TAID", mTargetActorId); esm.writeHNT("DURA", mRemainingDuration); if (!mCellId.empty()) esm.writeHNString("CELL", mCellId); if (mRepeat) esm.writeHNT("REPT", mRepeat); } void AiFollow::load(ESMReader& esm) { esm.getNamedComposite("DATA", mData); mTargetId = esm.getHNRefId("TARG"); mTargetActorId = -1; esm.getHNOT(mTargetActorId, "TAID"); esm.getHNT(mRemainingDuration, "DURA"); mCellId = esm.getHNOString("CELL"); esm.getHNT(mAlwaysFollow, "ALWY"); mCommanded = false; esm.getHNOT(mCommanded, "CMND"); mActive = false; esm.getHNOT(mActive, "ACTV"); mRepeat = false; esm.getHNOT(mRepeat, "REPT"); if (esm.getFormatVersion() <= MaxOldAiPackageFormatVersion) { // mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration. // The exact value of mDuration only matters for repeating packages. // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should // fix old saves. mData.mDuration = std::max(mRemainingDuration > 0, mRemainingDuration); } } void AiFollow::save(ESMWriter& esm) const { esm.writeNamedComposite("DATA", mData); esm.writeHNRefId("TARG", mTargetId); esm.writeHNT("TAID", mTargetActorId); esm.writeHNT("DURA", mRemainingDuration); if (!mCellId.empty()) esm.writeHNString("CELL", mCellId); esm.writeHNT("ALWY", mAlwaysFollow); esm.writeHNT("CMND", mCommanded); if (mActive) esm.writeHNT("ACTV", mActive); if (mRepeat) esm.writeHNT("REPT", mRepeat); } void AiActivate::load(ESMReader& esm) { mTargetId = esm.getHNRefId("TARG"); mRepeat = false; esm.getHNOT(mRepeat, "REPT"); } void AiActivate::save(ESMWriter& esm) const { esm.writeHNRefId("TARG", mTargetId); if (mRepeat) esm.writeHNT("REPT", mRepeat); } void AiCombat::load(ESMReader& esm) { esm.getHNT(mTargetActorId, "TARG"); } void AiCombat::save(ESMWriter& esm) const { esm.writeHNT("TARG", mTargetActorId); } void AiPursue::load(ESMReader& esm) { esm.getHNT(mTargetActorId, "TARG"); } void AiPursue::save(ESMWriter& esm) const { esm.writeHNT("TARG", mTargetActorId); } void AiSequence::save(ESMWriter& esm) const { for (std::vector::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) { esm.writeHNT("AIPK", it->mType); switch (it->mType) { case Ai_Wander: static_cast(*it->mPackage).save(esm); break; case Ai_Travel: static_cast(*it->mPackage).save(esm); break; case Ai_Escort: static_cast(*it->mPackage).save(esm); break; case Ai_Follow: static_cast(*it->mPackage).save(esm); break; case Ai_Activate: static_cast(*it->mPackage).save(esm); break; case Ai_Combat: static_cast(*it->mPackage).save(esm); break; case Ai_Pursue: static_cast(*it->mPackage).save(esm); break; default: break; } } esm.writeHNT("LAST", mLastAiPackage); } void AiSequence::load(ESMReader& esm) { int count = 0; while (esm.isNextSub("AIPK")) { int32_t type; esm.getHT(type); mPackages.emplace_back(); mPackages.back().mType = type; switch (type) { case Ai_Wander: { std::unique_ptr ptr = std::make_unique(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Travel: { std::unique_ptr ptr = std::make_unique(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Escort: { std::unique_ptr ptr = std::make_unique(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Follow: { std::unique_ptr ptr = std::make_unique(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Activate: { std::unique_ptr ptr = std::make_unique(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Combat: { std::unique_ptr ptr = std::make_unique(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); break; } case Ai_Pursue: { std::unique_ptr ptr = std::make_unique(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); break; } default: return; } } esm.getHNT(mLastAiPackage, "LAST"); if (count > 1 && esm.getFormatVersion() <= MaxOldAiPackageFormatVersion) { for (auto& pkg : mPackages) { if (pkg.mType == Ai_Wander) static_cast(*pkg.mPackage).mData.mShouldRepeat = true; else if (pkg.mType == Ai_Travel) static_cast(*pkg.mPackage).mRepeat = true; else if (pkg.mType == Ai_Escort) static_cast(*pkg.mPackage).mRepeat = true; else if (pkg.mType == Ai_Follow) static_cast(*pkg.mPackage).mRepeat = true; else if (pkg.mType == Ai_Activate) static_cast(*pkg.mPackage).mRepeat = true; } } } } }