mirror of
https://github.com/Jous99/F4MP.git
synced 2026-01-12 17:10:54 +01:00
203 lines
7 KiB
C++
203 lines
7 KiB
C++
|
|
#include <Components.h>
|
||
|
|
#include <World.h>
|
||
|
|
#include <imgui.h>
|
||
|
|
#include <inttypes.h>
|
||
|
|
#include <Services/DebugService.h>
|
||
|
|
#include <Systems/AnimationSystem.h>
|
||
|
|
|
||
|
|
#include <BSGraphics/BSGraphicsRenderer.h>
|
||
|
|
#include <Camera/PlayerCamera.h>
|
||
|
|
#include <Games/Skyrim/Forms/TESForm.h>
|
||
|
|
#include <Games/Skyrim/Interface/Menus/HUDMenuUtils.h>
|
||
|
|
#include <Games/Skyrim/NetImmerse/NiCamera.h>
|
||
|
|
#include <Games/Skyrim/TESObjectREFR.h>
|
||
|
|
#include <NetImmerse/NiCamera.h>
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
#if 0
|
||
|
|
constexpr float fFloatQuestMarkerMaxDistance = 2000.f;
|
||
|
|
constexpr float fFloatQuestMarkerMinDistance = 1000.f;
|
||
|
|
|
||
|
|
float CalculateFloatingQuestMarkerAlpha()
|
||
|
|
{
|
||
|
|
float v1 = fsqrt((__m128)LODWORD(this->fDistanceToPlayerSqr)).m128_f32[0] -
|
||
|
|
fFloatQuestMarkerMaxDistance) /
|
||
|
|
(fFloatQuestMarkerMinDistance - fFloatQuestMarkerMaxDistance)) * 100.0;
|
||
|
|
if (v1 > 100.0)
|
||
|
|
return FLOAT_100_0;
|
||
|
|
result = 0.0;
|
||
|
|
if (v1 >= 0.0)
|
||
|
|
return v1;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static __declspec(noinline) bool DrawInWorldSpace(TESObjectREFR* apRefr, ImVec2& outViewPos)
|
||
|
|
{
|
||
|
|
// Attach at the head ish.
|
||
|
|
auto pos = apRefr->position;
|
||
|
|
pos.z += apRefr->GetHeight();
|
||
|
|
|
||
|
|
NiPoint3 screenPoint{};
|
||
|
|
HUDMenuUtils::WorldPtToScreenPt3(pos, screenPoint);
|
||
|
|
// Calculate window collision bounds.
|
||
|
|
auto* pViewport = BSGraphics::GetMainWindow();
|
||
|
|
|
||
|
|
// Translate to screen
|
||
|
|
const ImVec2 screenPos = ImVec2{
|
||
|
|
(pViewport->uiWindowWidth * screenPoint.x),
|
||
|
|
(pViewport->uiWindowHeight * (1.0f - screenPoint.y)),
|
||
|
|
};
|
||
|
|
|
||
|
|
const ImVec2 windowSize = {
|
||
|
|
static_cast<float>(pViewport->uiWindowWidth),
|
||
|
|
static_cast<float>(pViewport->uiWindowHeight),
|
||
|
|
};
|
||
|
|
|
||
|
|
auto IsVisible = [](const ImVec2& acVec2, const ImVec2& acScreenSize, float z) {
|
||
|
|
return (acVec2.x > 0 && acVec2.x <= acScreenSize.x) &&
|
||
|
|
(acVec2.y > 0 && acVec2.y <= acScreenSize.y) && z >= 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
if (IsVisible(screenPos, windowSize, screenPoint.z))
|
||
|
|
{
|
||
|
|
outViewPos = screenPos;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
outViewPos = {};
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
// TODO(Force): Net Histrogram (waves)
|
||
|
|
// Engine stuff.
|
||
|
|
// Fix cursor.
|
||
|
|
|
||
|
|
void DebugService::SetDebugId(const uint32_t aFormId) noexcept
|
||
|
|
{
|
||
|
|
m_formId = aFormId;
|
||
|
|
}
|
||
|
|
|
||
|
|
void DebugService::DrawComponentDebugView()
|
||
|
|
{
|
||
|
|
auto view = m_world.view<FormIdComponent>();
|
||
|
|
|
||
|
|
if (!m_formId)
|
||
|
|
return;
|
||
|
|
|
||
|
|
auto entityIt = std::find_if(view.begin(), view.end(), [id = m_formId, view](auto entity) { return view.get<FormIdComponent>(entity).Id == id; });
|
||
|
|
|
||
|
|
if (entityIt == view.end())
|
||
|
|
return;
|
||
|
|
|
||
|
|
auto entity = *entityIt;
|
||
|
|
|
||
|
|
if (auto* pObject = Cast<TESObjectREFR>(TESForm::GetById(m_formId)))
|
||
|
|
{
|
||
|
|
ImVec2 screenPos{};
|
||
|
|
if (DrawInWorldSpace(pObject, screenPos))
|
||
|
|
{
|
||
|
|
ImGui::SetNextWindowPos(screenPos);
|
||
|
|
ImGui::Begin("Component debug");
|
||
|
|
|
||
|
|
if (auto* pComponent = m_world.try_get<ReplayedActionsDebugComponent>(entity))
|
||
|
|
{
|
||
|
|
DisplayListOfReplayedActions(*pComponent, m_world.try_get<RemoteAnimationComponent>(entity));
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auto serverIdRes = Utils::GetServerId(entity))
|
||
|
|
{
|
||
|
|
ImGui::InputScalar("Server ID", ImGuiDataType_U32, &(*serverIdRes), 0, 0, "%" PRIx32, ImGuiInputTextFlags_CharsHexadecimal);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auto* pComponent = m_world.try_get<LocalComponent>(entity))
|
||
|
|
{
|
||
|
|
if (ImGui::CollapsingHeader("LocalComponent"))
|
||
|
|
{
|
||
|
|
ImGui::InputScalar("Is dead?", ImGuiDataType_U8, &pComponent->IsDead, 0, 0, "%" PRIx8, ImGuiInputTextFlags_CharsHexadecimal);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auto* pComponent = m_world.try_get<LocalAnimationComponent>(entity))
|
||
|
|
{
|
||
|
|
if (ImGui::CollapsingHeader("LocalAnimationComponent"))
|
||
|
|
{
|
||
|
|
int actions = int(pComponent->Actions.size());
|
||
|
|
ImGui::InputInt("Number of actions", &actions, 0, 0, ImGuiInputTextFlags_ReadOnly);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auto* pComponent = m_world.try_get<RemoteComponent>(entity))
|
||
|
|
{
|
||
|
|
if (ImGui::CollapsingHeader("RemoteComponent"))
|
||
|
|
{
|
||
|
|
ImGui::InputScalar("Cached ref ID", ImGuiDataType_U32, &pComponent->CachedRefId, 0, 0, "%" PRIx32, ImGuiInputTextFlags_CharsHexadecimal);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auto* pComponent = m_world.try_get<InterpolationComponent>(entity))
|
||
|
|
{
|
||
|
|
if (ImGui::CollapsingHeader("InterpolationComponent"))
|
||
|
|
{
|
||
|
|
ImGui::Text("%f,%f,%f\n", pComponent->Position.x, pComponent->Position.y, pComponent->Position.z);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auto* pComponent = m_world.try_get<FaceGenComponent>(entity))
|
||
|
|
{
|
||
|
|
if (ImGui::CollapsingHeader("FaceGenComponent"))
|
||
|
|
{
|
||
|
|
for (auto x : pComponent->FaceTints.Entries)
|
||
|
|
{
|
||
|
|
ImGui::Text("Alpha %f, Color %u, Type %u\n", x.Alpha, x.Color, x.Type);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auto* pComponent = m_world.try_get<RemoteAnimationComponent>(entity))
|
||
|
|
{
|
||
|
|
if (ImGui::CollapsingHeader("RemoteAnimationComponent"))
|
||
|
|
{
|
||
|
|
ImGui::Text("EventName: %s\nTargetEventName: %s\nState1: %u\nState2: %u", pComponent->LastRanAction.EventName.c_str(), pComponent->LastRanAction.TargetEventName.c_str(), pComponent->LastRanAction.State1, pComponent->LastRanAction.State2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ImGui::End();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void DebugService::DisplayListOfReplayedActions(const ReplayedActionsDebugComponent& aDebugComponent,
|
||
|
|
RemoteAnimationComponent* apAnimationComponent) const noexcept
|
||
|
|
{
|
||
|
|
const size_t total = aDebugComponent.ActionsReceivedForReplay.Actions.size();
|
||
|
|
const auto header = fmt::format("List of Replayed Actions ({})", total);
|
||
|
|
|
||
|
|
if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen))
|
||
|
|
{
|
||
|
|
String replayedActionsText;
|
||
|
|
if (aDebugComponent.ActionsReceivedForReplay.ResetAnimationGraph)
|
||
|
|
replayedActionsText += "<Animation graph reset>, ";
|
||
|
|
|
||
|
|
for (size_t i = 0; i < total; ++i)
|
||
|
|
{
|
||
|
|
if (i > 0)
|
||
|
|
replayedActionsText += ", ";
|
||
|
|
replayedActionsText += aDebugComponent.ActionsReceivedForReplay.Actions[i].EventName;
|
||
|
|
}
|
||
|
|
total == 0 ? replayedActionsText = "<None>" : replayedActionsText += '.';
|
||
|
|
ImGui::TextWrapped(replayedActionsText.c_str());
|
||
|
|
|
||
|
|
if (ImGui::IsItemHovered())
|
||
|
|
{
|
||
|
|
ImGui::SetTooltip("Actions that were run (replayed) after this remote Actor received\n"
|
||
|
|
"spawn data from the server. Right click to replay again.");
|
||
|
|
}
|
||
|
|
if (ImGui::IsItemClicked(1) && apAnimationComponent && total > 0)
|
||
|
|
{
|
||
|
|
AnimationSystem::AddActionsForReplay(*apAnimationComponent, aDebugComponent.ActionsReceivedForReplay);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|