F4MP/codigos originales/tiltedcode/Code/client/Services/Debug/Views/CombatView.cpp
2026-01-06 18:53:59 +01:00

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();
}