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{
11
14 static void InterruptAttack(RE::Actor* a_actor)
15 {
16
17 if (!a_actor)
18 return;
19
20 a_actor->NotifyAnimationGraph("attackStop");
21 a_actor->NotifyAnimationGraph("blockStop");
22 }
23
27 static bool IsMoving(const RE::Actor* a_actor)
28 {
29 if (!a_actor)
30 {
31 return false;
32 }
33
34 return (static_cast<bool>(a_actor->actorState1.movingForward) ||
35 static_cast<bool>(a_actor->actorState1.movingBack) ||
36 static_cast<bool>(a_actor->actorState1.movingLeft) ||
37 static_cast<bool>(a_actor->actorState1.movingRight));
38 }
39
44 [[nodiscard]] static bool IsGod(RE::Actor* a_actor)
45 {
46 if (!a_actor)
47 return false;
48 return a_actor->IsPlayerRef() && RE::PlayerCharacter::IsGodMode();
49 }
50
55 static bool CasterIsCastingObject(RE::ActorMagicCaster* a_caster)
56 {
57 if (!a_caster)
58 return false;
59 using ct = RE::MagicSystem::CastingType;
60 switch (const auto type = a_caster->currentSpell->GetCastingType())
61 {
62 case ct::kScroll:
63 return true;
64 default:
65 return false;
66 }
67 }
68
73 static void GetPerksFromBaseActor(RE::Actor* a_actor, std::vector<RE::BGSPerk*>& perks)
74 {
75 if (!a_actor)
76 return;
77
78 const auto base = a_actor->GetActorBase();
79 if (!base)
80 return;
81
82 const uint32_t perk_count = base->perkCount;
83 for (uint32_t i = 0; i < perk_count; i++)
84 {
85 RE::BGSPerk* perk = base->perks[i].perk;
86 if (!perk)
87 continue;
88 perks.push_back(perk);
89 }
90 }
91
96 static bool ActorHasEquippedHeavyArmor(RE::Actor* actor)
97 {
98 if (!actor)
99 return false;
100 const auto proc = actor->currentProcess;
101 if (!proc)
102 return false;
103
104 for (auto& [object, slot] : proc->equippedForms)
105 {
106 if (object)
107 {
108 if (const auto armor = object->As<RE::TESObjectARMO>())
109 {
110 if (armor->IsHeavyArmor())
111 {
112 return true;
113 }
114 }
115 }
116 }
117 return false;
118 }
119
123 static bool ActorHasEquippedLightArmor(const RE::Actor* actor)
124 {
125 if (!actor)
126 return false;
127 const auto proc = actor->currentProcess;
128 if (!proc)
129 return false;
130
131 for (auto& [object, slot] : proc->equippedForms)
132 {
133 if (object)
134 {
135 if (const auto armor = object->As<RE::TESObjectARMO>())
136 {
137 if (!armor->IsHeavyArmor())
138 {
139 return true;
140 }
141 }
142 }
143 }
144 return false;
145 }
146
153 static bool IsInOpportunityState(RE::Actor* victim, const RE::Actor* attacker)
154 {
155
156 if (!victim || !attacker)
157 {
158 return false;
159 }
160
161 return IsPowerAttacking(victim) || victim->IsStaggering() ||
162 victim->actorState1.sitSleepState == RE::SIT_SLEEP_STATE::kIsSitting ||
163 victim->actorState1.sitSleepState == RE::SIT_SLEEP_STATE::kIsSleeping ||
164 (victim->GetHeadingAngle(attacker->GetPosition(), false) <= -135 ||
165 victim->GetHeadingAngle(attacker->GetPosition(), false) >= 135) ||
166 ActorHasEffectOfTypeActive(victim, RE::EffectArchetypes::ArchetypeID::kParalysis) ||
167 ActorHasEffectOfTypeActive(victim, RE::EffectArchetypes::ArchetypeID::kCalm);
168 }
169
173 static bool ActorHasQuestObjectInHand(RE::Actor* actor)
174 {
175 if (actor)
176 {
177 if (const auto* rightHandItem = actor->GetEquippedEntryData(false))
178 {
179 if (rightHandItem->IsQuestObject())
180 {
181 return true;
182 }
183 }
184
185 if (const auto* leftHandItem = actor->GetEquippedEntryData(true))
186 {
187 if (leftHandItem->IsQuestObject())
188 {
189 return true;
190 }
191 }
192 }
193 return false;
194 };
195
199 static bool IsVampire(RE::Actor* a_ref)
200 {
201
202 if (!a_ref)
203 {
204 return false;
205 }
206
207 if (a_ref->HasKeywordWithType(RE::DEFAULT_OBJECT::kKeywordVampire))
208 {
209 return true;
210 }
211 return false;
212 }
213
217 static bool IsUndead(RE::Actor* a_ref)
218 {
219
220 if (!a_ref)
221 {
222 return false;
223 }
224
225 if (a_ref->HasKeywordWithType(RE::DEFAULT_OBJECT::kKeywordUndead))
226 return true;
227 return false;
228 }
229
237 static bool IsDragon(const RE::Actor* a_actor)
238 {
239
240 if (!a_actor)
241 {
242 return false;
243 }
244
245 if (a_actor->HasKeywordWithType(RE::DEFAULT_OBJECT::kKeywordDragon))
246 {
247 return true;
248 }
249 const auto key = RE::TESForm::LookupByEditorID<RE::BGSKeyword>("ActorTypeDragon");
250 if (!key)
251 return false;
252 return a_actor->HasKeyword(key);
253 }
254
257 static RE::TESObjectCELL* GetPlayerCell()
258 {
259 const auto player = RE::PlayerCharacter::GetSingleton();
260
261 auto cell = player->GetParentCell();
262 if (!cell)
263 {
264 cell = player->GetSaveParentCell();
265 }
266 return cell;
267 }
268
272 static float GetCurrentLightLevel(const RE::Actor* a_actor)
273 {
274 if (!a_actor)
275 return 0.0f;
276
277 const auto process = a_actor->GetHighProcess();
278 return process ? process->lightLevel : 0.0f;
279 }
280
284 {
285 const auto player = RE::PlayerCharacter::GetSingleton();
286 return GetCurrentLightLevel(player);
287 }
288
295
296 static void TryStagger(RE::Actor* a_target, float a_staggerMult, RE::Actor* a_aggressor)
297 {
298 using func_t = decltype(&TryStagger);
299 static REL::Relocation<func_t> func{RELOCATION_ID(36700, 37710)};
300 func(a_target, a_staggerMult, a_aggressor);
301 }
302
308
309 static bool StartsDead(const RE::Actor* actor)
310 {
311 return actor && (actor->formFlags & RE::Actor::RecordFlags::kStartsDead);
312 }
313
320
321 static float GetMaxHealth(RE::Actor* a_actor)
322 {
323
324 if (!a_actor)
325 {
326 return 0.f;
327 }
328
329 return a_actor->GetActorValueModifier(RE::ACTOR_VALUE_MODIFIER::kTemporary, RE::ActorValue::kHealth) +
330 a_actor->GetPermanentActorValue(RE::ActorValue::kHealth);
331 }
332
336 static float GetMaxStamina(RE::Actor* actor)
337 {
338
339 if (!a_actor)
340 {
341 return 0.f;
342 }
343
344 return actor->GetActorValueModifier(RE::ACTOR_VALUE_MODIFIER::kTemporary, RE::ActorValue::kStamina) +
345 actor->GetPermanentActorValue(RE::ActorValue::kStamina);
346 }
347
351 static float GetMaxMagicka(RE::Actor* actor)
352 {
353
354 if (!a_actor)
355 {
356 return 0.f;
357 }
358
359 return actor->GetActorValueModifier(RE::ACTOR_VALUE_MODIFIER::kTemporary, RE::ActorValue::kMagicka) +
360 actor->GetPermanentActorValue(RE::ActorValue::kMagicka);
361 }
362
365 static void FullyHealActor(RE::Actor* a_actor)
366 {
367
368 if (!a_actor)
369 {
370 return;
371 }
372
373 a_actor->RestoreActorValue(RE::ActorValue::kHealth, GetMaxHealth(a_actor));
374 a_actor->RestoreActorValue(RE::ActorValue::kStamina, GetMaxStamina(a_actor));
375 a_actor->RestoreActorValue(RE::ActorValue::kMagicka, GetMaxMagicka(a_actor));
376 }
377
382 static bool ActorHasEffectOfTypeActive(RE::Actor* a_actor, RE::EffectArchetypes::ArchetypeID a_type)
383 {
384 if (!a_actor)
385 return false;
386
387 const auto& activeEffects = a_actor->GetActiveEffectList();
388 if (!activeEffects)
389 return false;
390
391 return std::ranges::any_of(*activeEffects,
392 [a_type](const RE::ActiveEffect* effect) -> bool
393 {
394 if (!effect || effect->flags.any(RE::ActiveEffect::Flag::kInactive))
395 return false;
396 const auto base = effect->GetBaseObject();
397 return base && base->HasArchetype(a_type);
398 });
399 }
400
405 static bool HasEffectWithKeywordActive(RE::Actor* a_actor, const std::string_view a_keyword)
406 {
407 if (!a_actor || a_keyword.empty() || !a_actor->Is3DLoaded())
408 {
409 return false;
410 }
411 const auto& activeEffects = a_actor->GetActiveEffectList();
412 if (!activeEffects)
413 return false;
414
415 if (const auto key = RE::TESForm::LookupByEditorID<RE::BGSKeyword>(a_keyword))
416 {
417 return std::ranges::any_of(*activeEffects,
418 [key](const RE::ActiveEffect* effect) -> bool
419 {
420 if (!effect || effect->flags.any(RE::ActiveEffect::Flag::kInactive))
421 return false;
422 const auto base = effect->GetBaseObject();
423 return base && base->HasKeyword(key);
424 });
425 }
426 return std::ranges::any_of(*activeEffects,
427 [a_keyword](const RE::ActiveEffect* effect) -> bool
428 {
429 if (!effect || effect->flags.any(RE::ActiveEffect::Flag::kInactive))
430 return false;
431 const auto base = effect->GetBaseObject();
432 return base && base->HasKeywordString(a_keyword);
433 });
434 }
435
440 static bool IsEffectActive(RE::Actor* a_actor, const RE::EffectSetting* a_effect)
441 {
442 if (!a_actor || !a_effect)
443 {
444 return false;
445 }
446 const auto& activeEffects = a_actor->GetActiveEffectList();
447 if (!activeEffects)
448 return false;
449
450 return std::ranges::any_of(*activeEffects,
451 [a_effect](const RE::ActiveEffect* effect) -> bool
452 {
453 if (!effect || effect->flags.any(RE::ActiveEffect::Flag::kInactive))
454 return false;
455 const auto base = effect->GetBaseObject();
456 return base == a_effect;
457 });
458 }
459
466 static void AddItem(RE::Actor* a_actor, RE::TESBoundObject* a_item, RE::ExtraDataList* a_extraList, int a_count,
467 RE::TESObjectREFR* a_fromRefr)
468 {
469 using func_t = decltype(AddItem);
470 static REL::Relocation<func_t> func{RELOCATION_ID(36525, 37525)};
471 return func(a_actor, a_item, a_extraList, a_count, a_fromRefr);
472 }
473
477 static void AddItemPlayer(RE::TESBoundObject* a_item, const int a_count)
478 {
479 return AddItem(RE::PlayerCharacter::GetSingleton(), a_item, nullptr, a_count, nullptr);
480 }
481
486 static int RemoveItemPlayer(RE::TESBoundObject* item, int count)
487 {
488 using func_t = decltype(RemoveItemPlayer);
489 static REL::Relocation<func_t> func{RELOCATION_ID(16564, 16919)};
490 return func(item, count);
491 }
492
497 static bool ActorHasItem(RE::Actor* actor, RE::TESBoundObject* item)
498 {
499 if (!actor || !item)
500 return false;
501
502 const auto& inv = actor->GetInventory();
503 const auto it = inv.find(item);
504 return it != inv.end() && it->second.first > 0;
505 }
506
513 static RE::TESObjectWEAP* GetWieldingWeapon(RE::Actor* a_actor)
514 {
515
516 if (!a_actor)
517 {
518 return nullptr;
519 }
520
521 if (const auto weapon = a_actor->GetAttackingWeapon())
522 {
523 const auto obj = weapon->object;
524 return obj ? obj->As<RE::TESObjectWEAP>() : nullptr;
525 }
526 if (const auto rhs = a_actor->GetEquippedObject(false); rhs && rhs->IsWeapon())
527 {
528 return rhs->As<RE::TESObjectWEAP>();
529 }
530 if (const auto lhs = a_actor->GetEquippedObject(true); lhs && lhs->IsWeapon())
531 {
532 return lhs->As<RE::TESObjectWEAP>();
533 }
534
535 return nullptr;
536 }
537
540 static RE::Actor* GetPlayerMount()
541 {
542 RE::NiPointer<RE::Actor> currentMount;
543 if (RE::PlayerCharacter::GetSingleton()->GetMount(currentMount))
544 {
545 return currentMount.get();
546 }
547 return nullptr;
548 }
549
553 static bool IsPowerAttacking(const RE::Actor* actor)
554 {
555
556 if (!actor)
557 return false;
558
559 const auto high = actor->GetHighProcess();
560 if (!high)
561 return false;
562
563 const auto& attackData = high->attackData;
564 if (!attackData)
565 return false;
566
567 return attackData->data.flags.any(RE::AttackData::AttackFlag::kPowerAttack);
568 }
569
573 static bool IsBashing(const RE::Actor* actor)
574 {
575
576 if (!actor)
577 return false;
578
579 const auto high = actor->GetHighProcess();
580
581 if (!high)
582 return false;
583
584 if (actor->GetAttackState() == RE::ATTACK_STATE_ENUM::kNone)
585 return false;
586
587 const auto& attackData = high->attackData;
588 if (!attackData)
589 return false;
590
591 return attackData->data.flags.any(RE::AttackData::AttackFlag::kBashAttack);
592 }
593
601 static std::vector<RE::Actor*> GetNearbyActors(const RE::TESObjectREFR* a_ref, const float a_radius,
602 const bool a_ignorePlayer)
603 {
604 std::vector<RE::Actor*> result;
605 const auto processLists = RE::ProcessLists::GetSingleton();
606 if (!processLists)
607 return result;
608
609 if (a_ignorePlayer && processLists->numberHighActors == 0)
610 return result;
611
612 const auto squaredRadius = a_radius * a_radius;
613 const auto originPos = a_ref->GetPosition();
614
615 result.reserve(processLists->numberHighActors);
616
617 const auto get_actor_within_radius = [&](RE::Actor* a_actor)
618 {
619 if (a_actor && a_actor != a_ref && originPos.GetSquaredDistance(a_actor->GetPosition()) <= squaredRadius)
620 {
621 result.emplace_back(a_actor);
622 }
623 };
624 for (auto& actorHandle : processLists->highActorHandles)
625 {
626 const auto actor = actorHandle.get();
627 get_actor_within_radius(actor.get());
628 }
629
630 if (!a_ignorePlayer)
631 {
632 get_actor_within_radius(RE::PlayerCharacter::GetSingleton());
633 }
634
635 return result;
636 }
637
643 static std::vector<RE::Actor*> GetNearbyNonPlayerTeammates(const RE::TESObjectREFR* a_ref, const float a_radius)
644 {
645 auto actors = GetNearbyActors(a_ref, a_radius, true);
646 std::erase_if(actors, [](const RE::Actor* a) { return a->IsPlayerTeammate(); });
647 return actors;
648 }
649
654 static bool IsGuardNearby(const RE::TESObjectREFR* a_ref, const float a_radius)
655 {
656 return std::ranges::any_of(GetNearbyActors(a_ref, a_radius, false),
657 [](const RE::Actor* a) { return a && a->IsGuard(); });
658 }
659
664 static RE::Actor* GetClosestActor(const RE::TESObjectREFR* a_ref, const float a_radius)
665 {
666 const auto nearby_actors = GetNearbyActors(a_ref, a_radius, false);
667 return GetClosestFromVector(a_ref, nearby_actors);
668 }
669
674 static RE::Actor* GetClosestNonPlayerTeammate(const RE::TESObjectREFR* a_ref, const float a_radius)
675 {
676 const auto nearby_actors = GetNearbyNonPlayerTeammates(a_ref, a_radius);
677 return GetClosestFromVector(a_ref, nearby_actors);
678 }
679
686 static void SetNPCLevel(RE::Actor* actor, uint16_t level)
687 {
688 MiscUtil::RunConsoleCommandOnRef(actor, std::format("SetLevel {}", level));
689 }
690
696 static float GetActorValuePercentage(RE::Actor* a_actor, RE::ActorValue a_av)
697 {
698 using func_t = decltype(&GetActorValuePercentage);
699 static REL::Relocation<func_t> func{RELOCATION_ID(36347, 37337)};
700 return func(a_actor, a_av);
701 }
702
703 private:
708 static RE::Actor* GetClosestFromVector(const RE::TESObjectREFR* a_ref, const std::vector<RE::Actor*>& actors)
709 {
710 if (actors.empty())
711 return nullptr;
712 const auto originPos = a_ref->GetPosition();
713 const auto it = std::ranges::min_element(actors,
714 [&](RE::Actor* a, RE::Actor* b)
715 {
716 return originPos.GetSquaredDistance(a->GetPosition()) <
717 originPos.GetSquaredDistance(b->GetPosition());
718 });
719 return it != actors.end() ? *it : nullptr;
720 }
721};
722
723} // 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:477
static float GetMaxHealth(RE::Actor *a_actor)
Get maximum Health of an actor.
Definition st-actor.h:321
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:405
static bool StartsDead(const RE::Actor *actor)
Check if actor is supposed to spawn dead.
Definition st-actor.h:309
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:96
static RE::Actor * GetClosestActor(const RE::TESObjectREFR *a_ref, const float a_radius)
Get the closest actor within a radius.
Definition st-actor.h:664
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:708
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:643
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:654
static bool CasterIsCastingObject(RE::ActorMagicCaster *a_caster)
check if current spell is an object
Definition st-actor.h:55
static bool IsVampire(RE::Actor *a_ref)
Check if actor is vampire.
Definition st-actor.h:199
static bool IsBashing(const RE::Actor *actor)
Check if an actor is shield bashing.
Definition st-actor.h:573
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:674
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:153
static bool ActorHasItem(RE::Actor *actor, RE::TESBoundObject *item)
Check if actor has the specified item in the inventory.
Definition st-actor.h:497
static RE::TESObjectWEAP * GetWieldingWeapon(RE::Actor *a_actor)
Get the weapon a character is currently wielding.
Definition st-actor.h:513
static float GetCurrentLightLevelPlayer()
Get the light level the player is standing in.
Definition st-actor.h:283
static bool IsMoving(const RE::Actor *a_actor)
Convenience function to check if actor is moving.
Definition st-actor.h:27
static float GetMaxMagicka(RE::Actor *actor)
Get maximum Magicka of an actor.
Definition st-actor.h:351
static int RemoveItemPlayer(RE::TESBoundObject *item, int count)
Remove item from the player.
Definition st-actor.h:486
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:686
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:601
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:466
static void InterruptAttack(RE::Actor *a_actor)
Interrupt attack.
Definition st-actor.h:14
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:123
static bool IsDragon(const RE::Actor *a_actor)
Check if actor is a dragon.
Definition st-actor.h:237
static void FullyHealActor(RE::Actor *a_actor)
Fully heals all 3 attributes of an actor.
Definition st-actor.h:365
static RE::Actor * GetPlayerMount()
Get the player mount.
Definition st-actor.h:540
static float GetMaxStamina(RE::Actor *actor)
Get maximum Stamina of an actor.
Definition st-actor.h:336
static void TryStagger(RE::Actor *a_target, float a_staggerMult, RE::Actor *a_aggressor)
Try to stagger a target.
Definition st-actor.h:296
static bool ActorHasQuestObjectInHand(RE::Actor *actor)
Check if actor has Quest item as weapon.
Definition st-actor.h:173
static float GetActorValuePercentage(RE::Actor *a_actor, RE::ActorValue a_av)
Get the actor value percentage of an actor.
Definition st-actor.h:696
static bool IsUndead(RE::Actor *a_ref)
Check if actor is undead.
Definition st-actor.h:217
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:382
static float GetCurrentLightLevel(const RE::Actor *a_actor)
Get the current light level the actor is standing in.
Definition st-actor.h:272
static RE::TESObjectCELL * GetPlayerCell()
Get the current cell the player is in.
Definition st-actor.h:257
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:73
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:440
static bool IsGod(RE::Actor *a_actor)
convenience function to check if an actor is the player and whether god mode is enabled
Definition st-actor.h:44
static bool IsPowerAttacking(const RE::Actor *actor)
Check if an actor is power attacking.
Definition st-actor.h:553
static void RunConsoleCommandOnRef(RE::TESObjectREFR *a_target, const std::string_view a_command)
Run console command on reference.
Definition st-misc.h:117