1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-16 17:59:56 +00:00
openmw/components/esm3/aisequence.cpp
elsid 6451750890
Write AiSequence and Script data field by field via decompose function
Use the same function to load and save to have single place with field order
definition. Use concepts for overload over different types.
2024-01-14 23:04:56 +01:00

307 lines
11 KiB
C++

#include "aisequence.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include <components/misc/concepts.hpp>
#include <algorithm>
#include <memory>
namespace ESM
{
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)
{
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<float>(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<float>(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<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"))
{
int32_t 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.getHNT(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;
}
}
}
}
}