docs: add plugin setup guide

This commit is contained in:
2026-05-30 18:33:24 +12:00
parent f3db41c402
commit b61043a3fe
1776 changed files with 122386 additions and 2 deletions
+92
View File
@@ -0,0 +1,92 @@
#include "RE/T/TES.h"
#include "RE/E/EXTERIOR_DATA.h"
#include "RE/G/GridCellArray.h"
#include "RE/T/TESObjectCELL.h"
#include "RE/T/TESObjectREFR.h"
#include "RE/T/TESWorldSpace.h"
namespace RE
{
void TES::ForEachReference(std::function<BSContainer::ForEachResult(TESObjectREFR* a_ref)> a_callback)
{
if (interiorCell) {
interiorCell->ForEachReference([&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
} else {
if (const auto gridLength = gridCells ? gridCells->dimension : 0; gridLength > 0) {
std::uint32_t x = 0;
do {
std::uint32_t y = 0;
do {
if (const auto cell = gridCells->GetCell(x, y); cell && cell->IsAttached()) {
cell->ForEachReference([&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
++y;
} while (y < gridLength);
++x;
} while (x < gridLength);
}
}
if (const auto skyCell = worldSpace ? worldSpace->GetSkyCell() : nullptr; skyCell) {
skyCell->ForEachReference([&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
}
void TES::ForEachReferenceInRange(const NiPoint3& a_origin, const float a_radius, std::function<BSContainer::ForEachResult(TESObjectREFR* a_ref)> a_callback)
{
if (a_radius > 0.0f) {
if (interiorCell) {
interiorCell->ForEachReferenceInRange(a_origin, a_radius, [&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
} else {
if (const auto gridLength = gridCells ? gridCells->dimension : 0; gridLength > 0) {
const float yPlus = a_origin.y + a_radius;
const float yMinus = a_origin.y - a_radius;
const float xPlus = a_origin.x + a_radius;
const float xMinus = a_origin.x - a_radius;
std::uint32_t x = 0;
do {
std::uint32_t y = 0;
do {
if (const auto cell = gridCells->GetCell(x, y); cell && cell->IsAttached()) {
if (const auto cellCoords = cell->GetCoordinates(); cellCoords) {
const NiPoint2 worldPos{ cellCoords->worldX, cellCoords->worldY };
if (worldPos.x < xPlus && (worldPos.x + 4096.0f) > xMinus && worldPos.y < yPlus && (worldPos.y + 4096.0f) > yMinus) {
cell->ForEachReferenceInRange(a_origin, a_radius, [&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
}
}
++y;
} while (y < gridLength);
++x;
} while (x < gridLength);
}
}
if (const auto skyCell = worldSpace ? worldSpace->GetSkyCell() : nullptr; skyCell) {
skyCell->ForEachReferenceInRange(a_origin, a_radius, [&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
} else {
ForEachReference([&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
}
void TES::ForEachReferenceInRange(const TESObjectREFR* a_ref, const float a_radius, std::function<BSContainer::ForEachResult(TESObjectREFR* a_ref)> a_callback)
{
ForEachReferenceInRange(a_ref->GetPosition(), a_radius, a_callback);
}
}
@@ -0,0 +1,52 @@
#include "RE/T/TESFullName.h"
#include "RE/C/CHANGE_TYPES.h"
#include "RE/T/TESActorBase.h"
#include "RE/T/TESForm.h"
#include "RE/T/TESFormUtil.h"
#include "RE/T/TESObjectCELL.h"
namespace RE
{
std::string_view TESFullName::GetFullName(const TESForm& a_form, bool a_strict)
{
if (const auto fullName = a_form.As<TESFullName>()) {
const auto name = fullName->GetFullName();
return name ? name : ""sv;
} else {
if (a_strict) {
switch (a_form.GetFormType()) {
case ENUM_FORM_ID::kKYWD: // BGSKeyword
case ENUM_FORM_ID::kLCRT: // BGSLocationRefType
case ENUM_FORM_ID::kAACT: // BGSAction
case ENUM_FORM_ID::kLIGH: // TESObjectLIGH
case ENUM_FORM_ID::kSTAT: // TESObjectSTAT
case ENUM_FORM_ID::kSCOL: // BGSStaticCollection
case ENUM_FORM_ID::kMSTT: // BGSMovableStatic
case ENUM_FORM_ID::kFLST: // BGSListForm
break;
default:
return ""sv;
}
}
const auto& map = GetSparseFullNameMap();
const auto it = map.find(std::addressof(a_form));
return it != map.end() ? it->second : ""sv;
}
}
void TESFullName::SetFullName(TESForm& a_form, std::string_view a_fullName)
{
if (const auto full = a_form.As<TESFullName>()) {
full->fullName = a_fullName;
if (const auto actor = a_form.As<TESActorBase>()) {
actor->AddChange(CHANGE_TYPES::kActorBaseFullName);
} else if (const auto cell = a_form.As<TESObjectCELL>()) {
cell->AddChange(CHANGE_TYPES::kCellFullname);
} else {
a_form.AddChange(CHANGE_TYPES::kBaseObjectFullName);
}
}
}
}
@@ -0,0 +1,76 @@
#include "RE/T/TESNPC.h"
#include "RE/P/PlayerCharacter.h"
#include "RE/T/TESRace.h"
namespace RE
{
void TESNPC::CopyPerkRankArray(const std::vector<PerkRankData>& a_copiedData)
{
const auto oldData = perks;
const auto newSize = a_copiedData.size();
const auto newData = calloc<PerkRankData>(newSize);
std::ranges::copy(a_copiedData, newData);
perkCount = static_cast<std::uint32_t>(newSize);
perks = newData;
free(oldData);
}
bool TESNPC::AddPerks(const std::vector<BGSPerk*>& a_perks, std::int8_t a_rank)
{
std::vector<PerkRankData> copiedData{ perks, perks + perkCount };
std::ranges::for_each(a_perks, [&](auto& perk) {
if (!GetPerkIndex(perk)) {
const auto newPerk = new PerkRankData(perk, a_rank);
copiedData.push_back(*newPerk);
}
});
CopyPerkRankArray(copiedData);
return true;
}
bool TESNPC::ContainsKeyword(std::string_view a_editorID) const
{
if (ContainsKeywordString(a_editorID)) {
return true;
}
if (const auto npcRace = GetFormRace(); npcRace && npcRace->ContainsKeywordString(a_editorID)) {
return true;
}
return false;
}
bool TESNPC::HasApplicableKeywordString(std::string_view a_editorID) const
{
if (HasKeywordString(a_editorID)) {
return true;
}
if (const auto npcRace = GetFormRace(); npcRace && npcRace->HasKeywordString(a_editorID)) {
return true;
}
return false;
}
bool TESNPC::RemovePerks(const std::vector<BGSPerk*>& a_perks)
{
std::vector<PerkRankData> copiedData{ perks, perks + perkCount };
if (std::erase_if(copiedData, [&](auto& perkRank) { return std::ranges::find(a_perks, perkRank.perk) != a_perks.end(); }) > 0) {
CopyPerkRankArray(copiedData);
return true;
}
return false;
}
bool TESNPC::UsingAlternateHeadPartList() const
{
if (const auto player = PlayerCharacter::GetSingleton(); IsPlayer() && player) {
const auto& map = GetAlternateHeadPartListMap();
return player->charGenRace && player->charGenRace != formRace && map.contains(player->GetNPC());
} else {
return originalRace && originalRace != formRace;
}
}
}
@@ -0,0 +1,52 @@
#include "RE/T/TESObjectCELL.h"
#include "RE/E/EXTERIOR_DATA.h"
#include "RE/E/ExtraCellWaterType.h"
#include "RE/E/ExtraDataList.h"
#include "RE/T/TESObjectREFR.h"
#include "RE/T/TESWorldSpace.h"
namespace RE
{
void TESObjectCELL::ForEachReference(std::function<BSContainer::ForEachResult(TESObjectREFR*)> a_callback)
{
const BSAutoLock locker(spinLock);
for (const auto& ref : references) {
if (ref && a_callback(ref.get()) == BSContainer::ForEachResult::kStop) {
break;
}
}
}
void TESObjectCELL::ForEachReferenceInRange(const NiPoint3& a_origin, float a_radius, std::function<BSContainer::ForEachResult(TESObjectREFR*)> a_callback)
{
const float squaredRadius = a_radius * a_radius;
ForEachReference([&](TESObjectREFR* a_ref) {
const auto distance = a_origin.GetSquaredDistance(a_ref->GetPosition());
if (distance <= squaredRadius)
return a_callback(a_ref);
return BSContainer::ForEachResult::kContinue;
});
}
EXTERIOR_DATA* TESObjectCELL::GetCoordinates() const
{
return IsExterior() ? cellData.exterior : nullptr;
}
TESWaterForm* TESObjectCELL::GetWaterType() const noexcept
{
const auto xWater = extraList ? extraList->GetByType<ExtraCellWaterType>() : nullptr;
auto water = xWater ? xWater->water : nullptr;
if (!water) {
water = IsExterior() && worldSpace ? worldSpace->GetWaterType() : nullptr;
if (!water) {
static REL::Relocation<TESWaterForm**> defaultWater{ ID::TESObjectCELL::DefaultWater };
water = *defaultWater;
}
}
return water;
}
}
@@ -0,0 +1,15 @@
#include "RE/T/TESValueForm.h"
#include "RE/C/CHANGE_TYPES.h"
#include "RE/T/TESForm.h"
namespace RE
{
void TESValueForm::SetFormValue(TESForm& a_form, std::int32_t a_value)
{
if (const auto val = a_form.As<TESValueForm>()) {
val->value = a_value;
a_form.AddChange(CHANGE_TYPES::kBaseObjectValue);
}
}
}
@@ -0,0 +1,7 @@
#include "RE/T/ThumbstickEvent.h"
namespace RE
{
template ThumbstickEvent* InputEvent::As() noexcept;
template const ThumbstickEvent* InputEvent::As() const noexcept;
}