2014-06-12 21:27:04 +00:00
|
|
|
#include "aisequence.hpp"
|
|
|
|
|
|
|
|
#include "esmreader.hpp"
|
|
|
|
#include "esmwriter.hpp"
|
|
|
|
|
2024-01-12 01:15:54 +00:00
|
|
|
#include <components/misc/concepts.hpp>
|
|
|
|
|
2021-11-17 19:44:55 +00:00
|
|
|
#include <algorithm>
|
2014-06-12 21:27:04 +00:00
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
namespace ESM
|
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
template <Misc::SameAsWithoutCvref<AiSequence::AiWanderData> T>
|
|
|
|
void decompose(T&& v, const auto& f)
|
|
|
|
{
|
|
|
|
f(v.mDistance, v.mDuration, v.mTimeOfDay, v.mIdle, v.mShouldRepeat);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Misc::SameAsWithoutCvref<AiSequence::AiWanderDuration> T>
|
|
|
|
void decompose(T&& v, const auto& f)
|
|
|
|
{
|
|
|
|
std::uint32_t unused = 0;
|
|
|
|
f(v.mRemainingDuration, unused);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Misc::SameAsWithoutCvref<AiSequence::AiTravelData> T>
|
|
|
|
void decompose(T&& v, const auto& f)
|
|
|
|
{
|
|
|
|
f(v.mX, v.mY, v.mZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Misc::SameAsWithoutCvref<AiSequence::AiEscortData> T>
|
|
|
|
void decompose(T&& v, const auto& f)
|
2014-06-12 21:27:04 +00:00
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
f(v.mX, v.mY, v.mZ, v.mDuration);
|
|
|
|
}
|
2022-09-22 18:26:05 +00:00
|
|
|
|
2024-01-12 01:15:54 +00:00
|
|
|
namespace AiSequence
|
|
|
|
{
|
2016-06-11 13:34:49 +00:00
|
|
|
void AiWander::load(ESMReader& esm)
|
2014-12-31 20:27:19 +00:00
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.getNamedComposite("DATA", mData);
|
|
|
|
esm.getNamedComposite("STAR", mDurationData); // was mStartTime
|
2023-12-20 11:28:34 +00:00
|
|
|
mStoredInitialActorPosition = esm.getHNOT("POS_", mInitialActorPosition.mValues);
|
2014-12-31 20:27:19 +00:00
|
|
|
}
|
2014-06-12 21:27:04 +00:00
|
|
|
|
|
|
|
void AiWander::save(ESMWriter& esm) const
|
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.writeNamedComposite("DATA", mData);
|
|
|
|
esm.writeNamedComposite("STAR", mDurationData); // was mStartTime
|
2014-12-31 20:27:19 +00:00
|
|
|
if (mStoredInitialActorPosition)
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.writeHNT("POS_", mInitialActorPosition.mValues);
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AiTravel::load(ESMReader& esm)
|
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.getNamedComposite("DATA", mData);
|
2024-01-06 13:59:22 +00:00
|
|
|
esm.getHNT(mHidden, "HIDD");
|
2021-11-17 19:44:55 +00:00
|
|
|
mRepeat = false;
|
|
|
|
esm.getHNOT(mRepeat, "REPT");
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AiTravel::save(ESMWriter& esm) const
|
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.writeNamedComposite("DATA", mData);
|
2018-05-25 15:31:31 +00:00
|
|
|
esm.writeHNT("HIDD", mHidden);
|
2021-11-17 19:44:55 +00:00
|
|
|
if (mRepeat)
|
|
|
|
esm.writeHNT("REPT", mRepeat);
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AiEscort::load(ESMReader& esm)
|
2021-11-17 19:44:55 +00:00
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.getNamedComposite("DATA", mData);
|
2023-02-12 18:36:56 +00:00
|
|
|
mTargetId = esm.getHNRefId("TARG");
|
2019-02-05 04:03:31 +00:00
|
|
|
mTargetActorId = -1;
|
2017-11-21 16:00:51 +00:00
|
|
|
esm.getHNOT(mTargetActorId, "TAID");
|
2014-06-12 21:27:04 +00:00
|
|
|
esm.getHNT(mRemainingDuration, "DURA");
|
2023-01-19 16:31:45 +00:00
|
|
|
mCellId = esm.getHNOString("CELL");
|
2021-11-17 19:44:55 +00:00
|
|
|
mRepeat = false;
|
|
|
|
esm.getHNOT(mRepeat, "REPT");
|
2023-02-10 12:16:52 +00:00
|
|
|
if (esm.getFormatVersion() <= MaxOldAiPackageFormatVersion)
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
2021-11-17 19:44:55 +00:00
|
|
|
// 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);
|
2022-09-22 18:26:05 +00:00
|
|
|
}
|
2021-11-17 19:44:55 +00:00
|
|
|
}
|
2014-06-12 21:27:04 +00:00
|
|
|
|
|
|
|
void AiEscort::save(ESMWriter& esm) const
|
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.writeNamedComposite("DATA", mData);
|
2023-02-12 18:36:56 +00:00
|
|
|
esm.writeHNRefId("TARG", mTargetId);
|
2017-11-21 16:00:51 +00:00
|
|
|
esm.writeHNT("TAID", mTargetActorId);
|
2014-06-12 21:27:04 +00:00
|
|
|
esm.writeHNT("DURA", mRemainingDuration);
|
|
|
|
if (!mCellId.empty())
|
2023-01-19 16:31:45 +00:00
|
|
|
esm.writeHNString("CELL", mCellId);
|
2021-11-17 19:44:55 +00:00
|
|
|
if (mRepeat)
|
|
|
|
esm.writeHNT("REPT", mRepeat);
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AiFollow::load(ESMReader& esm)
|
2021-11-17 19:44:55 +00:00
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.getNamedComposite("DATA", mData);
|
2023-02-12 18:36:56 +00:00
|
|
|
mTargetId = esm.getHNRefId("TARG");
|
2019-02-05 04:03:31 +00:00
|
|
|
mTargetActorId = -1;
|
2017-11-21 16:00:51 +00:00
|
|
|
esm.getHNOT(mTargetActorId, "TAID");
|
2014-06-12 21:27:04 +00:00
|
|
|
esm.getHNT(mRemainingDuration, "DURA");
|
2023-01-19 16:31:45 +00:00
|
|
|
mCellId = esm.getHNOString("CELL");
|
2014-06-12 21:27:04 +00:00
|
|
|
esm.getHNT(mAlwaysFollow, "ALWY");
|
2014-08-06 19:16:14 +00:00
|
|
|
mCommanded = false;
|
|
|
|
esm.getHNOT(mCommanded, "CMND");
|
2014-12-09 21:25:28 +00:00
|
|
|
mActive = false;
|
|
|
|
esm.getHNOT(mActive, "ACTV");
|
2021-11-17 19:44:55 +00:00
|
|
|
mRepeat = false;
|
|
|
|
esm.getHNOT(mRepeat, "REPT");
|
2023-02-10 12:16:52 +00:00
|
|
|
if (esm.getFormatVersion() <= MaxOldAiPackageFormatVersion)
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
2021-11-17 19:44:55 +00:00
|
|
|
// 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);
|
2022-09-22 18:26:05 +00:00
|
|
|
}
|
2021-11-17 19:44:55 +00:00
|
|
|
}
|
2014-06-12 21:27:04 +00:00
|
|
|
|
|
|
|
void AiFollow::save(ESMWriter& esm) const
|
|
|
|
{
|
2024-01-12 01:15:54 +00:00
|
|
|
esm.writeNamedComposite("DATA", mData);
|
2023-02-12 18:36:56 +00:00
|
|
|
esm.writeHNRefId("TARG", mTargetId);
|
2017-11-21 16:00:51 +00:00
|
|
|
esm.writeHNT("TAID", mTargetActorId);
|
2014-06-12 21:27:04 +00:00
|
|
|
esm.writeHNT("DURA", mRemainingDuration);
|
|
|
|
if (!mCellId.empty())
|
2023-01-19 16:31:45 +00:00
|
|
|
esm.writeHNString("CELL", mCellId);
|
2014-06-12 21:27:04 +00:00
|
|
|
esm.writeHNT("ALWY", mAlwaysFollow);
|
2014-08-06 19:16:14 +00:00
|
|
|
esm.writeHNT("CMND", mCommanded);
|
2014-12-09 21:25:28 +00:00
|
|
|
if (mActive)
|
|
|
|
esm.writeHNT("ACTV", mActive);
|
2021-11-17 19:44:55 +00:00
|
|
|
if (mRepeat)
|
|
|
|
esm.writeHNT("REPT", mRepeat);
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AiActivate::load(ESMReader& esm)
|
|
|
|
{
|
2023-02-12 18:36:56 +00:00
|
|
|
mTargetId = esm.getHNRefId("TARG");
|
2021-11-17 19:44:55 +00:00
|
|
|
mRepeat = false;
|
|
|
|
esm.getHNOT(mRepeat, "REPT");
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AiActivate::save(ESMWriter& esm) const
|
|
|
|
{
|
2023-02-12 18:36:56 +00:00
|
|
|
esm.writeHNRefId("TARG", mTargetId);
|
2021-11-17 19:44:55 +00:00
|
|
|
if (mRepeat)
|
|
|
|
esm.writeHNT("REPT", mRepeat);
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
2014-06-12 21:27:04 +00:00
|
|
|
case Ai_Wander:
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<const AiWander&>(*it->mPackage).save(esm);
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
case Ai_Travel:
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<const AiTravel&>(*it->mPackage).save(esm);
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
case Ai_Escort:
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<const AiEscort&>(*it->mPackage).save(esm);
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
case Ai_Follow:
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<const AiFollow&>(*it->mPackage).save(esm);
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
case Ai_Activate:
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<const AiActivate&>(*it->mPackage).save(esm);
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
case Ai_Combat:
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<const AiCombat&>(*it->mPackage).save(esm);
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
case Ai_Pursue:
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<const AiPursue&>(*it->mPackage).save(esm);
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2022-09-22 18:26:05 +00:00
|
|
|
}
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
2018-05-25 16:07:08 +00:00
|
|
|
|
|
|
|
esm.writeHNT("LAST", mLastAiPackage);
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AiSequence::load(ESMReader& esm)
|
|
|
|
{
|
2021-11-17 19:44:55 +00:00
|
|
|
int count = 0;
|
2014-06-12 21:27:04 +00:00
|
|
|
while (esm.isNextSub("AIPK"))
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
2023-10-24 17:25:52 +00:00
|
|
|
int32_t type;
|
2014-06-12 21:27:04 +00:00
|
|
|
esm.getHT(type);
|
|
|
|
|
2020-10-17 08:26:35 +00:00
|
|
|
mPackages.emplace_back();
|
2014-06-12 21:27:04 +00:00
|
|
|
mPackages.back().mType = type;
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case Ai_Wander:
|
|
|
|
{
|
2021-06-25 18:55:38 +00:00
|
|
|
std::unique_ptr<AiWander> ptr = std::make_unique<AiWander>();
|
2014-06-12 21:27:04 +00:00
|
|
|
ptr->load(esm);
|
2022-02-22 23:39:30 +00:00
|
|
|
mPackages.back().mPackage = std::move(ptr);
|
2021-11-17 19:44:55 +00:00
|
|
|
++count;
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Ai_Travel:
|
|
|
|
{
|
2021-06-25 18:55:38 +00:00
|
|
|
std::unique_ptr<AiTravel> ptr = std::make_unique<AiTravel>();
|
2014-06-12 21:27:04 +00:00
|
|
|
ptr->load(esm);
|
2022-02-22 23:39:30 +00:00
|
|
|
mPackages.back().mPackage = std::move(ptr);
|
2021-11-17 19:44:55 +00:00
|
|
|
++count;
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Ai_Escort:
|
|
|
|
{
|
2021-06-25 18:55:38 +00:00
|
|
|
std::unique_ptr<AiEscort> ptr = std::make_unique<AiEscort>();
|
2014-06-12 21:27:04 +00:00
|
|
|
ptr->load(esm);
|
2022-02-22 23:39:30 +00:00
|
|
|
mPackages.back().mPackage = std::move(ptr);
|
2022-09-22 18:26:05 +00:00
|
|
|
++count;
|
|
|
|
break;
|
|
|
|
}
|
2021-11-17 19:44:55 +00:00
|
|
|
case Ai_Follow:
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
2021-06-25 18:55:38 +00:00
|
|
|
std::unique_ptr<AiFollow> ptr = std::make_unique<AiFollow>();
|
2014-06-12 21:27:04 +00:00
|
|
|
ptr->load(esm);
|
2022-02-22 23:39:30 +00:00
|
|
|
mPackages.back().mPackage = std::move(ptr);
|
2022-09-22 18:26:05 +00:00
|
|
|
++count;
|
|
|
|
break;
|
|
|
|
}
|
2014-06-12 21:27:04 +00:00
|
|
|
case Ai_Activate:
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
2021-06-25 18:55:38 +00:00
|
|
|
std::unique_ptr<AiActivate> ptr = std::make_unique<AiActivate>();
|
2014-06-12 21:27:04 +00:00
|
|
|
ptr->load(esm);
|
2022-02-22 23:39:30 +00:00
|
|
|
mPackages.back().mPackage = std::move(ptr);
|
2021-11-17 19:44:55 +00:00
|
|
|
++count;
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Ai_Combat:
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
2021-06-25 18:55:38 +00:00
|
|
|
std::unique_ptr<AiCombat> ptr = std::make_unique<AiCombat>();
|
2014-06-12 21:27:04 +00:00
|
|
|
ptr->load(esm);
|
2022-02-22 23:39:30 +00:00
|
|
|
mPackages.back().mPackage = std::move(ptr);
|
2022-09-22 18:26:05 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-06-12 21:27:04 +00:00
|
|
|
case Ai_Pursue:
|
|
|
|
{
|
2021-06-25 18:55:38 +00:00
|
|
|
std::unique_ptr<AiPursue> ptr = std::make_unique<AiPursue>();
|
2014-06-12 21:27:04 +00:00
|
|
|
ptr->load(esm);
|
2022-02-22 23:39:30 +00:00
|
|
|
mPackages.back().mPackage = std::move(ptr);
|
2014-06-12 21:27:04 +00:00
|
|
|
break;
|
2022-09-22 18:26:05 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
2018-05-25 16:07:08 +00:00
|
|
|
|
2024-01-06 13:59:22 +00:00
|
|
|
esm.getHNT(mLastAiPackage, "LAST");
|
2021-11-17 19:44:55 +00:00
|
|
|
|
2023-02-10 12:16:52 +00:00
|
|
|
if (count > 1 && esm.getFormatVersion() <= MaxOldAiPackageFormatVersion)
|
2021-11-17 19:44:55 +00:00
|
|
|
{
|
|
|
|
for (auto& pkg : mPackages)
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
2021-11-17 19:44:55 +00:00
|
|
|
if (pkg.mType == Ai_Wander)
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<AiWander&>(*pkg.mPackage).mData.mShouldRepeat = true;
|
2021-11-17 19:44:55 +00:00
|
|
|
else if (pkg.mType == Ai_Travel)
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<AiTravel&>(*pkg.mPackage).mRepeat = true;
|
2021-11-17 19:44:55 +00:00
|
|
|
else if (pkg.mType == Ai_Escort)
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<AiEscort&>(*pkg.mPackage).mRepeat = true;
|
2021-11-17 19:44:55 +00:00
|
|
|
else if (pkg.mType == Ai_Follow)
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<AiFollow&>(*pkg.mPackage).mRepeat = true;
|
2021-11-17 19:44:55 +00:00
|
|
|
else if (pkg.mType == Ai_Activate)
|
2022-02-22 23:39:30 +00:00
|
|
|
static_cast<AiActivate&>(*pkg.mPackage).mRepeat = true;
|
2022-09-22 18:26:05 +00:00
|
|
|
}
|
2021-11-17 19:44:55 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
|
|
|
}
|