docs: add plugin setup guide
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user