Skip to content

Commit

Permalink
feat: parse NPCs
Browse files Browse the repository at this point in the history
  • Loading branch information
RobbeBryssinck committed Jan 17, 2022
1 parent ee5271e commit 5ce7919
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
20 changes: 20 additions & 0 deletions Code/es_loader/Records/Chunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,24 @@ NAME::NAME(Buffer::Reader& aReader)
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_baseId), 4);
}

DOFT::DOFT(Buffer::Reader& aReader)
{
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_formId), 4);
}

ACBS::ACBS(Buffer::Reader& aReader)
{
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_flags), 4);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_magickaOffset), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_staminaOffset), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_level), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_calcMinLevel), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_calcMaxLevel), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_speedMultiplier), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_dispositionBase), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_templateDataFlags), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_healthOffset), 2);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_bleedoutOverride), 2);
}

} // namespace
88 changes: 88 additions & 0 deletions Code/es_loader/Records/Chunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,92 @@ struct NAME
uint32_t m_baseId{};
};

struct DOFT
{
DOFT(){}
DOFT(Buffer::Reader& aReader);

uint32_t m_formId = 0;
};

struct ACBS
{
ACBS(){}
ACBS(Buffer::Reader& aReader);

enum ActorFlags
{
kFemale = 1 << 0,
kEssential = 1 << 1,
kIsChargenFacePreset = 1 << 2,
kRespawn = 1 << 3,
kAutoCalcStats = 1 << 4,
kUnique = 1 << 5,
kDoesntAffectStealth = 1 << 6,
kPCLevelMult = 1 << 7,
kAudioTemplate_m = 1 << 8, // ??
kProtected = 1 << 9,
kSummonable = 1 << 10,
kDoesntBleed = 1 << 11,
kOwnedOrFollowed_m = 1 << 12, // ??
kOppositeGenderAnims = 1 << 13,
kSimpleActor = 1 << 14,
kLoopedScript_m = 1 << 15, // ??
kLoopedAudio_m = 1 << 16, // ??
kGhostOrNonInteractable = 1 << 17,
kInvulnerable = 1 << 18,
};

enum TemplateFlags
{
kTraits = 1 << 0,
kStats = 1 << 1,
kFactions = 1 << 2,
kSpells = 1 << 3,
kAIData = 1 << 4,
kAIPackages = 1 << 5,
kUnused = 1 << 6,
kBaseData = 1 << 7,
kInventory = 1 << 8,
kScript = 1 << 9,
kAIDefPackList = 1 << 10,
kAttackData = 1 << 11,
kKeywords = 1 << 12
};

[[nodiscard]] bool IsFemale() const { return m_flags & ActorFlags::kFemale; }
[[nodiscard]] bool IsEssential() const { return m_flags & ActorFlags::kEssential; }
[[nodiscard]] bool IsChargenFacePreset() const { return m_flags & ActorFlags::kIsChargenFacePreset; }
[[nodiscard]] bool IsRespawn() const { return m_flags & ActorFlags::kRespawn; }
[[nodiscard]] bool IsAutoCalcStats() const { return m_flags & ActorFlags::kAutoCalcStats; }
[[nodiscard]] bool IsUnique() const { return m_flags & ActorFlags::kUnique; }
[[nodiscard]] bool DoesAffectStealth() const { return !(m_flags & ActorFlags::kDoesntAffectStealth); }
[[nodiscard]] bool IsPCLevelMult() const { return m_flags & ActorFlags::kPCLevelMult; }
[[nodiscard]] bool IsAudioTemplate() const { return m_flags & ActorFlags::kAudioTemplate_m; }
[[nodiscard]] bool IsProtected() const { return m_flags & ActorFlags::kProtected; }
[[nodiscard]] bool IsSummonable() const { return m_flags & ActorFlags::kSummonable; }
[[nodiscard]] bool DoesBleed() const { return !(m_flags & ActorFlags::kDoesntBleed); }
[[nodiscard]] bool IsOwnerOrFollowed() const { return m_flags & ActorFlags::kOwnedOrFollowed_m; }
[[nodiscard]] bool IsOppositeGenderAnims() const { return m_flags & ActorFlags::kOppositeGenderAnims; }
[[nodiscard]] bool IsSimpleActor() const { return m_flags & ActorFlags::kSimpleActor; }
[[nodiscard]] bool IsLoopedScript() const { return m_flags & ActorFlags::kLoopedScript_m; }
[[nodiscard]] bool IsLoopedAudio() const { return m_flags & ActorFlags::kLoopedAudio_m; }
[[nodiscard]] bool IsGhostOrNonInteractable() const { return m_flags & ActorFlags::kGhostOrNonInteractable; }
[[nodiscard]] bool IsInvulnerable() const { return m_flags & ActorFlags::kInvulnerable; }

uint32_t m_flags = 0;
int16_t m_magickaOffset = 0;
int16_t m_staminaOffset = 0;
uint16_t m_level = 0;
uint16_t m_calcMinLevel = 0;
uint16_t m_calcMaxLevel = 0;
uint16_t m_speedMultiplier = 0;
uint16_t m_dispositionBase = 0;
uint16_t m_templateDataFlags = 0;
int16_t m_healthOffset = 0;
uint16_t m_bleedoutOverride = 0;
};

static_assert(sizeof(ACBS) == 0x18);

} // namespace
22 changes: 22 additions & 0 deletions Code/es_loader/Records/NPC.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "NPC.h"

#include <ESLoader.h>

NPC::Data NPC::ParseChunks() noexcept
{
Data data;

IterateChunks([&](ChunkId aChunkId, Buffer::Reader& aReader) {
switch (aChunkId)
{
case ChunkId::ACBS_ID:
data.m_baseStats = Chunks::ACBS(aReader);
break;
case ChunkId::DOFT_ID:
data.m_defaultOutfit = Chunks::DOFT(aReader);
break;
}
});

return data;
}
19 changes: 19 additions & 0 deletions Code/es_loader/Records/NPC.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include "Record.h"
#include "Chunks.h"

// https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/NPC
class NPC : Record
{
public:
static constexpr FormEnum kType = FormEnum::NPC_;

struct Data
{
Chunks::ACBS m_baseStats{};
Chunks::DOFT m_defaultOutfit{};
};

Data ParseChunks() noexcept;
};
1 change: 1 addition & 0 deletions Code/es_loader/Records/TESFileRecordTypes.inl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum class FormEnum : uint32_t
CELL = 0x4C4C4543,
CLMT = 0x544D4C43,
CONT = 0x544E4F43,
NPC_ = 0x5F43504E,
};

enum class ChunkId : uint32_t
Expand Down

0 comments on commit 5ce7919

Please sign in to comment.