#include "aisequence.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include <algorithm> #include <memory> namespace ESM { namespace AiSequence { void AiWander::load(ESMReader& esm) { esm.getHNTSized<14>(mData, "DATA"); esm.getHNTSized<8>(mDurationData, "STAR"); // was mStartTime mStoredInitialActorPosition = false; if (esm.isNextSub("POS_")) { mStoredInitialActorPosition = true; esm.getHTSized<12>(mInitialActorPosition); } } void AiWander::save(ESMWriter& esm) const { esm.writeHNT("DATA", mData); esm.writeHNT("STAR", mDurationData); if (mStoredInitialActorPosition) esm.writeHNT("POS_", mInitialActorPosition); } void AiTravel::load(ESMReader& esm) { esm.getHNTSized<12>(mData, "DATA"); esm.getHNOT(mHidden, "HIDD"); mRepeat = false; esm.getHNOT(mRepeat, "REPT"); } void AiTravel::save(ESMWriter& esm) const { esm.writeHNT("DATA", mData); esm.writeHNT("HIDD", mHidden); if (mRepeat) esm.writeHNT("REPT", mRepeat); } void AiEscort::load(ESMReader& esm) { esm.getHNTSized<14>(mData, "DATA"); 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<float>(mRemainingDuration > 0, mRemainingDuration); } } void AiEscort::save(ESMWriter& esm) const { esm.writeHNT("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.getHNTSized<14>(mData, "DATA"); 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<float>(mRemainingDuration > 0, mRemainingDuration); } } void AiFollow::save(ESMWriter& esm) const { esm.writeHNT("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<AiPackageContainer>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) { esm.writeHNT("AIPK", it->mType); switch (it->mType) { case Ai_Wander: static_cast<const AiWander&>(*it->mPackage).save(esm); break; case Ai_Travel: static_cast<const AiTravel&>(*it->mPackage).save(esm); break; case Ai_Escort: static_cast<const AiEscort&>(*it->mPackage).save(esm); break; case Ai_Follow: static_cast<const AiFollow&>(*it->mPackage).save(esm); break; case Ai_Activate: static_cast<const AiActivate&>(*it->mPackage).save(esm); break; case Ai_Combat: static_cast<const AiCombat&>(*it->mPackage).save(esm); break; case Ai_Pursue: static_cast<const AiPursue&>(*it->mPackage).save(esm); break; default: break; } } esm.writeHNT("LAST", mLastAiPackage); } void AiSequence::load(ESMReader& esm) { int count = 0; while (esm.isNextSub("AIPK")) { int type; esm.getHT(type); mPackages.emplace_back(); mPackages.back().mType = type; switch (type) { case Ai_Wander: { std::unique_ptr<AiWander> ptr = std::make_unique<AiWander>(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Travel: { std::unique_ptr<AiTravel> ptr = std::make_unique<AiTravel>(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Escort: { std::unique_ptr<AiEscort> ptr = std::make_unique<AiEscort>(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Follow: { std::unique_ptr<AiFollow> ptr = std::make_unique<AiFollow>(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Activate: { std::unique_ptr<AiActivate> ptr = std::make_unique<AiActivate>(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); ++count; break; } case Ai_Combat: { std::unique_ptr<AiCombat> ptr = std::make_unique<AiCombat>(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); break; } case Ai_Pursue: { std::unique_ptr<AiPursue> ptr = std::make_unique<AiPursue>(); ptr->load(esm); mPackages.back().mPackage = std::move(ptr); break; } default: return; } } esm.getHNOT(mLastAiPackage, "LAST"); if (count > 1 && esm.getFormatVersion() <= MaxOldAiPackageFormatVersion) { for (auto& pkg : mPackages) { if (pkg.mType == Ai_Wander) static_cast<AiWander&>(*pkg.mPackage).mData.mShouldRepeat = true; else if (pkg.mType == Ai_Travel) static_cast<AiTravel&>(*pkg.mPackage).mRepeat = true; else if (pkg.mType == Ai_Escort) static_cast<AiEscort&>(*pkg.mPackage).mRepeat = true; else if (pkg.mType == Ai_Follow) static_cast<AiFollow&>(*pkg.mPackage).mRepeat = true; else if (pkg.mType == Ai_Activate) static_cast<AiActivate&>(*pkg.mPackage).mRepeat = true; } } } } }