mirror of
https://github.com/Jous99/F4MP.git
synced 2026-01-12 06:50:52 +01:00
225 lines
7.7 KiB
C++
225 lines
7.7 KiB
C++
#include <Services/DebugService.h>
|
|
#include <imgui.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <Combat/CombatController.h>
|
|
#include <Combat/CombatGroup.h>
|
|
#include <Combat/CombatTargetSelector.h>
|
|
#include <Games/Misc/BGSWorldLocation.h>
|
|
#include <Combat/CombatInventory.h>
|
|
|
|
#include <math.h>
|
|
|
|
namespace
|
|
{
|
|
struct DetectionState : public NiRefObject
|
|
{
|
|
~DetectionState() override;
|
|
|
|
uint32_t level;
|
|
uint8_t unk14;
|
|
uint8_t unk15;
|
|
uint8_t unk16;
|
|
uint8_t pad17;
|
|
float unk18;
|
|
NiPoint3 unk1C;
|
|
float unk28;
|
|
NiPoint3 unk2C;
|
|
float unk38;
|
|
NiPoint3 unk3C;
|
|
};
|
|
static_assert(sizeof(DetectionState) == 0x48);
|
|
|
|
BGSEncounterZone* GetLocationEncounterZone(Actor* apActor)
|
|
{
|
|
TP_THIS_FUNCTION(TGetLocationEncounterZone, BGSEncounterZone*, Actor);
|
|
POINTER_SKYRIMSE(TGetLocationEncounterZone, getLocationEncounterZone, 20203);
|
|
return TiltedPhoques::ThisCall(getLocationEncounterZone, apActor);
|
|
}
|
|
|
|
bool IsValidTarget(CombatTargetSelector* apThis, Actor* apAttacker, Actor* apTarget, BGSEncounterZone* apEncounterZone)
|
|
{
|
|
TP_THIS_FUNCTION(TIsValidTarget, bool, CombatTargetSelector, Actor* apAttacker, Actor* apTarget,
|
|
BGSEncounterZone* apEncounterZone);
|
|
POINTER_SKYRIMSE(TIsValidTarget, isValidTarget, 47196);
|
|
return TiltedPhoques::ThisCall(isValidTarget, apThis, apAttacker, apTarget, apEncounterZone);
|
|
}
|
|
|
|
DetectionState* GetDetectionState(Actor* apAttacker, Actor* apTarget)
|
|
{
|
|
TP_THIS_FUNCTION(TGetDetectionState, DetectionState*, Actor, Actor* apTarget);
|
|
POINTER_SKYRIMSE(TGetDetectionState, getDetectionState, 37757);
|
|
return TiltedPhoques::ThisCall(getDetectionState, apAttacker, apTarget);
|
|
}
|
|
|
|
float GetCombatTargetSelectorDetectionTimeLimit()
|
|
{
|
|
POINTER_SKYRIMSE(float, s_value, 382393);
|
|
return *s_value;
|
|
}
|
|
|
|
float GetCombatTargetSelectorRecentLOSTimeLimit()
|
|
{
|
|
POINTER_SKYRIMSE(float, s_value, 382400);
|
|
return *s_value;
|
|
}
|
|
|
|
bool ActorHasEquippedRangedWeapon_m(Actor* apActor)
|
|
{
|
|
TP_THIS_FUNCTION(TActorHasEquippedRangedWeapon_m, bool, Actor);
|
|
POINTER_SKYRIMSE(TActorHasEquippedRangedWeapon_m, actorHasEquippedRangedWeapon_m, 47303);
|
|
return TiltedPhoques::ThisCall(actorHasEquippedRangedWeapon_m, apActor);
|
|
}
|
|
|
|
BGSWorldLocation* RefrGetWorldLocation(TESObjectREFR* apThis, BGSWorldLocation* apResult)
|
|
{
|
|
TP_THIS_FUNCTION(TRefrGetWorldLocation, BGSWorldLocation*, TESObjectREFR, BGSWorldLocation*);
|
|
POINTER_SKYRIMSE(TRefrGetWorldLocation, refrGetWorldLocation, 19784);
|
|
return TiltedPhoques::ThisCall(refrGetWorldLocation, apThis, apResult);
|
|
}
|
|
|
|
float GetModifiedDistance(BGSWorldLocation* apThis, BGSWorldLocation* apWorldLocation)
|
|
{
|
|
TP_THIS_FUNCTION(TGetModifiedDistance, float, BGSWorldLocation, BGSWorldLocation*);
|
|
POINTER_SKYRIMSE(TGetModifiedDistance, getModifiedDistance, 18518);
|
|
return TiltedPhoques::ThisCall(getModifiedDistance, apThis, apWorldLocation);
|
|
}
|
|
|
|
float GetDword_142FE5B78()
|
|
{
|
|
POINTER_SKYRIMSE(float, s_value, 405282);
|
|
return *s_value;
|
|
}
|
|
|
|
bool CheckMovement(CombatController* apThis, BGSWorldLocation* apWorldLocation)
|
|
{
|
|
TP_THIS_FUNCTION(TCheckMovement, bool, CombatController, BGSWorldLocation*);
|
|
POINTER_SKYRIMSE(TCheckMovement, checkMovement, 33261);
|
|
return TiltedPhoques::ThisCall(checkMovement, apThis, apWorldLocation);
|
|
}
|
|
|
|
bool sub_1407E7A40(Actor* apThis, BGSWorldLocation* apWorldLocation)
|
|
{
|
|
TP_THIS_FUNCTION(TFunc, bool, Actor, BGSWorldLocation*);
|
|
POINTER_SKYRIMSE(TFunc, func, 47307);
|
|
return TiltedPhoques::ThisCall(func, apThis, apWorldLocation);
|
|
}
|
|
|
|
bool IsFleeing(Actor* apThis)
|
|
{
|
|
TP_THIS_FUNCTION(TFunc, bool, Actor);
|
|
POINTER_SKYRIMSE(TFunc, func, 37577);
|
|
return TiltedPhoques::ThisCall(func, apThis);
|
|
}
|
|
|
|
float CalculateTargetScore(CombatTargetSelector* apThis, CombatTarget* apCombatTarget, Actor* apAttacker, Actor* apTarget, BGSEncounterZone* apEncounterZone)
|
|
{
|
|
float score = 0.f;
|
|
|
|
CombatController* pCombatController = apThis->pCombatController;
|
|
Actor* pCachedAttacker = pCombatController->pCachedAttacker.object;
|
|
|
|
BGSWorldLocation pAttackerWorldLocation{};
|
|
RefrGetWorldLocation(apAttacker, &pAttackerWorldLocation);
|
|
BGSWorldLocation pTargetWorldLocation{};
|
|
RefrGetWorldLocation(apTarget, &pTargetWorldLocation);
|
|
|
|
float aiTime = AITimer::GetAITime();
|
|
|
|
auto* pDetectionState = GetDetectionState(apAttacker, apTarget);
|
|
if (pDetectionState && (aiTime - pDetectionState->unk38) <= GetCombatTargetSelectorDetectionTimeLimit())
|
|
{
|
|
if (pDetectionState->unk15)
|
|
score = 1000.f;
|
|
else if (pCachedAttacker == apTarget &&
|
|
(aiTime - pDetectionState->unk18) < GetCombatTargetSelectorRecentLOSTimeLimit())
|
|
score = 1000.f;
|
|
|
|
if (pDetectionState->unk14)
|
|
score += 100.f;
|
|
}
|
|
|
|
if (pCachedAttacker == apTarget)
|
|
score += 100.f;
|
|
|
|
if (ActorHasEquippedRangedWeapon_m(apAttacker) == ActorHasEquippedRangedWeapon_m(apTarget))
|
|
score += 100.f;
|
|
|
|
bool isSameWorldLocation = pTargetWorldLocation.pSpace == pAttackerWorldLocation.pSpace;
|
|
float distanceFactor = 0.f;
|
|
if (isSameWorldLocation)
|
|
distanceFactor = GetModifiedDistance(&pAttackerWorldLocation, &pTargetWorldLocation);
|
|
else if (apTarget != pCachedAttacker)
|
|
distanceFactor = 2048.f;
|
|
|
|
score += (float)((float)(1.0 - (float)(fminf(distanceFactor, 2048.f) * 0.00048828125f)) * 1000.f);
|
|
|
|
if (apCombatTarget->attackerCount > (pCachedAttacker == apTarget))
|
|
score += GetDword_142FE5B78();
|
|
|
|
if (pCombatController->pInventory->maximumRange < 1024.f || !isSameWorldLocation)
|
|
{
|
|
if (!CheckMovement(pCombatController, &pTargetWorldLocation))
|
|
score -= 2000.f;
|
|
if (!isSameWorldLocation && !sub_1407E7A40(apAttacker, &pTargetWorldLocation))
|
|
score -= 2500.f;
|
|
}
|
|
|
|
if (apTarget->VirtIsDead(false))
|
|
score -= 500.f;
|
|
|
|
if (IsFleeing(apTarget))
|
|
score -= 500.f;
|
|
|
|
return score;
|
|
}
|
|
} // namespace
|
|
|
|
void DebugService::DrawCombatView()
|
|
{
|
|
if (!m_formId)
|
|
return;
|
|
|
|
ImGui::Begin("Combat");
|
|
|
|
ImGui::InputScalar("Form ID", ImGuiDataType_U32, (void*)&m_formId, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
|
|
|
|
Actor* pActor = Cast<Actor>(TESForm::GetById(m_formId));
|
|
|
|
if (pActor && pActor->pCombatController && pActor->pCombatController->pCombatGroup && pActor->pCombatController->pActiveTargetSelector)
|
|
{
|
|
Actor* pTarget = Cast<Actor>(TESObjectREFR::GetByHandle(pActor->pCombatController->targetHandle));
|
|
if (pTarget)
|
|
{
|
|
uint32_t targetFormID = pTarget->formID;
|
|
ImGui::InputScalar("Current target ID", ImGuiDataType_U32, (void*)&targetFormID, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
|
|
}
|
|
|
|
BGSEncounterZone* pZone = GetLocationEncounterZone(pActor);
|
|
|
|
ImGui::BeginChild("Potential targets", ImVec2(0, 200), true);
|
|
|
|
auto& targets = pActor->pCombatController->pCombatGroup->targets;
|
|
for (int i = 0; i < targets.length; i++)
|
|
{
|
|
auto& target = targets[i];
|
|
Actor* pTarget = Cast<Actor>(TESObjectREFR::GetByHandle(target.targetHandle));
|
|
if (pTarget)
|
|
{
|
|
const uint32_t formID = pTarget->formID;
|
|
ImGui::InputScalar("Form ID", ImGuiDataType_U32, (void*)&formID, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
|
|
|
|
float score =
|
|
CalculateTargetScore(pActor->pCombatController->pActiveTargetSelector, &target, pActor, pTarget, pZone);
|
|
|
|
ImGui::InputFloat("Score", &score, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|