Skip to content

Commit 5d32888

Browse files
committed
New function: FindNearbyBooks
New function: TryAffixDisplayName
1 parent ee2705b commit 5d32888

File tree

5 files changed

+119
-32
lines changed

5 files changed

+119
-32
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ Bool[] Function SearchListsForForm(FormList akHaystack, Form akNeedle) Global Na
6666
; Returns actors in loaded cells within `afRadius` of `akOrigin`
6767
Actor[] Function FindNearbyActors(ObjectReference akOrigin, Float afRadius) Global Native
6868
69+
; Returns books in loaded cells within `afRadius` of `akOrigin`
70+
ObjectReference[] Function FindNearbyBooks(ObjectReference akOrigin, Float afRadius) Global Native
71+
6972
; Returns commanded actors in loaded cells within `afRadius` of `akOrigin` who are controlled by `akOrigin`
7073
Actor[] Function FindNearbyCommandedActors(ObjectReference akOrigin, Float afRadius) Global Native
7174
@@ -77,6 +80,11 @@ Actor[] Function FindNearbySummons(ObjectReference akOrigin, Float afRadius) Glo
7780
7881
; Returns teammates in loaded cells within `afRadius` of player
7982
Actor[] Function FindNearbyTeammates(Float afRadius) Global Native
83+
84+
; Attempts to affix full name of `akMessage` to display name for `akRef`, and returns whether operation was successful
85+
; If `abPrepend` is True, the message name will be prepended. If False, the name will be appended.
86+
; If `abForce` is True, display names set by quest aliases will be overridden.
87+
Bool Function TryAffixDisplayName(ObjectReference akRef, Message akMessage, Bool abPrepend, Bool abForce) Global Native
8088
```
8189

8290
## String Functions

Scripts/Source/LibFire.psc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ Bool[] Function SearchListsForForm(FormList akHaystack, Form akNeedle) Global Na
5757
; Returns actors in loaded cells within `afRadius` of `akOrigin`
5858
Actor[] Function FindNearbyActors(ObjectReference akOrigin, Float afRadius) Global Native
5959

60+
; Returns books in loaded cells within `afRadius` of `akOrigin`
61+
ObjectReference[] Function FindNearbyBooks(ObjectReference akOrigin, Float afRadius) Global Native
62+
6063
; Returns commanded actors in loaded cells within `afRadius` of `akOrigin` who are controlled by `akOrigin`
6164
Actor[] Function FindNearbyCommandedActors(ObjectReference akOrigin, Float afRadius) Global Native
6265

@@ -69,6 +72,11 @@ Actor[] Function FindNearbySummons(ObjectReference akOrigin, Float afRadius) Glo
6972
; Returns teammates in loaded cells within `afRadius` of player
7073
Actor[] Function FindNearbyTeammates(Float afRadius) Global Native
7174

75+
; Attempts to affix full name of `akMessage` to display name for `akRef`, and returns whether operation was successful
76+
; If `abPrepend` is True, the message name will be prepended. If False, the name will be appended.
77+
; If `abForce` is True, display names set by quest aliases will be overridden.
78+
Bool Function TryAffixDisplayName(ObjectReference akRef, Message akMessage, Bool abPrepend, Bool abForce) Global Native
79+
7280
{ String }
7381

7482
; Returns whether `asText` contains `asSubText` case-sensitively

src/Papyrus/PapyrusObjectREFR.cpp

Lines changed: 101 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace PapyrusObjectREFR
1414
}
1515

1616
if (const auto TES = RE::TES::GetSingleton(); TES) {
17-
const auto formType = static_cast<RE::FormType>(RE::FormType::NPC);
17+
const auto formType = RE::FormType::NPC;
1818

1919
TES->ForEachReferenceInRange(a_origin, a_radius, [&](RE::TESObjectREFR& a_ref) {
2020
auto base = a_ref.GetBaseObject();
@@ -30,6 +30,30 @@ namespace PapyrusObjectREFR
3030
return vec;
3131
}
3232

33+
auto FindNearbyBooks(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::TESObjectREFR* a_origin, float a_radius) -> std::vector<RE::TESObjectREFR*>
34+
{
35+
std::vector<RE::TESObjectREFR*> vec;
36+
37+
if (!a_origin) {
38+
a_vm->TraceStack("akOrigin cannot be None", a_stackID, Severity::kInfo);
39+
return vec;
40+
}
41+
42+
if (const auto TES = RE::TES::GetSingleton(); TES) {
43+
const auto formType = RE::FormType::Book;
44+
45+
TES->ForEachReferenceInRange(a_origin, a_radius, [&](RE::TESObjectREFR& a_ref) {
46+
auto base = a_ref.GetBaseObject();
47+
if (a_ref.As<RE::TESObjectREFR>() != a_origin && (a_ref.Is(formType) || base && base->Is(formType))) {
48+
vec.push_back(&a_ref);
49+
}
50+
return true;
51+
});
52+
}
53+
54+
return vec;
55+
}
56+
3357
auto FindNearbyCommandedActors(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::TESObjectREFR* a_origin, float a_radius) -> std::vector<RE::Actor*>
3458
{
3559
std::vector<RE::Actor*> vec;
@@ -40,25 +64,25 @@ namespace PapyrusObjectREFR
4064
}
4165

4266
if (const auto TES = RE::TES::GetSingleton(); TES) {
43-
const auto formType = static_cast<RE::FormType>(RE::FormType::NPC);
67+
const auto formType = RE::FormType::NPC;
4468

4569
TES->ForEachReferenceInRange(a_origin, a_radius, [&](RE::TESObjectREFR& a_ref) {
46-
auto base = a_ref.GetBaseObject();
47-
if (a_ref.As<RE::TESObjectREFR>() != a_origin && (a_ref.Is(formType) || base && base->Is(formType))) {
48-
if (auto actor = a_ref.As<RE::Actor>(); actor) {
49-
if (actor->IsCommandedActor()) {
50-
if (const auto commandingActorHandle = actor->GetCommandingActor(); commandingActorHandle) {
51-
const auto commandingActorPtr = commandingActorHandle.get();
52-
const auto commandingActor = commandingActorPtr.get();
53-
54-
if (commandingActor != nullptr && commandingActor == a_origin) {
55-
vec.push_back(actor);
56-
}
57-
}
58-
}
59-
}
60-
}
61-
return true;
70+
auto base = a_ref.GetBaseObject();
71+
if (a_ref.As<RE::TESObjectREFR>() != a_origin && (a_ref.Is(formType) || base && base->Is(formType))) {
72+
if (auto actor = a_ref.As<RE::Actor>(); actor) {
73+
if (actor->IsCommandedActor()) {
74+
if (const auto commandingActorHandle = actor->GetCommandingActor(); commandingActorHandle) {
75+
const auto commandingActorPtr = commandingActorHandle.get();
76+
const auto commandingActor = commandingActorPtr.get();
77+
78+
if (commandingActor != nullptr && commandingActor == a_origin) {
79+
vec.push_back(actor);
80+
}
81+
}
82+
}
83+
}
84+
}
85+
return true;
6286
});
6387
}
6488

@@ -71,7 +95,7 @@ namespace PapyrusObjectREFR
7195

7296
if (const auto TES = RE::TES::GetSingleton(); TES) {
7397
if (const auto player = RE::PlayerCharacter::GetSingleton(); player) {
74-
const auto formType = static_cast<RE::FormType>(RE::FormType::NPC);
98+
const auto formType = RE::FormType::NPC;
7599

76100
TES->ForEachReferenceInRange(player, a_radius, [&](RE::TESObjectREFR& a_ref) {
77101
auto base = a_ref.GetBaseObject();
@@ -109,18 +133,18 @@ namespace PapyrusObjectREFR
109133
}
110134

111135
if (const auto TES = RE::TES::GetSingleton(); TES) {
112-
const auto formType = static_cast<RE::FormType>(RE::FormType::NPC);
136+
const auto formType = RE::FormType::NPC;
113137

114138
TES->ForEachReferenceInRange(a_origin, a_radius, [&](RE::TESObjectREFR& a_ref) {
115-
auto base = a_ref.GetBaseObject();
116-
if (a_ref.As<RE::TESObjectREFR>() != a_origin && (a_ref.Is(formType) || base && base->Is(formType))) {
117-
if (auto actor = a_ref.As<RE::Actor>(); actor) {
118-
if (actor->IsSummoned()) {
119-
vec.push_back(actor);
120-
}
121-
}
122-
}
123-
return true;
139+
auto base = a_ref.GetBaseObject();
140+
if (a_ref.As<RE::TESObjectREFR>() != a_origin && (a_ref.Is(formType) || base && base->Is(formType))) {
141+
if (auto actor = a_ref.As<RE::Actor>(); actor) {
142+
if (actor->IsSummoned()) {
143+
vec.push_back(actor);
144+
}
145+
}
146+
}
147+
return true;
124148
});
125149
}
126150

@@ -133,9 +157,8 @@ namespace PapyrusObjectREFR
133157

134158
if (const auto TES = RE::TES::GetSingleton(); TES) {
135159
if (const auto player = RE::PlayerCharacter::GetSingleton(); player) {
136-
137160
if (player->teammateCount > 0) {
138-
const auto formType = static_cast<RE::FormType>(RE::FormType::NPC);
161+
const auto formType = RE::FormType::NPC;
139162

140163
TES->ForEachReferenceInRange(player, a_radius, [&](RE::TESObjectREFR& a_ref) {
141164
auto base = a_ref.GetBaseObject();
@@ -155,6 +178,51 @@ namespace PapyrusObjectREFR
155178
return vec;
156179
}
157180

181+
auto TryAffixDisplayName(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::TESObjectREFR* a_ref, RE::BGSMessage* a_message, bool a_prepend, bool a_force) -> bool
182+
{
183+
if (!a_ref) {
184+
a_vm->TraceStack("akRef cannot be None", a_stackID, Severity::kInfo);
185+
return false;
186+
}
187+
188+
if (!a_message) {
189+
a_vm->TraceStack("akMessage cannot be None", a_stackID, Severity::kInfo);
190+
return false;
191+
}
192+
193+
const std::string_view displayName = a_ref->GetDisplayFullName();
194+
195+
if (displayName.empty()) {
196+
return false;
197+
}
198+
199+
const std::string_view affix = a_message->GetFullName();
200+
201+
if (affix.empty()) {
202+
return false;
203+
}
204+
205+
std::string_view fullName = displayName;
206+
207+
if (const auto base = a_ref->GetBaseObject(); base) {
208+
fullName = base->GetName();
209+
}
210+
211+
if (a_prepend && !displayName.starts_with(affix)) {
212+
std::stringstream ss;
213+
ss << affix << ' ' << fullName;
214+
return a_ref->SetDisplayName(ss.str(), a_force);
215+
}
216+
217+
if (!a_prepend && !displayName.ends_with(affix)) {
218+
std::stringstream ss;
219+
ss << fullName << ' ' << affix;
220+
return a_ref->SetDisplayName(ss.str(), a_force);
221+
}
222+
223+
return false;
224+
}
225+
158226
auto RegisterFuncs(VM* a_vm) -> bool
159227
{
160228
if (!a_vm) {
@@ -163,10 +231,12 @@ namespace PapyrusObjectREFR
163231
}
164232

165233
a_vm->RegisterFunction("FindNearbyActors", PROJECT_NAME, FindNearbyActors);
234+
a_vm->RegisterFunction("FindNearbyBooks", PROJECT_NAME, FindNearbyBooks);
166235
a_vm->RegisterFunction("FindNearbyCommandedActors", PROJECT_NAME, FindNearbyCommandedActors);
167236
a_vm->RegisterFunction("FindNearbyFollowers", PROJECT_NAME, FindNearbyFollowers);
168237
a_vm->RegisterFunction("FindNearbySummons", PROJECT_NAME, FindNearbySummons);
169238
a_vm->RegisterFunction("FindNearbyTeammates", PROJECT_NAME, FindNearbyTeammates);
239+
a_vm->RegisterFunction("TryAffixDisplayName", PROJECT_NAME, TryAffixDisplayName);
170240

171241
return true;
172242
}

src/Papyrus/PapyrusObjectREFR.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ namespace PapyrusObjectREFR
99
using Severity = RE::BSScript::ErrorLogger::Severity;
1010

1111
auto FindNearbyActors(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::TESObjectREFR* a_origin, float a_radius) -> std::vector<RE::Actor*>;
12+
auto FindNearbyBooks(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::TESObjectREFR* a_origin, float a_radius) -> std::vector<RE::TESObjectREFR*>;
1213
auto FindNearbyCommandedActors(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::TESObjectREFR* a_origin, float a_radius) -> std::vector<RE::Actor*>;
1314
auto FindNearbyFollowers(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, float a_radius) -> std::vector<RE::Actor*>;
1415
auto FindNearbySummons(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::TESObjectREFR* a_origin, float a_radius) -> std::vector<RE::Actor*>;
1516
auto FindNearbyTeammates(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, float a_radius) -> std::vector<RE::Actor*>;
17+
auto TryAffixDisplayName(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::TESObjectREFR* a_ref, RE::BGSMessage* a_message, bool a_prepend, bool a_force) -> bool;
1618

1719
auto RegisterFuncs(VM* a_vm) -> bool;
1820
}

src/Papyrus/PapyrusString.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,4 @@ namespace PapyrusString
1717
auto WrapString(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, RE::BSFixedString a_source, std::int32_t a_max_length) -> RE::BSFixedString;
1818

1919
auto RegisterFuncs(VM* a_vm) -> bool;
20-
2120
}

0 commit comments

Comments
 (0)