Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
12 kB · 366 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367#include "loadnpc.hpp"
#include <array>
#include "esmreader.hpp"#include "esmwriter.hpp"
#include <components/misc/concepts.hpp>
namespace ESM{ namespace { struct EsmNPDTstruct52 { int16_t mLevel{}; std::array<unsigned char, Attribute::Length> mAttributes{}; std::array<unsigned char, Skill::Length> mSkills{}; uint16_t mHealth{}, mMana{}, mFatigue{}; unsigned char mDisposition{}, mReputation{}, mRank{}; int32_t mGold{}; };
struct NPDTstruct12 { EsmNPDTstruct52& mStruct; };
void toBinary(const NPC::NPDTstruct52& src, EsmNPDTstruct52& dst) { dst.mLevel = src.mLevel; for (int i = 0; i < Attribute::Length; ++i) dst.mAttributes[i] = src.getAttribute(ESM::Attribute::indexToRefId(i)); for (int i = 0; i < Skill::Length; ++i) dst.mSkills[i] = src.getSkill(ESM::Skill::indexToRefId(i)); dst.mHealth = src.mHealth; dst.mMana = src.mMana; dst.mFatigue = src.mFatigue; dst.mDisposition = src.mDisposition; dst.mReputation = src.mReputation; dst.mRank = src.mRank; dst.mGold = src.mGold; }
void fromBinary(const EsmNPDTstruct52& src, NPC::NPDTstruct52& dst) { dst.mLevel = src.mLevel; dst.mAttributes.clear(); for (int i = 0; i < Attribute::Length; ++i) dst.mAttributes.emplace(ESM::Attribute::indexToRefId(i), src.mAttributes[i]); for (int i = 0; i < Skill::Length; ++i) dst.mSkills.emplace(ESM::Skill::indexToRefId(i), src.mSkills[i]); dst.mHealth = src.mHealth; dst.mMana = src.mMana; dst.mFatigue = src.mFatigue; dst.mDisposition = src.mDisposition; dst.mReputation = src.mReputation; dst.mRank = src.mRank; dst.mGold = src.mGold; }
void writeBaseStats(ESMWriter& esm, NAME name, const std::map<ESM::RefId, unsigned char>& stats) { for (const auto& [stat, value] : stats) { if (stat.empty() || value == 0) continue; esm.startSubRecord(name); esm.writeT(value); esm.writeHRefId(stat); esm.endRecord(name); } }
void readBaseStats(ESMReader& esm, NAME name, std::map<ESM::RefId, unsigned char>& stats) { while (esm.isNextSub(name)) { esm.getSubHeader(); unsigned char value; esm.getT(value); ESM::RefId stat = esm.getRefId(esm.getSubSize() - sizeof(value)); stats.emplace(stat, value); } } }
template <Misc::SameAsWithoutCvref<EsmNPDTstruct52> T> void decompose(T&& v, const auto& f) { char padding1 = 0; char padding2 = 0; f(v.mLevel, v.mAttributes, v.mSkills, padding1, v.mHealth, v.mMana, v.mFatigue, v.mDisposition, v.mReputation, v.mRank, padding2, v.mGold); }
template <Misc::SameAsWithoutCvref<NPDTstruct12> T> void decompose(T&& v, const auto& f) { char padding[] = { 0, 0, 0 }; f(v.mStruct.mLevel, v.mStruct.mDisposition, v.mStruct.mReputation, v.mStruct.mRank, padding, v.mStruct.mGold); }
template <Misc::SameAsWithoutCvref<NPC::NPDTstruct52> T> void decompose(T&& v, const auto& f) { f(v.mLevel, v.mHealth, v.mMana, v.mFatigue, v.mDisposition, v.mReputation, v.mRank, v.mGold); }
void NPC::load(ESMReader& esm, bool& isDeleted) { isDeleted = false; mRecordFlags = esm.getRecordFlags();
mSpells.mList.clear(); mInventory.mList.clear(); mTransport.mList.clear(); mAiPackage.mList.clear(); mAiData.blank(); mAiData.mHello = mAiData.mFight = mAiData.mFlee = 30;
bool hasName = false; bool hasNpdt = false; bool hasFlags = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().toInt()) { case SREC_NAME: mId = esm.getRefId(); hasName = true; break; case fourCC("MODL"): mModel = esm.getHString(); break; case fourCC("FNAM"): mName = esm.getHString(); break; case fourCC("RNAM"): mRace = esm.getRefId(); break; case fourCC("CNAM"): mClass = esm.getRefId(); break; case fourCC("ANAM"): mFaction = esm.getRefId(); break; case fourCC("BNAM"): mHead = esm.getRefId(); break; case fourCC("KNAM"): mHair = esm.getRefId(); break; case fourCC("SCRI"): mScript = esm.getRefId(); break; case fourCC("NPDT"): { hasNpdt = true; if (esm.getFormatVersion() <= MaxFixedStatsFormatVersion) { esm.getSubHeader(); EsmNPDTstruct52 esmData; if (esm.getSubSize() == getCompositeSize(esmData)) { mNpdtType = NPC_DEFAULT; esm.getComposite(esmData); } else { NPDTstruct12 data{ esmData }; if (esm.getSubSize() == getCompositeSize(data)) { mNpdtType = NPC_WITH_AUTOCALCULATED_STATS;
// Clearing the mNdpt struct to initialize all values blankNpdt(); esm.getComposite(data); } else esm.fail("NPC_NPDT must be 12 or 52 bytes long"); } fromBinary(esmData, mNpdt); } else { esm.getSubComposite(mNpdt); readBaseStats(esm, "ATTR", mNpdt.mAttributes); readBaseStats(esm, "SKIL", mNpdt.mSkills); } break; } case fourCC("FLAG"): hasFlags = true; int32_t flags; esm.getHT(flags); mFlags = flags & 0xFF; mBloodType = ((flags >> 8) & 0xFF) >> 2; break; case fourCC("NPCS"): mSpells.add(esm); break; case fourCC("NPCO"): mInventory.add(esm); break; case fourCC("AIDT"): esm.getSubComposite(mAiData); break; case fourCC("DODT"): case fourCC("DNAM"): mTransport.add(esm); break; case AI_Wander: case AI_Activate: case AI_Escort: case AI_Follow: case AI_Travel: case AI_CNDT: mAiPackage.add(esm); break; case SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } }
if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasNpdt && !isDeleted) esm.fail("Missing NPDT subrecord"); if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); } void NPC::save(ESMWriter& esm, bool isDeleted) const { esm.writeHNCRefId("NAME", mId);
if (isDeleted) { esm.writeHNString("DELE", "", 3); return; }
esm.writeHNOCString("MODL", mModel.getOriginal()); esm.writeHNOCString("FNAM", mName); esm.writeHNCRefId("RNAM", mRace); esm.writeHNCRefId("CNAM", mClass); esm.writeHNCRefId("ANAM", mFaction); esm.writeHNCRefId("BNAM", mHead); esm.writeHNCRefId("KNAM", mHair); esm.writeHNOCRefId("SCRI", mScript);
if (esm.getFormatVersion() <= MaxFixedStatsFormatVersion) { EsmNPDTstruct52 data; toBinary(mNpdt, data); if (mNpdtType == NPC_DEFAULT) { esm.writeNamedComposite("NPDT", data); } else if (mNpdtType == NPC_WITH_AUTOCALCULATED_STATS) { esm.writeNamedComposite("NPDT", NPDTstruct12{ data }); } } else { esm.writeNamedComposite("NPDT", mNpdt); writeBaseStats(esm, "ATTR", mNpdt.mAttributes); writeBaseStats(esm, "SKIL", mNpdt.mSkills); }
esm.writeHNT("FLAG", ((mBloodType << 10) + mFlags));
mInventory.save(esm); mSpells.save(esm); esm.writeNamedComposite("AIDT", mAiData);
mTransport.save(esm);
mAiPackage.save(esm); }
bool NPC::isMale() const { return (mFlags & Female) == 0; }
void NPC::setIsMale(bool value) { mFlags |= Female; if (value) { mFlags ^= Female; } }
void NPC::blank() { mRecordFlags = 0; mNpdtType = NPC_DEFAULT; blankNpdt(); mBloodType = 0; mFlags = 0; mInventory.mList.clear(); mSpells.mList.clear(); mAiData.blank(); mAiData.mHello = mAiData.mFight = mAiData.mFlee = 30; mTransport.mList.clear(); mAiPackage.mList.clear(); mName.clear(); mModel.clear(); mRace = ESM::RefId(); mClass = ESM::RefId(); mFaction = ESM::RefId(); mScript = ESM::RefId(); mHair = ESM::RefId(); mHead = ESM::RefId(); }
void NPC::blankNpdt() { mNpdt.mLevel = 0; mNpdt.mAttributes.clear(); mNpdt.mSkills.clear(); mNpdt.mReputation = 0; mNpdt.mHealth = mNpdt.mMana = mNpdt.mFatigue = 0; mNpdt.mDisposition = 0; mNpdt.mRank = 0; mNpdt.mGold = 0; }
int NPC::getFactionRank() const { if (mFaction.empty()) return -1; else return mNpdt.mRank; }
const std::vector<Transport::Dest>& NPC::getTransport() const { return mTransport.mList; }
unsigned char NPC::NPDTstruct52::getAttribute(ESM::RefId id) const { const auto it = mAttributes.find(id); if (it == mAttributes.end()) return 0; return it->second; }
unsigned char NPC::NPDTstruct52::getSkill(ESM::RefId id) const { const auto it = mSkills.find(id); if (it == mSkills.end()) return 0; return it->second; }}