styyx-util 1.0
Utility Header for SKSE Plugin development
Loading...
Searching...
No Matches
st-actor.h
Go to the documentation of this file.
1#pragma once
2#include <vector>
3
4#include "st-misc.h"
5
6namespace StyyxUtil
7{
8
10{
14 static void GetPerksFromBaseActor(RE::Actor* a_actor, std::vector<RE::BGSPerk*>& perks)
15 {
16 if (!a_actor)
17 return;
18
19 const auto base = a_actor->GetActorBase();
20 if (!base)
21 return;
22
23 const uint32_t perk_count = base->perkCount;
24 for (uint32_t i = 0; i < perk_count; i++)
25 {
26 RE::BGSPerk* perk = base->perks[i].perk;
27 if (!perk)
28 continue;
29 perks.push_back(perk);
30 }
31 }
32
36 static bool ActorHasEquippedHeavyArmor(RE::Actor *actor)
37 {
38 if (!actor)
39 return false;
40 const auto proc = actor->currentProcess;
41 if (!proc)
42 return false;
43
44 for (auto& [object, slot] : proc->equippedForms)
45 {
46 if (object)
47 {
48 if (const auto armor = object->As<RE::TESObjectARMO>())
49 {
50 if (armor->IsHeavyArmor())
51 {
52 return true;
53 }
54 }
55 }
56 }
57 return false;
58 }
59
62 static bool ActorHasEquippedLightArmor(const RE::Actor *actor)
63 {
64 if (!actor)
65 return false;
66 const auto proc = actor->currentProcess;
67 if (!proc)
68 return false;
69
70 for (auto& [object, slot] : proc->equippedForms)
71 {
72 if (object)
73 {
74 if (const auto armor = object->As<RE::TESObjectARMO>())
75 {
76 if (!armor->IsHeavyArmor())
77 {
78 return true;
79 }
80 }
81 }
82 }
83 return false;
84 }
85
90 static bool IsInOpportunityState(RE::Actor *victim, const RE::Actor *attacker)
91 {
92 return IsPowerAttacking(victim) || victim->IsStaggering() ||
93 victim->actorState1.sitSleepState == RE::SIT_SLEEP_STATE::kIsSitting ||
94 victim->actorState1.sitSleepState == RE::SIT_SLEEP_STATE::kIsSleeping ||
95 (victim->GetHeadingAngle(attacker->GetPosition(), false) <= -135 ||
96 victim->GetHeadingAngle(attacker->GetPosition(), false) >= 135) ||
97 ActorHasEffectOfTypeActive(victim, RE::EffectArchetypes::ArchetypeID::kParalysis) ||
98 ActorHasEffectOfTypeActive(victim, RE::EffectArchetypes::ArchetypeID::kCalm);
99 }
100
104 static bool ActorHasQuestObjectInHand(RE::Actor *actor)
105 {
106 if (actor)
107 {
108 if (const auto *rightHandItem = actor->GetEquippedEntryData(false))
109 {
110 if (rightHandItem->IsQuestObject())
111 {
112 return true;
113 }
114 }
115
116 if (const auto *leftHandItem = actor->GetEquippedEntryData(true))
117 {
118 if (leftHandItem->IsQuestObject())
119 {
120 return true;
121 }
122 }
123 }
124 return false;
125 };
126
130 static bool IsVampire(RE::Actor *a_ref)
131 {
132 if (a_ref->HasKeywordWithType(RE::DEFAULT_OBJECT::kKeywordVampire))
133 {
134 return true;
135 }
136 return false;
137 }
138
142 static bool IsUndead(RE::Actor *a_ref)
143 {
144 if (a_ref->HasKeywordWithType(RE::DEFAULT_OBJECT::kKeywordUndead))
145 return true;
146 return false;
147 }
148
155 static bool IsDragon(const RE::Actor *a_actor)
156 {
157 if (a_actor->HasKeywordWithType(RE::DEFAULT_OBJECT::kKeywordDragon))
158 {
159 return true;
160 }
161 const auto key = RE::TESForm::LookupByEditorID<RE::BGSKeyword>("ActorTypeDragon");
162 if (!key)
163 return false;
164 return a_actor->HasKeyword(key);
165 }
166
167
170 static RE::TESObjectCELL *GetPlayerCell()
171 {
172 const auto player = RE::PlayerCharacter::GetSingleton();
173
174 auto cell = player->GetParentCell();
175 if (!cell)
176 {
177 cell = player->GetSaveParentCell();
178 }
179 return cell;
180 }
181
185 static float GetCurrentLightLevel(const RE::Actor *a_actor)
186 {
187 if (!a_actor)
188 return 0.0f;
189
190 const auto process = a_actor->GetHighProcess();
191 return process ? process->lightLevel : 0.0f;
192 }
193
197 {
198 const auto player = RE::PlayerCharacter::GetSingleton();
199 return GetCurrentLightLevel(player);
200 }
201
207
208 static void TryStagger(RE::Actor* a_target, float a_staggerMult, RE::Actor* a_aggressor) {
209 using func_t = decltype(&TryStagger);
210 static REL::Relocation<func_t> func{ RELOCATION_ID(36700, 37710) };
211 func(a_target, a_staggerMult, a_aggressor);
212 }
213
218
219 static bool StartsDead(const RE::Actor *actor)
220 {
221 return actor && (actor->formFlags & RE::Actor::RecordFlags::kStartsDead);
222 }
223
224
229
230 static float GetMaxHealth(RE::Actor *a_actor)
231 {
232 return a_actor->GetActorValueModifier(RE::ACTOR_VALUE_MODIFIER::kTemporary, RE::ActorValue::kHealth) +
233 a_actor->GetPermanentActorValue(RE::ActorValue::kHealth);
234 }
235
239 static float GetMaxStamina(RE::Actor *actor)
240 {
241 return actor->GetActorValueModifier(RE::ACTOR_VALUE_MODIFIER::kTemporary, RE::ActorValue::kStamina) +
242 actor->GetPermanentActorValue(RE::ActorValue::kStamina);
243 }
244
248 static float GetMaxMagicka(RE::Actor *actor)
249 {
250 return actor->GetActorValueModifier(RE::ACTOR_VALUE_MODIFIER::kTemporary, RE::ActorValue::kMagicka) +
251 actor->GetPermanentActorValue(RE::ActorValue::kMagicka);
252 }
253
256 static void FullyHealActor(RE::Actor *a_actor)
257 {
258 a_actor->RestoreActorValue(RE::ActorValue::kHealth, GetMaxHealth(a_actor));
259 a_actor->RestoreActorValue(RE::ActorValue::kStamina, GetMaxStamina(a_actor));
260 a_actor->RestoreActorValue(RE::ActorValue::kMagicka, GetMaxMagicka(a_actor));
261 }
262
267 static bool ActorHasEffectOfTypeActive(RE::Actor *a_actor, RE::EffectArchetypes::ArchetypeID a_type)
268 {
269 if (!a_actor)
270 return false;
271
272 const auto& activeEffects = a_actor->GetActiveEffectList();
273 if (!activeEffects)
274 return false;
275
276 return std::ranges::any_of(*activeEffects, [a_type](const RE::ActiveEffect* effect) -> bool
277 {
278 if (!effect || effect->flags.any(RE::ActiveEffect::Flag::kInactive)) return false;
279 const auto base = effect->GetBaseObject();
280 return base && base->HasArchetype(a_type);
281 });
282 }
283
288 static bool HasEffectWithKeywordActive(RE::Actor *a_actor, const std::string_view a_keyword)
289 {
290 if (!a_actor || a_keyword.empty() || !a_actor->Is3DLoaded())
291 {
292 return false;
293 }
294 const auto& activeEffects = a_actor->GetActiveEffectList();
295 if (!activeEffects)
296 return false;
297
298 if (const auto key = RE::TESForm::LookupByEditorID<RE::BGSKeyword>(a_keyword)) {
299 return std::ranges::any_of(*activeEffects, [key](const RE::ActiveEffect* effect) -> bool {
300 if (!effect || effect->flags.any(RE::ActiveEffect::Flag::kInactive)) return false;
301 const auto base = effect->GetBaseObject();
302 return base && base->HasKeyword(key);
303 });
304 }
305 return std::ranges::any_of(*activeEffects, [a_keyword](const RE::ActiveEffect* effect) -> bool {
306 if (!effect || effect->flags.any(RE::ActiveEffect::Flag::kInactive)) return false;
307 const auto base = effect->GetBaseObject();
308 return base && base->HasKeywordString(a_keyword);
309 });
310 }
311
312
317 static bool IsEffectActive(RE::Actor *a_actor, const RE::EffectSetting *a_effect)
318 {
319 if (!a_actor || !a_effect)
320 {
321 return false;
322 }
323 const auto& activeEffects = a_actor->GetActiveEffectList();
324 if (!activeEffects)
325 return false;
326
327 return std::ranges::any_of(*activeEffects, [a_effect](const RE::ActiveEffect* effect) -> bool
328 {
329 if (!effect || effect->flags.any(RE::ActiveEffect::Flag::kInactive))
330 return false;
331 const auto base = effect->GetBaseObject();
332 return base == a_effect;
333 });
334 }
335
342 static void AddItem(RE::Actor *a_actor, RE::TESBoundObject *a_item, RE::ExtraDataList *a_extraList, int a_count,
343 RE::TESObjectREFR *a_fromRefr)
344 {
345 using func_t = decltype(AddItem);
346 static REL::Relocation<func_t> func{RELOCATION_ID(36525,37525)};
347 return func(a_actor, a_item, a_extraList, a_count, a_fromRefr);
348 }
349
353 static void AddItemPlayer(RE::TESBoundObject *a_item, const int a_count)
354 {
355 return AddItem(RE::PlayerCharacter::GetSingleton(), a_item, nullptr, a_count, nullptr);
356 }
357
362 static int RemoveItemPlayer(RE::TESBoundObject *item, int count)
363 {
364 using func_t = decltype(RemoveItemPlayer);
365 static REL::Relocation<func_t> func{RELOCATION_ID(16564, 16919)};
366 return func(item, count);
367 }
368
373 static bool ActorHasItem(RE::Actor* actor, RE::TESBoundObject* item) {
374 if (!actor || !item)
375 return false;
376
377 const auto& inv = actor->GetInventory();
378 const auto it = inv.find(item);
379 return it != inv.end() && it->second.first > 0;
380 }
381
386 static RE::TESObjectWEAP* GetWieldingWeapon(RE::Actor *a_actor)
387 {
388 if (const auto weapon = a_actor->GetAttackingWeapon())
389 {
390 const auto obj = weapon->object;
391 return obj ? obj->As<RE::TESObjectWEAP>() : nullptr;
392 }
393 if (const auto rhs = a_actor->GetEquippedObject(false); rhs && rhs->IsWeapon())
394 {
395 return rhs->As<RE::TESObjectWEAP>();
396 }
397 if (const auto lhs = a_actor->GetEquippedObject(true); lhs && lhs->IsWeapon())
398 {
399 return lhs->As<RE::TESObjectWEAP>();
400 }
401
402 return nullptr;
403 }
404
407 static RE::Actor* GetPlayerMount()
408 {
409 RE::NiPointer<RE::Actor> currentMount;
410 if ( RE::PlayerCharacter::GetSingleton()->GetMount(currentMount)) {
411 return currentMount.get();
412 }
413 return nullptr;
414 }
415
419 static bool IsPowerAttacking(const RE::Actor *actor)
420 {
421 const auto high = actor->GetHighProcess();
422 if (!high)
423 return false;
424
425 const auto &attackData = high->attackData;
426 if (!attackData)
427 return false;
428
429 return attackData->data.flags.any(RE::AttackData::AttackFlag::kPowerAttack);
430 }
431
435 static bool IsBashing(const RE::Actor* actor) {
436
437 const auto high = actor->GetHighProcess();
438
439 if (!high)
440 return false;
441
442 if (actor->GetAttackState() == RE::ATTACK_STATE_ENUM::kNone)
443 return false;
444
445 const auto& attackData = high->attackData;
446 if (!attackData)
447 return false;
448
449 return attackData->data.flags.any(RE::AttackData::AttackFlag::kBashAttack);
450 }
451
452
459 static std::vector<RE::Actor*> GetNearbyActors(const RE::TESObjectREFR* a_ref, const float a_radius, const bool a_ignorePlayer)
460 {
461 std::vector<RE::Actor*> result;
462 const auto processLists = RE::ProcessLists::GetSingleton();
463 if (!processLists)
464 return result;
465
466 if (a_ignorePlayer && processLists->numberHighActors == 0)
467 return result;
468
469 const auto squaredRadius = a_radius * a_radius;
470 const auto originPos = a_ref->GetPosition();
471
472 result.reserve(processLists->numberHighActors);
473
474 const auto get_actor_within_radius = [&](RE::Actor* a_actor) {
475 if (a_actor && a_actor != a_ref && originPos.GetSquaredDistance(a_actor->GetPosition()) <= squaredRadius) {
476 result.emplace_back(a_actor);
477 }
478 };
479 for (auto& actorHandle : processLists->highActorHandles) {
480 const auto actor = actorHandle.get();
481 get_actor_within_radius(actor.get());
482 }
483
484 if (!a_ignorePlayer) {
485 get_actor_within_radius(RE::PlayerCharacter::GetSingleton());
486 }
487
488 return result;
489 }
490
495 static std::vector<RE::Actor*> GetNearbyNonPlayerTeammates(const RE::TESObjectREFR* a_ref, const float a_radius)
496 {
497 auto actors = GetNearbyActors(a_ref, a_radius, true);
498 std::erase_if(actors, [](const RE::Actor* a) { return a->IsPlayerTeammate(); });
499 return actors;
500 }
501
502
507 static bool IsGuardNearby(const RE::TESObjectREFR* a_ref, const float a_radius)
508 {
509 return std::ranges::any_of(GetNearbyActors(a_ref, a_radius, false), [](const RE::Actor* a) {
510 return a && a->IsGuard();
511 });
512 }
513
518 static RE::Actor* GetClosestActor(const RE::TESObjectREFR* a_ref, const float a_radius)
519 {
520 const auto nearby_actors = GetNearbyActors(a_ref, a_radius, false);
521 return GetClosestFromVector(a_ref, nearby_actors);
522 }
523
528 static RE::Actor* GetClosestNonPlayerTeammate(const RE::TESObjectREFR* a_ref, const float a_radius)
529 {
530 const auto nearby_actors = GetNearbyNonPlayerTeammates(a_ref, a_radius);
531 return GetClosestFromVector(a_ref, nearby_actors);
532 }
533
538 static void SetNPCLevel(RE::Actor *actor, uint16_t level)
539 {
540 MiscUtil::RunConsoleCommandOnRef(actor, std::format("SetLevel {}", level));
541 }
542
548 static float GetActorValuePercentage(RE::Actor* a_actor, RE::ActorValue a_av)
549 {
550 using func_t = decltype(&GetActorValuePercentage);
551 static REL::Relocation<func_t> func{ RELOCATION_ID(0, 37337) };
552 return func(a_actor, a_av);
553 }
554
555private:
560 static RE::Actor* GetClosestFromVector(const RE::TESObjectREFR* a_ref, const std::vector<RE::Actor*>& actors)
561 {
562 if (actors.empty()) return nullptr;
563 const auto originPos = a_ref->GetPosition();
564 const auto it = std::ranges::min_element(actors, [&](RE::Actor* a, RE::Actor* b) {
565 return originPos.GetSquaredDistance(a->GetPosition()) < originPos.GetSquaredDistance(b->GetPosition());
566 });
567 return it != actors.end() ? *it : nullptr;
568 }
569};
570
571} // namespace StyyxUtil
Definition st-actor.h:7
Definition st-actor.h:10
static void AddItemPlayer(RE::TESBoundObject *a_item, const int a_count)
Helper function to add an item to the player.
Definition st-actor.h:353
static float GetMaxHealth(RE::Actor *a_actor)
Get maximum Health of an actor.
Definition st-actor.h:230
static bool HasEffectWithKeywordActive(RE::Actor *a_actor, const std::string_view a_keyword)
Check if an actor has an effect active with a specific keyword.
Definition st-actor.h:288
static bool StartsDead(const RE::Actor *actor)
Check if actor is supposed to spawn dead.
Definition st-actor.h:219
static bool ActorHasEquippedHeavyArmor(RE::Actor *actor)
Check if an actor has any heavy armor equipped. It returning false does not mean the actor has light ...
Definition st-actor.h:36
static RE::Actor * GetClosestActor(const RE::TESObjectREFR *a_ref, const float a_radius)
Get the closest actor within a radius.
Definition st-actor.h:518
static RE::Actor * GetClosestFromVector(const RE::TESObjectREFR *a_ref, const std::vector< RE::Actor * > &actors)
Helper function to get the closest actor from a vector.
Definition st-actor.h:560
static std::vector< RE::Actor * > GetNearbyNonPlayerTeammates(const RE::TESObjectREFR *a_ref, const float a_radius)
Search all non-teammates within a certain radius.
Definition st-actor.h:495
static bool IsGuardNearby(const RE::TESObjectREFR *a_ref, const float a_radius)
Check if any actor within a certain radius is a guard.
Definition st-actor.h:507
static bool IsVampire(RE::Actor *a_ref)
Check if actor is vampire.
Definition st-actor.h:130
static bool IsBashing(const RE::Actor *actor)
Check if an actor is shield bashing.
Definition st-actor.h:435
static RE::Actor * GetClosestNonPlayerTeammate(const RE::TESObjectREFR *a_ref, const float a_radius)
Get the closest non-player teammate within a radius of a reference.
Definition st-actor.h:528
static bool IsInOpportunityState(RE::Actor *victim, const RE::Actor *attacker)
Very specific function used to see if an Actor is in a state an attack of opportunity should be possi...
Definition st-actor.h:90
static bool ActorHasItem(RE::Actor *actor, RE::TESBoundObject *item)
Check if actor has the specified item in the inventory.
Definition st-actor.h:373
static RE::TESObjectWEAP * GetWieldingWeapon(RE::Actor *a_actor)
Get the weapon a character is currently wielding.
Definition st-actor.h:386
static float GetCurrentLightLevelPlayer()
Get the light level the player is standing in.
Definition st-actor.h:196
static float GetMaxMagicka(RE::Actor *actor)
Get maximum Magicka of an actor.
Definition st-actor.h:248
static int RemoveItemPlayer(RE::TESBoundObject *item, int count)
Remove item from the player.
Definition st-actor.h:362
static void SetNPCLevel(RE::Actor *actor, uint16_t level)
Runs the console command SetLevel to set an actor's level to the amount.
Definition st-actor.h:538
static std::vector< RE::Actor * > GetNearbyActors(const RE::TESObjectREFR *a_ref, const float a_radius, const bool a_ignorePlayer)
Get all nearby actors.
Definition st-actor.h:459
static void AddItem(RE::Actor *a_actor, RE::TESBoundObject *a_item, RE::ExtraDataList *a_extraList, int a_count, RE::TESObjectREFR *a_fromRefr)
Add item to actor.
Definition st-actor.h:342
static bool ActorHasEquippedLightArmor(const RE::Actor *actor)
Check if an actor has any light armor equipped. It returning false does not mean the actor has heavy ...
Definition st-actor.h:62
static bool IsDragon(const RE::Actor *a_actor)
Check if actor is a dragon.
Definition st-actor.h:155
static void FullyHealActor(RE::Actor *a_actor)
Fully heals all 3 attributes of an actor.
Definition st-actor.h:256
static RE::Actor * GetPlayerMount()
Get the player mount.
Definition st-actor.h:407
static float GetMaxStamina(RE::Actor *actor)
Get maximum Stamina of an actor.
Definition st-actor.h:239
static void TryStagger(RE::Actor *a_target, float a_staggerMult, RE::Actor *a_aggressor)
Try to stagger a target.
Definition st-actor.h:208
static bool ActorHasQuestObjectInHand(RE::Actor *actor)
Check if actor has Quest item as weapon.
Definition st-actor.h:104
static float GetActorValuePercentage(RE::Actor *a_actor, RE::ActorValue a_av)
Get the actor value percentage of an actor.
Definition st-actor.h:548
static bool IsUndead(RE::Actor *a_ref)
Check if actor is undead.
Definition st-actor.h:142
static bool ActorHasEffectOfTypeActive(RE::Actor *a_actor, RE::EffectArchetypes::ArchetypeID a_type)
Check if actor has an effect of a specific type active.
Definition st-actor.h:267
static float GetCurrentLightLevel(const RE::Actor *a_actor)
Get the current light level the actor is standing in.
Definition st-actor.h:185
static RE::TESObjectCELL * GetPlayerCell()
Get the current cell the player is in.
Definition st-actor.h:170
static void GetPerksFromBaseActor(RE::Actor *a_actor, std::vector< RE::BGSPerk * > &perks)
Fills a vector<RE::BGSPerk*> reference with all the perks an actor inherits from the actor base.
Definition st-actor.h:14
static bool IsEffectActive(RE::Actor *a_actor, const RE::EffectSetting *a_effect)
Check if an actor has a specific effect active.
Definition st-actor.h:317
static bool IsPowerAttacking(const RE::Actor *actor)
Check if an actor is power attacking.
Definition st-actor.h:419
static void RunConsoleCommandOnRef(RE::TESObjectREFR *a_target, const std::string_view a_command)
Run console command on reference.
Definition st-misc.h:114