Git: Añadido .gitignore y limpieza de archivos de compilación

This commit is contained in:
Jous99 2026-01-06 19:13:21 +01:00
parent 88dd258cd7
commit 38c2dc701d
42 changed files with 41 additions and 2612 deletions

41
.gitignore vendored Normal file
View file

@ -0,0 +1,41 @@
# --- Visual Studio ---
.vs/
[Dd]ebug/
[Rr]elease/
x64/
x86/
*.user
*.aps
*.pdb
*.obj
*.log
*.tlog
*.lastbuildstate
*.idb
*.iobj
*.ipdb
*.ipch
*.ncb
*.sdf
*.suo
*.vsp
*.vspx
*.orig
*.db
*.opendb
# --- Archivos de compilación ---
*.exe
*.dll
*.lib
*.exp
*.ilk
# --- Archivos de sistema ---
.DS_Store
Thumbs.db
# --- F4MP Específico ---
# Ignorar logs que genera el mod al testearlo
F4MP.log
F4MP_Client.log

View file

@ -1,12 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Jous\\Documents\\GitHub\\F4MP\\Client\\F4MP\\",
"Documents": [],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
}
]
}

View file

@ -1,12 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Jous\\Documents\\GitHub\\F4MP\\Client\\F4MP\\",
"Documents": [],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
}
]
}

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View file

@ -1,446 +0,0 @@
#include <BranchInfo.h>
#include <Havok/hkbStateMachine.h>
#include <Structs/AnimationGraphDescriptorManager.h>
#include <Havok/BShkbAnimationGraph.h>
#include <Havok/BShkbHkxDB.h>
#include <Havok/hkbBehaviorGraph.h>
#include <Services/ImguiService.h>
#include <Services/DebugService.h>
#include <Services/TransportService.h>
#include <Services/PapyrusService.h>
#include <Services/QuestService.h>
#include <Events/UpdateEvent.h>
#include <Events/DialogueEvent.h>
#include <Events/SubtitleEvent.h>
#include <Events/MoveActorEvent.h>
#include <Events/ConnectionErrorEvent.h>
#include <Games/References.h>
#include <BSAnimationGraphManager.h>
#include <Forms/TESFaction.h>
#include <Forms/TESQuest.h>
#include <Forms/BGSAction.h>
#include <Forms/TESIdleForm.h>
#include <Forms/TESNPC.h>
#include <Games/Animation/ActorMediator.h>
#include <Games/Animation/TESActionData.h>
#include <Magic/ActorMagicCaster.h>
#include <Misc/BSFixedString.h>
#include <Structs/ActionEvent.h>
#include <Components.h>
#include <World.h>
#include <Forms/TESObjectCELL.h>
#include <Forms/TESWorldSpace.h>
#include <Games/TES.h>
#include <AI/AIProcess.h>
#include <Messages/RequestRespawn.h>
#include <Messages/PartyCreateRequest.h>
#include <Messages/PartyLeaveRequest.h>
#include <Games/Misc/SubtitleManager.h>
#include <Games/Overrides.h>
#include <OverlayApp.hpp>
#include <EquipManager.h>
#include <Forms/TESAmmo.h>
#include <BSGraphics/BSGraphicsRenderer.h>
#include <Interface/UI.h>
#include <Combat/CombatController.h>
#include <Camera/PlayerCamera.h>
#include <AI/Movement/PlayerControls.h>
#include <Interface/IMenu.h>
#include <Camera/PlayerCamera.h>
#include <DefaultObjectManager.h>
#include <Misc/InventoryEntry.h>
#include <Misc/MiddleProcess.h>
#include <imgui.h>
#include <inttypes.h>
extern thread_local bool g_overrideFormId;
constexpr char kBuildTag[] = "Build: " BUILD_COMMIT " " BUILD_BRANCH " EVO\nBuilt: " __TIMESTAMP__;
static void DrawBuildTag()
{
auto* pWindow = BSGraphics::GetMainWindow();
const ImVec2 coord{50.f, static_cast<float>((pWindow->uiWindowHeight + 25) - 100)};
ImGui::GetBackgroundDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize(), coord, ImColor::ImColor(255.f, 0.f, 0.f), kBuildTag);
}
void __declspec(noinline) DebugService::PlaceActorInWorld() noexcept
{
if (m_actors.size())
return;
const auto pPlayerBaseForm = static_cast<TESNPC*>(PlayerCharacter::Get()->baseForm);
auto pActor = Actor::Create(pPlayerBaseForm);
const Inventory inventory = PlayerCharacter::Get()->GetActorInventory();
pActor->SetActorInventory(inventory);
pActor->GetExtension()->SetPlayer(true);
m_actors.emplace_back(pActor);
}
DebugService::DebugService(entt::dispatcher& aDispatcher, World& aWorld, TransportService& aTransport, ImguiService& aImguiService)
: m_dispatcher(aDispatcher)
, m_transport(aTransport)
, m_world(aWorld)
{
m_updateConnection = m_dispatcher.sink<UpdateEvent>().connect<&DebugService::OnUpdate>(this);
m_drawImGuiConnection = aImguiService.OnDraw.connect<&DebugService::OnDraw>(this);
m_dialogueConnection = m_dispatcher.sink<DialogueEvent>().connect<&DebugService::OnDialogue>(this);
m_dispatcher.sink<SubtitleEvent>().connect<&DebugService::OnSubtitle>(this);
m_dispatcher.sink<MoveActorEvent>().connect<&DebugService::OnMoveActor>(this);
}
void DebugService::OnDialogue(const DialogueEvent& acEvent) noexcept
{
if (ActorID)
return;
ActorID = acEvent.ActorID;
VoiceFile = acEvent.VoiceFile;
}
void DebugService::OnSubtitle(const SubtitleEvent& acEvent) noexcept
{
if (SubActorID)
return;
SubActorID = acEvent.SpeakerID;
SubtitleText = acEvent.Text;
TopicID = acEvent.TopicFormID;
}
// TODO: yeah, i'm aware of how dumb this looks, but things crash if
// you do it directly by adding an event to the queue, no symbols for tiltedcore when debugging,
// so this'll do for now
struct MoveData
{
Actor* pActor = nullptr;
TESObjectCELL* pCell = nullptr;
NiPoint3 position;
} moveData;
void DebugService::OnMoveActor(const MoveActorEvent& acEvent) noexcept
{
Actor* pActor = Cast<Actor>(TESForm::GetById(acEvent.FormId));
TESObjectCELL* pCell = Cast<TESObjectCELL>(TESForm::GetById(acEvent.CellId));
if (!pActor || !pCell)
return;
moveData.pActor = pActor;
moveData.pCell = pCell;
moveData.position = acEvent.Position;
}
extern thread_local bool g_forceAnimation;
void DebugService::OnUpdate(const UpdateEvent& acUpdateEvent) noexcept
{
if (!BSGraphics::GetMainWindow()->IsForeground())
return;
if (moveData.pActor)
{
moveData.pActor->MoveTo(moveData.pCell, moveData.position);
moveData.pActor = nullptr;
}
static std::atomic<bool> s_f8Pressed = false;
static std::atomic<bool> s_f7Pressed = false;
static std::atomic<bool> s_f6Pressed = false;
if (GetAsyncKeyState(VK_F3) & 0x01)
{
m_showDebugStuff = !m_showDebugStuff;
}
#if (!IS_MASTER)
if (GetAsyncKeyState(VK_F6))
{
if (!s_f6Pressed)
{
s_f6Pressed = true;
static char s_address[256] = "127.0.0.1:10578";
if (!m_transport.IsOnline())
m_transport.Connect(s_address);
else
m_transport.Close();
}
}
else
s_f6Pressed = false;
if (GetAsyncKeyState(VK_F7))
{
if (!s_f7Pressed)
{
s_f7Pressed = true;
if (!m_world.GetPartyService().IsInParty())
m_transport.Send(PartyCreateRequest{});
else
m_transport.Send(PartyLeaveRequest{});
}
}
else
s_f7Pressed = false;
if (GetAsyncKeyState(VK_F8) & 0x01)
{
if (!s_f8Pressed)
{
s_f8Pressed = true;
//PlaceActorInWorld();
}
}
else
s_f8Pressed = false;
#endif
}
static bool g_enableServerWindow{false};
static bool g_enableAnimWindow{false};
static bool g_enableEntitiesWindow{false};
static bool g_enableInventoryWindow{false};
static bool g_enableNetworkWindow{false};
static bool g_enableFormsWindow{false};
static bool g_enablePlayerWindow{false};
static bool g_enableSkillsWindow{false};
static bool g_enablePartyWindow{false};
static bool g_enableActorValuesWindow{false};
static bool g_enableQuestWindow{false};
static bool g_enableCellWindow{false};
static bool g_enableProcessesWindow{false};
static bool g_enableWeatherWindow{false};
static bool g_enableCombatWindow{false};
static bool g_enableCalendarWindow{false};
static bool g_enableDragonSpawnerWindow{false};
void DebugService::DrawServerView() noexcept
{
ImGui::SetNextWindowSize(ImVec2(250, 440), ImGuiCond_FirstUseEver);
ImGui::Begin("Server");
static char s_address[1024] = "127.0.0.1:10578";
static char s_password[1024] = "";
ImGui::InputText("Address", s_address, std::size(s_address));
ImGui::InputText("Password", s_password, std::size(s_password));
if (m_transport.IsOnline())
{
if (ImGui::Button("Disconnect"))
m_transport.Close();
}
else
{
if (ImGui::Button("Connect"))
{
m_transport.SetServerPassword(s_password);
m_transport.Connect(s_address);
}
}
ImGui::End();
}
void DebugService::OnDraw() noexcept
{
const auto view = m_world.view<FormIdComponent>();
if (view.empty() || !m_showDebugStuff)
return;
ImGui::BeginMainMenuBar();
if (ImGui::BeginMenu("Helpers"))
{
if (ImGui::Button("Unstuck player"))
{
auto* pPlayer = PlayerCharacter::Get();
pPlayer->currentProcess->KnockExplosion(pPlayer, &pPlayer->position, 0.f);
}
if (ImGui::Button("Stop all combat"))
{
auto* pPlayer = PlayerCharacter::Get();
pPlayer->PayCrimeGoldToAllFactions();
ProcessLists* const pProcessLists = ProcessLists::Get();
if (pProcessLists)
{
for (uint32_t i = 0; i < pProcessLists->highActorHandleArray.length; ++i)
{
Actor* const pRefr = Cast<Actor>(TESObjectREFR::GetByHandle(pProcessLists->highActorHandleArray[i]));
if (pRefr && pRefr->GetNiNode())
pRefr->StopCombat();
}
}
}
ImGui::EndMenu();
}
#if (!IS_MASTER)
if (ImGui::BeginMenu("Components"))
{
ImGui::MenuItem("Show selected entity in world", nullptr, &m_drawComponentsInWorldSpace);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("UI"))
{
ImGui::MenuItem("Show build tag", nullptr, &m_showBuildTag);
if (ImGui::Button("Log all open windows"))
{
UI* pUI = UI::Get();
for (const auto& it : pUI->menuMap)
{
if (pUI->GetMenuOpen(it.key))
spdlog::info("{}", it.key.AsAscii());
}
}
if (ImGui::Button("Close all menus"))
{
UI::Get()->CloseAllMenus();
}
ImGui::EndMenu();
}
#endif
if (ImGui::BeginMenu("Debuggers"))
{
ImGui::MenuItem("Quests", nullptr, &g_enableQuestWindow);
ImGui::MenuItem("Entities", nullptr, &g_enableEntitiesWindow);
ImGui::MenuItem("Server", nullptr, &g_enableServerWindow);
ImGui::MenuItem("Party", nullptr, &g_enablePartyWindow);
ImGui::MenuItem("Dragon spawner", nullptr, &g_enableDragonSpawnerWindow);
#if (!IS_MASTER)
ImGui::MenuItem("Network", nullptr, &g_enableNetworkWindow);
ImGui::MenuItem("Forms", nullptr, &g_enableFormsWindow);
ImGui::MenuItem("Inventory", nullptr, &g_enableInventoryWindow);
ImGui::MenuItem("Animations", nullptr, &g_enableAnimWindow);
ImGui::MenuItem("Player", nullptr, &g_enablePlayerWindow);
ImGui::MenuItem("Skills", nullptr, &g_enableSkillsWindow);
ImGui::MenuItem("Cell", nullptr, &g_enableCellWindow);
ImGui::MenuItem("Processes", nullptr, &g_enableProcessesWindow);
ImGui::MenuItem("Weather", nullptr, &g_enableWeatherWindow);
ImGui::MenuItem("Combat", nullptr, &g_enableCombatWindow);
ImGui::MenuItem("Calendar", nullptr, &g_enableCalendarWindow);
#endif
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Misc"))
{
if (ImGui::Button("Crash Client"))
{
#if (!IS_MASTER)
spdlog::error("Crash client");
int* m = 0;
*m = 1338;
#else
ConnectionErrorEvent errorEvent{};
errorEvent.ErrorDetail = "Skyrim Together never crashes ;) With love, Yamashi, Force, Dragonisser, Cosideci.";
m_world.GetRunner().Trigger(errorEvent);
#endif
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
if (g_enableQuestWindow)
DrawQuestDebugView();
if (g_enableEntitiesWindow)
DrawEntitiesView();
if (g_enableServerWindow)
DrawServerView();
if (g_enablePartyWindow)
DrawPartyView();
if (g_enableDragonSpawnerWindow)
DrawDragonSpawnerView();
#if (!IS_MASTER)
if (g_enableNetworkWindow)
DrawNetworkView();
if (g_enableFormsWindow)
DrawFormDebugView();
if (g_enableInventoryWindow)
DrawContainerDebugView();
if (g_enableAnimWindow)
DrawAnimDebugView();
if (g_enablePlayerWindow)
DrawPlayerDebugView();
if (g_enableSkillsWindow)
DrawSkillView();
if (g_enableActorValuesWindow)
DrawActorValuesView();
if (g_enableCellWindow)
DrawCellView();
if (g_enableProcessesWindow)
DrawProcessView();
if (g_enableWeatherWindow)
DrawWeatherView();
if (g_enableCombatWindow)
DrawCombatView();
if (g_enableCalendarWindow)
DrawCalendarView();
if (m_drawComponentsInWorldSpace)
DrawComponentDebugView();
#endif
if (m_showBuildTag)
DrawBuildTag();
}
void DebugService::ArrangeGameWindows(HWND aThisWindow) noexcept
{
// This function conveniently arranges multiple game windows (multi-monitor window rearrangement is
// not implemented)
#if (!IS_MASTER)
const uint32_t screenWidth = GetSystemMetrics(SM_CXSCREEN);
const uint32_t screenHeight = GetSystemMetrics(SM_CYSCREEN);
RECT rect{};
GetWindowRect(aThisWindow, &rect);
const uint32_t gameWindowWidth = rect.right - rect.left;
const uint32_t gameWindowHeight = rect.bottom - rect.top;
const bool shouldArrangeWindows = (static_cast<float>(gameWindowWidth) / screenWidth) < 0.76f;
if (!shouldArrangeWindows)
return; // Too little room for that
SetWindowPos(GetConsoleWindow(), nullptr, 0, gameWindowHeight, 0, 0, SWP_NOSIZE);
CreateMutexA(0, FALSE, "SkyrimTogetherWindowMutex");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
// One game instance is already open, arrange secondary game window...
const bool arrangeSideBySide = (static_cast<float>(gameWindowWidth) / screenWidth) <= 0.51f;
RECT conRect{};
GetWindowRect(GetConsoleWindow(), &conRect);
const uint32_t conY = arrangeSideBySide ? gameWindowHeight : 0;
const uint32_t conX = arrangeSideBySide ? (gameWindowWidth - 16) : screenWidth - (conRect.right - conRect.left);
SetWindowPos(GetConsoleWindow(), nullptr, conX, conY, 0, 0, SWP_NOSIZE);
const uint32_t x = arrangeSideBySide ? (gameWindowWidth - 16) : (screenWidth - gameWindowWidth);
const uint32_t y = arrangeSideBySide ? 0 : (screenHeight - gameWindowHeight - 48);
SetWindowPos(aThisWindow, nullptr, x, y, 0, 0, SWP_NOSIZE);
}
#endif
}

View file

@ -1,23 +0,0 @@
#include <Services/DebugService.h>
#include <Forms/ActorValueInfo.h>
#include <imgui.h>
void DebugService::DrawActorValuesView()
{
Actor* pActor = Cast<Actor>(TESForm::GetById(m_formId));
if (!pActor)
return;
ImGui::Begin("Actor values");
// for (int i = 0; i < ActorValueInfo::kActorValueCount; i++)
{
ActorValueOwner& actorValueOwner = pActor->actorValueOwner;
float health[3]{actorValueOwner.GetValue(24), actorValueOwner.GetBaseValue(24), actorValueOwner.GetPermanentValue(24)};
ImGui::InputFloat3("Health (val/base/perm)", health, "%.3f", ImGuiInputTextFlags_ReadOnly);
}
ImGui::End();
}

View file

@ -1,408 +0,0 @@
#include <imgui.h>
#include <EquipManager.h>
#include <services/DebugService.h>
#include <Games/Skyrim/Forms/TESForm.h>
#include <Games/Skyrim/BSGraphics/BSGraphicsRenderer.h>
#include <Games/Skyrim/DefaultObjectManager.h>
#include <Games/Skyrim/Forms/TESAmmo.h>
#include <Games/Skyrim/Misc/InventoryEntry.h>
#include <Games/Skyrim/Misc/MiddleProcess.h>
#include <Games/ActorExtension.h>
#include <Actor.h>
#include <BSAnimationGraphManager.h>
#include <Havok/hkbStateMachine.h>
#include <Havok/BShkbAnimationGraph.h>
#include <Havok/BShkbHkxDB.h>
#include <Havok/hkbBehaviorGraph.h>
#include <structs/AnimationGraphDescriptorManager.h>
#include <inttypes.h>
#include <ModCompat/BehaviorVar.h>
uint64_t DisplayGraphDescriptorKey(BSAnimationGraphManager* pManager) noexcept
{
auto hash = pManager->GetDescriptorKey();
auto pDescriptor = AnimationGraphDescriptorManager::Get().GetDescriptor(hash);
spdlog::info("Key: {}", hash);
std::cout << "uint64_t key = " << hash << ";" << std::endl;
if (!pDescriptor)
spdlog::error("Descriptor key not found");
return hash;
}
void DebugService::DrawAnimDebugView()
{
static uint32_t fetchFormId = 0;
static Actor* pActor = nullptr;
static Map<uint32_t, uint32_t> s_values;
static Map<uint32_t, uint32_t> s_reusedValues;
static Map<uint32_t, short> s_valueTypes; // 0 for bool, 1 for float, 2 for int
static Vector<uint32_t> s_blacklist{};
static SortedMap<uint32_t, String> s_varMap{};
static Map<uint64_t, uint32_t> s_cachedKeys{};
ImGui::Begin("Animation debugging");
ImGui::InputScalar("Form ID", ImGuiDataType_U32, &fetchFormId, 0, 0, "%" PRIx32, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::Button("Look up"))
{
if (fetchFormId)
{
auto* pFetchForm = TESForm::GetById(fetchFormId);
if (pFetchForm)
pActor = Cast<Actor>(pFetchForm);
}
s_values.clear();
s_reusedValues.clear();
s_valueTypes.clear();
s_blacklist.clear();
s_varMap.clear();
}
if (!pActor)
{
ImGui::End();
s_varMap.clear();
s_values.clear();
s_reusedValues.clear();
s_valueTypes.clear();
s_blacklist.clear();
return;
}
if (ImGui::Button("Show cached hash"))
{
spdlog::info("{}", pActor->GetExtension()->GraphDescriptorHash);
BehaviorVar::Get()->Debug();
}
if (ImGui::Button("Clear all"))
{
s_varMap.clear();
s_values.clear();
s_reusedValues.clear();
s_valueTypes.clear();
s_blacklist.clear();
}
BSAnimationGraphManager* pManager = nullptr;
pActor->animationGraphHolder.GetBSAnimationGraph(&pManager);
if (!pManager)
{
ImGui::End();
s_varMap.clear();
s_values.clear();
s_reusedValues.clear();
s_valueTypes.clear();
s_blacklist.clear();
return;
}
const auto pGraph = pManager->animationGraphs.Get(pManager->animationGraphIndex);
if (!pGraph)
{
ImGui::End();
s_varMap.clear();
s_values.clear();
s_reusedValues.clear();
s_valueTypes.clear();
s_blacklist.clear();
return;
}
if (s_varMap.empty())
{
s_varMap = pManager->DumpAnimationVariables(true);
auto hash = DisplayGraphDescriptorKey(pManager);
if (s_cachedKeys.find(hash) != std::end(s_cachedKeys))
spdlog::warn("Key was detected before! Form ID of last detected actor: {:X}", s_cachedKeys[hash]);
s_cachedKeys[hash] = pActor->formID;
}
if (ImGui::Button("Revert animation graph"))
{
spdlog::info("RevertAnimationGraphManager success? {}",
pActor->animationGraphHolder.RevertAnimationGraphManager());
}
if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
{
if (ImGui::BeginTabItem("Variables"))
{
auto hash = pManager->GetDescriptorKey();
auto pDescriptor = AnimationGraphDescriptorManager::Get().GetDescriptor(hash);
if (pDescriptor)
{
const auto* pVariableSet = pGraph->behaviorGraph->animationVariables;
Map<String, bool> bools{};
Map<String, float> floats{};
Map<String, int> ints{};
for (size_t i = 0; i < pDescriptor->BooleanLookUpTable.size(); ++i)
{
const auto idx = pDescriptor->BooleanLookUpTable[i];
bools[s_varMap[idx]] = *reinterpret_cast<bool*>(&pVariableSet->data[idx]);
}
for (size_t i = 0; i < pDescriptor->FloatLookupTable.size(); ++i)
{
const auto idx = pDescriptor->FloatLookupTable[i];
floats[s_varMap[idx]] = *reinterpret_cast<float*>(&pVariableSet->data[idx]);
}
for (size_t i = 0; i < pDescriptor->IntegerLookupTable.size(); ++i)
{
const auto idx = pDescriptor->IntegerLookupTable[i];
ints[s_varMap[idx]] = *reinterpret_cast<int*>(&pVariableSet->data[idx]);
}
if (ImGui::Button("Dump variable values"))
{
std::cout << "Bools: " << std::endl;
for (auto& [name, value] : bools)
{
std::cout << "\t" << name << ": " << value << std::endl;
}
std::cout << "Floats: " << std::endl;
for (auto& [name, value] : floats)
{
std::cout << "\t" << name << ": " << value << std::endl;
}
std::cout << "Integers: " << std::endl;
for (auto& [name, value] : ints)
{
std::cout << "\t" << name << ": " << value << std::endl;
}
}
if (ImGui::CollapsingHeader("Bools", ImGuiTreeNodeFlags_DefaultOpen))
{
for (auto pair : bools)
{
auto name = pair.first;
auto value = pair.second;
int iValue = int(value);
ImGui::InputInt(name.c_str(), &iValue, 0, 0, ImGuiInputTextFlags_ReadOnly);
}
}
if (ImGui::CollapsingHeader("Floats", ImGuiTreeNodeFlags_DefaultOpen))
{
for (auto pair : floats)
{
auto name = pair.first;
auto value = pair.second;
ImGui::InputFloat(name.c_str(), &value, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
}
}
if (ImGui::CollapsingHeader("Integers", ImGuiTreeNodeFlags_DefaultOpen))
{
for (auto pair : ints)
{
auto name = pair.first;
auto value = pair.second;
ImGui::InputInt(name.c_str(), &value, 0, 0, ImGuiInputTextFlags_ReadOnly);
}
}
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Blacklist"))
{
static uint32_t s_selectedBlacklistVar = 0;
static uint32_t s_selected = 0;
ImGui::BeginChild("Blacklisted variables");
int i = 0;
for (auto blacklistedVar : s_blacklist)
{
if (blacklistedVar >= pGraph->behaviorGraph->animationVariables->size)
{
++i;
continue;
}
const auto varName = s_varMap[blacklistedVar];
char name[256];
sprintf_s(name, std::size(name), "k%s (%d)", varName.c_str(), blacklistedVar);
if (ImGui::Selectable(name, s_selectedBlacklistVar == blacklistedVar))
{
s_selectedBlacklistVar = blacklistedVar;
}
if (s_selectedBlacklistVar == blacklistedVar)
{
s_selected = i;
}
++i;
}
ImGui::EndChild();
if (s_selected < s_blacklist.size())
{
if (ImGui::Button("Delete"))
{
s_blacklist.erase(s_blacklist.begin() + s_selected);
}
}
static uint32_t s_blacklistVar = 0;
ImGui::InputInt("New blacklist var:", (int*)&s_blacklistVar, 0, 0);
if (ImGui::Button("Add"))
{
s_blacklist.push_back(s_blacklistVar);
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Control"))
{
ImGui::InputInt("Animation graph count", (int*)&pManager->animationGraphs.size, 0, 0, ImGuiInputTextFlags_ReadOnly);
ImGui::InputInt("Animation graph index", (int*)&pManager->animationGraphIndex, 0, 0, ImGuiInputTextFlags_ReadOnly);
char name[256];
sprintf_s(name, std::size(name), "%s", pGraph->behaviorGraph->stateMachine->name);
ImGui::InputText("Graph state machine name", name, std::size(name), ImGuiInputTextFlags_ReadOnly);
const auto pVariableSet = pGraph->behaviorGraph->animationVariables;
auto pDescriptor = AnimationGraphDescriptorManager::Get().GetDescriptor(pManager->GetDescriptorKey());
static bool toggleVariableRecord = false;
if (ImGui::Button("Toggle variable recording"))
{
toggleVariableRecord = !toggleVariableRecord;
spdlog::info("Toggle variable recording: {}", toggleVariableRecord);
}
if (pVariableSet && toggleVariableRecord)
{
for (auto i = 0u; i < pVariableSet->size; ++i)
{
if (std::find(s_blacklist.begin(), s_blacklist.end(), i) != s_blacklist.end())
continue;
if (pDescriptor && pDescriptor->IsSynced(i))
continue;
auto iter = s_values.find(i);
if (iter == std::end(s_values))
{
s_values[i] = pVariableSet->data[i];
const auto varName = s_varMap[i];
spdlog::info("Variable k{} ({}) initialized to f: {} i: {}", varName, i, *(float*)&pVariableSet->data[i], *(int32_t*)&pVariableSet->data[i]);
}
else if (iter->second != pVariableSet->data[i])
{
const auto varName = s_varMap[i];
float floatCast = *(float*)&pVariableSet->data[i];
int intCast = *(int32_t*)&pVariableSet->data[i];
spdlog::warn("Variable k{} ({}) changed to f: {} i: {}", varName, i, floatCast, intCast);
s_values[i] = pVariableSet->data[i];
s_reusedValues[i] = pVariableSet->data[i];
char varTypeChar = varName[0]; // for guessing type, to see if u can find type char (i, f, b)
if (varTypeChar == 'f')
{
s_valueTypes[i] = 1;
}
else if (varTypeChar == 'i' && varName[1] != 's')
{
s_valueTypes[i] = 2;
}
else if (varTypeChar != 'b' && s_valueTypes[i] != 1) // no char hint to go off of and not assuming float
{
if (intCast > 1000 || intCast < -1000) // arbitrary int threshold
{
s_valueTypes[i] = 1; // assume float
}
else
{
if (intCast > 1 || intCast < 0)
{
s_valueTypes[i] = 2; // assume int
}
}
}
}
}
}
if (ImGui::Button("Dump variables") && pVariableSet)
{
// kinda ugly to iterate 3 times but idc cuz its just for debugging and its a small collection
// BOOLS
std::cout << "{" << std::endl;
for (auto& [key, value] : s_reusedValues)
{
if (s_valueTypes[key] == 0)
{
const auto varName = s_varMap[key];
std::cout << "k" << varName << "," << std::endl;
}
}
// FLOATS
std::cout << "}," << std::endl << "{" << std::endl;
for (auto& [key, value] : s_reusedValues)
{
if (s_valueTypes[key] == 1)
{
const auto varName = s_varMap[key];
std::cout << "k" << varName << "," << std::endl;
}
}
// INTS
std::cout << "}," << std::endl << "{" << std::endl;
for (auto& [key, value] : s_reusedValues)
{
if (s_valueTypes[key] == 2)
{
const auto varName = s_varMap[key];
std::cout << "k" << varName << "," << std::endl;
}
}
std::cout << "}" << std::endl;
}
if (ImGui::Button("Reset recording"))
{
s_values.clear();
s_reusedValues.clear();
spdlog::info("Variable recording has been reset");
}
ImGui::EndTabItem();
}
}
ImGui::End();
}

View file

@ -1,33 +0,0 @@
#include <Services/DebugService.h>
#include <imgui.h>
#include <inttypes.h>
#include <TimeManager.h>
void DebugService::DrawCalendarView()
{
ImGui::Begin("Calendar");
auto* pGameTime = TimeData::Get();
auto year = pGameTime->GameYear->f;
ImGui::InputFloat("Year", &year, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
auto month = pGameTime->GameMonth->f;
ImGui::InputFloat("Month", &month, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
auto day = pGameTime->GameDay->f;
ImGui::InputFloat("Day", &day, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
auto hour = pGameTime->GameHour->f;
ImGui::InputFloat("Hour", &hour, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
auto passed = pGameTime->GameDaysPassed->f;
ImGui::InputFloat("Passed", &passed, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
auto scale = pGameTime->TimeScale->f;
ImGui::InputFloat("Scale", &scale, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::End();
}

View file

@ -1,53 +0,0 @@
#include <Services/DebugService.h>
#include <imgui.h>
#include <inttypes.h>
#include <Games/TES.h>
#include <PlayerCharacter.h>
#include <Forms/TESObjectCELL.h>
#include <Forms/TESWorldSpace.h>
void DebugService::DrawCellView()
{
ImGui::Begin("Cell");
PlayerCharacter* pPlayer = PlayerCharacter::Get();
if (auto* pWorldSpace = pPlayer->GetWorldSpace())
{
if (ImGui::CollapsingHeader("World space", ImGuiTreeNodeFlags_DefaultOpen))
{
const uint32_t worldFormId = pWorldSpace->formID;
ImGui::InputScalar("Id", ImGuiDataType_U32, (void*)&worldFormId, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
char* pName = (char*)pWorldSpace->GetName();
size_t nameLen = strlen(pName);
ImGui::InputText("Name", pName, nameLen, ImGuiInputTextFlags_ReadOnly);
char* pEditorId = (char*)pWorldSpace->GetFormEditorID();
size_t editorIdLen = strlen(pEditorId);
ImGui::InputText("Editor ID", pEditorId, editorIdLen, ImGuiInputTextFlags_ReadOnly);
}
}
if (auto* pCell = pPlayer->parentCell)
{
if (ImGui::CollapsingHeader("Parent cell", ImGuiTreeNodeFlags_DefaultOpen))
{
const uint32_t cellId = pCell->formID;
ImGui::InputScalar("Id", ImGuiDataType_U32, (void*)&cellId, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
char* pName = (char*)pCell->GetName();
size_t nameLen = strlen(pName);
ImGui::InputText("Name", pName, nameLen, ImGuiInputTextFlags_ReadOnly);
char* pEditorId = (char*)pCell->GetFormEditorID();
size_t editorIdLen = strlen(pEditorId);
ImGui::InputText("Editor ID", pEditorId, editorIdLen, ImGuiInputTextFlags_ReadOnly);
}
}
ImGui::End();
}

View file

@ -1,225 +0,0 @@
#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();
}

View file

@ -1,202 +0,0 @@
#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);
}
}
}

View file

@ -1,131 +0,0 @@
#include <imgui.h>
#include <inttypes.h>
#include <Services/DebugService.h>
#include <PlayerCharacter.h>
#include <EquipManager.h>
#include <World.h>
#include <DefaultObjectManager.h>
void DebugService::DrawContainerDebugView()
{
static TESForm* pFetchForm = nullptr;
static Actor* pActor = nullptr;
ImGui::Begin("Inventory");
ImGui::InputScalar("Form ID", ImGuiDataType_U32, &m_formId, 0, 0, "%" PRIx32, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::Button("Look up"))
{
if (m_formId)
{
pFetchForm = TESForm::GetById(m_formId);
if (pFetchForm)
pActor = Cast<Actor>(pFetchForm);
}
}
if (pActor)
{
static Inventory inventory{};
if (ImGui::Button("Fetch inventory"))
inventory = pActor->GetInventory();
int inventoryCount = inventory.Entries.size();
ImGui::InputInt("Inventory count", &inventoryCount, 0, 0, ImGuiInputTextFlags_ReadOnly);
ImGui::BeginChild("Items", ImVec2(0, 200), true);
for (Inventory::Entry& entry : inventory.Entries)
{
std::string itemLabel = fmt::format("{:X}", entry.BaseId.BaseId + entry.BaseId.ModId);
if (!ImGui::CollapsingHeader(itemLabel.c_str()))
continue;
if (ImGui::Button("Equip"))
{
World::Get().GetRunner().Queue(
[entry, actorId = pActor->formID]()
{
auto& modSystem = World::Get().GetModSystem();
uint32_t itemId = modSystem.GetGameId(entry.BaseId);
TESForm* pItem = TESForm::GetById(itemId);
Actor* pActor = Cast<Actor>(TESForm::GetById(actorId));
EquipManager::Get()->Equip(pActor, pItem, nullptr, entry.Count, DefaultObjectManager::Get().rightEquipSlot, false, true, false, false);
});
}
if (ImGui::Button("Unequip"))
{
World::Get().GetRunner().Queue(
[entry, actorId = pActor->formID]()
{
auto& modSystem = World::Get().GetModSystem();
uint32_t itemId = modSystem.GetGameId(entry.BaseId);
TESForm* pItem = TESForm::GetById(itemId);
Actor* pActor = Cast<Actor>(TESForm::GetById(actorId));
EquipManager::Get()->UnEquip(pActor, pItem, nullptr, entry.Count, DefaultObjectManager::Get().rightEquipSlot, false, true, false, false, nullptr);
});
}
int itemCount = entry.Count;
ImGui::InputInt("Item count", &itemCount, 0, 0, ImGuiInputTextFlags_ReadOnly);
int isWorn = entry.ExtraWorn;
ImGui::InputInt("Is worn?", &isWorn, 0, 0, ImGuiInputTextFlags_ReadOnly);
int isWornLeft = entry.ExtraWornLeft;
ImGui::InputInt("Is worn left?", &isWornLeft, 0, 0, ImGuiInputTextFlags_ReadOnly);
int isWeapon = entry.EnchantData.IsWeapon;
ImGui::InputInt("Is weapon?", &isWeapon, 0, 0, ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Health", &entry.ExtraHealth, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
int poisonId = entry.ExtraPoisonId.BaseId + entry.ExtraPoisonId.ModId;
ImGui::InputInt("Poison ID", &poisonId, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
int poisonCount = entry.ExtraPoisonCount;
ImGui::InputInt("Poison count", &poisonCount, 0, 0, ImGuiInputTextFlags_ReadOnly);
int soulLevel = entry.ExtraSoulLevel;
ImGui::InputInt("Soul level", &soulLevel, 0, 0, ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Charge", &entry.ExtraCharge, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
int enchantId = entry.ExtraEnchantId.BaseId + entry.ExtraEnchantId.ModId;
ImGui::InputInt("Enchant ID", &enchantId, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
int enchantCharge = entry.ExtraEnchantCharge;
ImGui::InputInt("Enchant charge", &enchantCharge, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
int isEnchantRemoveUnequip = entry.ExtraEnchantRemoveUnequip;
ImGui::InputInt("Remove enchant on unequip?", &isEnchantRemoveUnequip, 0, 0, ImGuiInputTextFlags_ReadOnly);
if (ImGui::CollapsingHeader("Effects"))
{
for (Inventory::EffectItem& effect : entry.EnchantData.Effects)
{
std::string effectLabel = fmt::format("{:X}", effect.EffectId.BaseId + effect.EffectId.ModId);
if (!ImGui::CollapsingHeader(effectLabel.c_str()))
continue;
ImGui::InputFloat("Magnitude", &effect.Magnitude, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
int area = effect.Area;
ImGui::InputInt("Area", &area, 0, 0, ImGuiInputTextFlags_ReadOnly);
int duration = effect.Duration;
ImGui::InputInt("Duration", &duration, 0, 0, ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Raw cost", &effect.RawCost, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
}
}
ImGui::Separator();
}
ImGui::EndChild();
}
ImGui::End();
}

View file

@ -1,113 +0,0 @@
#include <imgui.h>
#include <inttypes.h>
void DebugService::DrawDragonSpawnerView()
{
ImGui::Begin("Dragon spawner");
if (ImGui::CollapsingHeader("Dragons (level 10)"))
{
if (ImGui::Button("Brown (fire)"))
{
Actor::Spawn(0x1CA03);
}
if (ImGui::Button("Brown (frost)"))
{
Actor::Spawn(0xF80FA);
}
if (ImGui::Button("White (frost)"))
{
Actor::Spawn(0x8BC7F);
}
if (ImGui::Button("Bronze (frost)"))
{
Actor::Spawn(0x8BC7E);
}
}
if (ImGui::CollapsingHeader("Blood dragons (level 20)"))
{
if (ImGui::Button("Blood (fire)"))
{
Actor::Spawn(0xF80FD);
}
if (ImGui::Button("Blood (frost)"))
{
Actor::Spawn(0xF77F8);
}
}
if (ImGui::CollapsingHeader("Frost dragons (level 27-35)"))
{
if (ImGui::Button("Frost"))
{
Actor::Spawn(0x351C3);
}
}
if (ImGui::CollapsingHeader("Elder dragons (level 36-44)"))
{
if (ImGui::Button("Elder (fire)"))
{
Actor::Spawn(0xF811B);
}
if (ImGui::Button("Elder (frost)"))
{
Actor::Spawn(0xF811A);
}
}
if (ImGui::CollapsingHeader("Ancient dragons (level 45-54)"))
{
if (ImGui::Button("Ancient (fire)"))
{
Actor::Spawn(0xF811C);
}
if (ImGui::Button("Ancient (frost)"))
{
Actor::Spawn(0xF811E);
}
}
if (ImGui::CollapsingHeader("Serpentine dragons (level 55-58)"))
{
if (ImGui::Button("Serpentine (fire)"))
{
Actor::Spawn(0x04036134);
}
if (ImGui::Button("Serpentine (frost)"))
{
Actor::Spawn(0x04036133);
}
}
if (ImGui::CollapsingHeader("Revered dragons (level 62)"))
{
if (ImGui::Button("Revered (fire)"))
{
Actor::Spawn(0x02008431);
}
}
if (ImGui::CollapsingHeader("Legendary dragons (level 75)"))
{
if (ImGui::Button("Legendary (fire)"))
{
Actor::Spawn(0x0200C5F5);
}
if (ImGui::Button("Legendary (frost)"))
{
Actor::Spawn(0x0200C5FD);
}
}
if (ImGui::CollapsingHeader("Skeletal dragons (warning: very scary)"))
{
if (ImGui::Button("Skeletal (frost)"))
{
Actor::Spawn(0x9192C);
}
}
ImGui::End();
}

View file

@ -1,217 +0,0 @@
#include <Services/DebugService.h>
#include <Services/CharacterService.h>
#include <AI/AIProcess.h>
#include <PlayerCharacter.h>
#include <Games/ActorExtension.h>
#include <Forms/TESObjectCELL.h>
#include <Events/MoveActorEvent.h>
#include <World.h>
#include <imgui.h>
#include <inttypes.h>
void DebugService::DrawEntitiesView()
{
const auto view = m_world.view<FormIdComponent>();
if (view.empty())
return;
ImGui::SetNextWindowSize(ImVec2(250, 440), ImGuiCond_FirstUseEver);
ImGui::Begin("Entities");
if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
{
if (ImGui::BeginTabItem("Actors"))
{
DisplayEntities();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Objects"))
{
DisplayObjects();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::End();
}
void DebugService::DisplayEntities() noexcept
{
static uint32_t s_selected = 0;
StackAllocator<1 << 12> allocator;
ScopedAllocator _{allocator};
const auto view = m_world.view<FormIdComponent>(entt::exclude<ObjectComponent>);
Vector<entt::entity> entities(view.begin(), view.end());
ImGui::Text("Actor list (%d)", entities.size());
ImGui::BeginChild("Entities", ImVec2(0, 200), true);
int i = 0;
for (auto it : entities)
{
auto& formComponent = view.get<FormIdComponent>(it);
const auto pActor = Cast<Actor>(TESForm::GetById(formComponent.Id));
if (!pActor)
continue;
char name[256];
if (!pActor->baseForm)
strncpy_s(name, "UNNAMED", sizeof(name));
sprintf_s(name, std::size(name), "%s (%x)", pActor->baseForm->GetName(), formComponent.Id);
if (ImGui::Selectable(name, m_formId == formComponent.Id))
m_formId = formComponent.Id;
if (m_formId == formComponent.Id)
s_selected = i;
++i;
}
ImGui::EndChild();
if (s_selected < entities.size())
DisplayEntityPanel(entities[s_selected]);
}
void DebugService::DisplayObjects() noexcept
{
static uint32_t s_selected = 0;
StackAllocator<1 << 12> allocator;
ScopedAllocator _{allocator};
const auto view = m_world.view<FormIdComponent, ObjectComponent>();
Vector<entt::entity> entities(view.begin(), view.end());
ImGui::Text("Object list (%d)", entities.size());
ImGui::BeginChild("Entities", ImVec2(0, 200), true);
int i = 0;
for (auto it : entities)
{
auto& formComponent = view.get<FormIdComponent>(it);
const auto pRefr = Cast<TESObjectREFR>(TESForm::GetById(formComponent.Id));
if (!pRefr || !pRefr->baseForm)
continue;
char name[256];
sprintf_s(name, std::size(name), "%s (%x)", pRefr->baseForm->GetName(), formComponent.Id);
if (ImGui::Selectable(name, m_formId == formComponent.Id))
m_formId = formComponent.Id;
if (m_formId == formComponent.Id)
s_selected = i;
++i;
}
ImGui::EndChild();
}
void DebugService::DisplayEntityPanel(entt::entity aEntity) noexcept
{
const auto pFormIdComponent = m_world.try_get<FormIdComponent>(aEntity);
const auto pLocalComponent = m_world.try_get<LocalComponent>(aEntity);
const auto pRemoteComponent = m_world.try_get<RemoteComponent>(aEntity);
if (pFormIdComponent)
DisplayFormComponent(*pFormIdComponent);
if (pLocalComponent)
DisplayLocalComponent(*pLocalComponent, pFormIdComponent ? pFormIdComponent->Id : 0);
if (pRemoteComponent)
DisplayRemoteComponent(*pRemoteComponent, aEntity, pFormIdComponent ? pFormIdComponent->Id : 0);
}
void DebugService::DisplayFormComponent(FormIdComponent& aFormComponent) const noexcept
{
if (!ImGui::CollapsingHeader("Form Component", ImGuiTreeNodeFlags_DefaultOpen))
return;
const auto pActor = Cast<Actor>(TESForm::GetById(aFormComponent.Id));
if (!pActor)
return;
/*
if (ImGui::Button("Teleport away"))
{
m_world.GetRunner().Queue([id = aFormComponent.Id]() {
Actor* pActor = Cast<Actor>(TESForm::GetById(id));
TESObjectCELL* pCell = Cast<TESObjectCELL>(TESForm::GetById(0x133C6));
NiPoint3 pos{};
pos.x = -313.f;
pos.y = 4.f;
pos.z = 13.f;
pActor->MoveTo(pCell, pos);
});
}
*/
ImGui::InputInt("Game Id", (int*)&aFormComponent.Id, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputFloat3("Position", pActor->position.AsArray(), "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Rotation", pActor->rotation.AsArray(), "%.3f", ImGuiInputTextFlags_ReadOnly);
int isDead = int(pActor->IsDead());
ImGui::InputScalar("Is dead?", ImGuiDataType_U8, &isDead, 0, 0, "%" PRIx8, ImGuiInputTextFlags_ReadOnly);
int isRemote = int(pActor->GetExtension()->IsRemote());
ImGui::InputScalar("Is remote?", ImGuiDataType_U8, &isRemote, 0, 0, "%" PRIx8, ImGuiInputTextFlags_ReadOnly);
auto handle = pActor->GetHandle();
ImGui::InputInt("Handle", (int*)&handle.handle.iBits, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
auto owner = pActor->GetCommandingActor();
int commandingActorId = int(owner ? owner->formID : 0x0);
ImGui::InputScalar("Commanding Actor", ImGuiDataType_U8, &commandingActorId, 0, 0, "%" PRIx8, ImGuiInputTextFlags_ReadOnly);
float attributes[3]{pActor->GetActorValue(24), pActor->GetActorValue(25), pActor->GetActorValue(26)};
ImGui::InputFloat3("Attributes (H/M/S)", attributes, "%.3f", ImGuiInputTextFlags_ReadOnly);
}
void DebugService::DisplayLocalComponent(LocalComponent& aLocalComponent, const uint32_t acFormId) const noexcept
{
if (!ImGui::CollapsingHeader("Local Component", ImGuiTreeNodeFlags_DefaultOpen))
return;
if (ImGui::Button("Teleport to me"))
{
auto* pPlayer = PlayerCharacter::Get();
m_world.GetRunner().Trigger(MoveActorEvent(acFormId, pPlayer->parentCell->formID, pPlayer->position));
}
auto& action = aLocalComponent.CurrentAction;
ImGui::InputInt("Net Id", (int*)&aLocalComponent.Id, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputInt("Action Id", (int*)&action.ActionId, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputInt("Idle Id", (int*)&action.IdleId, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputScalarN("State", ImGuiDataType_U32, &action.State1, 2, nullptr, nullptr, "%x", ImGuiInputTextFlags_ReadOnly);
}
void DebugService::DisplayRemoteComponent(RemoteComponent& aRemoteComponent, const entt::entity acEntity, const uint32_t acFormId) const noexcept
{
if (!ImGui::CollapsingHeader("Remote Component", ImGuiTreeNodeFlags_DefaultOpen))
return;
ImGui::InputInt("Server Id", (int*)&aRemoteComponent.Id, 0, 0, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::Button("Take ownership"))
{
m_world.GetRunner().Queue(
[acEntity, acFormId]()
{
if (auto* pRemoteCompoment = World::Get().try_get<RemoteComponent>(acEntity))
World::Get().GetCharacterService().TakeOwnership(acFormId, pRemoteCompoment->Id, acEntity);
});
}
}

View file

@ -1,77 +0,0 @@
#include <imgui.h>
#include <inttypes.h>
#include <Games/TES.h>
#include <PlayerCharacter.h>
#include <Forms/TESObjectCELL.h>
#include <Forms/TESWorldSpace.h>
#include <Services/DebugService.h>
#include <AI/AIProcess.h>
#include <Misc/MiddleProcess.h>
#include <Effects/ActiveEffect.h>
void DebugService::DrawFormDebugView()
{
static TESForm* pFetchForm = nullptr;
static Actor* pRefr = nullptr;
ImGui::Begin("Form");
ImGui::InputScalar("Form ID", ImGuiDataType_U32, &m_formId, 0, 0, "%" PRIx32, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::Button("Look up"))
{
if (m_formId)
{
pFetchForm = TESForm::GetById(m_formId);
if (pFetchForm)
pRefr = Cast<Actor>(pFetchForm);
}
}
if (pFetchForm)
{
ImGui::InputScalar("Memory address", ImGuiDataType_U64, (void*)&pFetchForm, 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
}
if (pRefr)
{
if (auto* pParentCell = pRefr->GetParentCell())
{
const uint32_t cellId = pParentCell->formID;
ImGui::InputScalar("GetParentCell", ImGuiDataType_U32, (void*)&cellId, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
}
if (auto* pParentCell = pRefr->parentCell)
{
const uint32_t cellId = pParentCell->formID;
ImGui::InputScalar("parentCell", ImGuiDataType_U32, (void*)&cellId, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
}
/*
char name[256];
sprintf_s(name, std::size(name), "%s (%x)", pRefr->baseForm->GetName(), pRefr->formID);
ImGui::InputText("Name", name, std::size(name), ImGuiInputTextFlags_ReadOnly);
for (ActiveEffect* pEffect : *pRefr->currentProcess->middleProcess->ActiveEffects)
{
if (!pEffect)
continue;
if (!ImGui::CollapsingHeader(pEffect->pSpell->fullName.value, ImGuiTreeNodeFlags_DefaultOpen))
continue;
ImGui::InputFloat("Elapsed seconds", &pEffect->fElapsedSeconds, 0, 0, "%.1f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Duration", &pEffect->fDuration, 0, 0, "%.1f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Magnitude", &pEffect->fMagnitude, 0, 0, "%.1f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputInt("Flags", (int*)&pEffect->uiFlags, 0, 0, ImGuiInputTextFlags_ReadOnly);
if (ImGui::Button("Elapse time"))
m_world.GetRunner().Queue([pEffect]() { pEffect->fElapsedSeconds = pEffect->fDuration - 3.f; });
}
*/
}
ImGui::End();
}

View file

@ -1,68 +0,0 @@
#include <Services/DebugService.h>
#include <Services/TransportService.h>
#include <imgui.h>
void DebugService::DrawNetworkView()
{
if (m_transport.IsConnected())
{
ImGui::Begin("Network");
auto stats = m_transport.GetStatistics();
float protocolSent = float(stats.SentBytes) / 1024.f;
float protocolReceived = float(stats.RecvBytes) / 1024.f;
float uncompressedSent = float(stats.UncompressedSentBytes) / 1024.f;
float uncompressedReceived = float(stats.UncompressedRecvBytes) / 1024.f;
// Fill an array of contiguous float values to plot
// Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
// and the sizeof() of your structure in the "stride" parameter.
static float values[90] = {};
static int values_offset = 0;
static double refresh_time = 0.0;
if (refresh_time == 0.0)
refresh_time = ImGui::GetTime();
while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo
{
values[values_offset] = protocolSent;
values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);
refresh_time += 1.0f;
}
// Plots can display overlay texts
// (in this example, we will display an average value)
{
float average = 0.0f;
for (int n = 0; n < IM_ARRAYSIZE(values); n++)
average += values[n];
average /= (float)IM_ARRAYSIZE(values);
ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, nullptr, 0.f, FLT_MAX, ImVec2(0, 160.0f));
}
/*
float width = -ImGui::CalcItemWidth();
ImGui::PushItemWidth(width);
auto status = GetConnectionStatus();
status.m_flOutBytesPerSec /= 1024.f;
status.m_flInBytesPerSec /= 1024.f;
ImGui::InputFloat("Net Out kBps", (float*)&status.m_flOutBytesPerSec, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Net In kBps", (float*)&status.m_flInBytesPerSec, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Protocol Out kBps", (float*)&protocolSent, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Protocol In kBps", (float*)&protocolReceived, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("User Out kBps", (float*)&uncompressedSent, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("User In kBps", (float*)&uncompressedReceived, 0.f, 0.f, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::PopItemWidth();
*/
ImGui::End();
}
// online indicator
ImGui::GetBackgroundDrawList()->AddRectFilled(ImVec2(23.f, 23.f), ImVec2(50.f, 50.f), m_transport.IsConnected() ? ImColor(0, 230, 64) : ImColor(240, 52, 52));
}

View file

@ -1,151 +0,0 @@
#include <Services/DebugService.h>
#include <Messages/PartyKickRequest.h>
#include <Messages/PartyChangeLeaderRequest.h>
#include <Messages/PartyInviteRequest.h>
#include <Messages/PartyAcceptInviteRequest.h>
#include <Messages/PartyLeaveRequest.h>
#include <Messages/PartyCreateRequest.h>
#include <Messages/TeleportCommandRequest.h>
#include <World.h>
#include <imgui.h>
void DebugService::DrawPartyView()
{
if (!m_transport.IsConnected())
return;
ImGui::Begin("Party");
auto& partyService = m_world.GetPartyService();
auto& players = partyService.GetPlayers();
auto& members = partyService.GetPartyMembers();
auto& invitations = partyService.GetInvitations();
if (partyService.IsInParty())
{
ImGui::Text("Party Members");
String pName{"You"};
if (partyService.IsLeader())
{
pName += " (Leader)";
}
ImGui::BulletText(pName.c_str());
for (auto& playerId : members)
{
ImGui::PushID(playerId);
auto playerEntry = players.find(playerId);
if (playerEntry != players.end())
{
auto playerName = playerEntry.value();
if (playerId == partyService.GetLeaderPlayerId())
{
playerName += " (Leader)";
}
ImGui::BulletText(playerName.c_str());
ImGui::SameLine(200);
if (ImGui::Button("Teleport"))
{
TeleportCommandRequest request{};
request.TargetPlayer = playerEntry.value();
m_transport.Send(request);
}
if (partyService.IsLeader())
{
ImGui::SameLine();
if (ImGui::Button("Kick"))
{
PartyKickRequest kickMessage;
kickMessage.PartyMemberPlayerId = playerEntry.key();
m_transport.Send(kickMessage);
}
ImGui::SameLine();
if (ImGui::Button("Make Leader"))
{
PartyChangeLeaderRequest changeMessage;
changeMessage.PartyMemberPlayerId = playerEntry.key();
m_transport.Send(changeMessage);
}
}
}
ImGui::PopID();
}
if (partyService.IsLeader())
{
ImGui::NewLine();
ImGui::Text("Other Players");
auto playerCount = 0;
for (auto& player : partyService.GetPlayers())
{
if (std::find(std::begin(members), std::end(members), player.first) != std::end(members))
continue;
playerCount++;
ImGui::BulletText(player.second.c_str());
ImGui::SameLine(100);
if (ImGui::Button("Invite"))
{
PartyInviteRequest request;
request.PlayerId = player.first;
m_transport.Send(request);
}
}
if (playerCount == 0)
{
ImGui::BulletText("<No one online>");
}
}
}
for (auto& player : players)
{
auto itor = invitations.find(player.first);
if (itor != std::end(invitations))
{
if (std::find(std::begin(members), std::end(members), player.first) != std::end(members))
continue;
ImGui::Text(player.second.c_str());
ImGui::SameLine(100);
if (ImGui::Button("Accept"))
{
invitations.erase(itor);
PartyAcceptInviteRequest message;
message.InviterId = player.first;
m_transport.Send(message);
}
}
}
ImGui::NewLine();
if (partyService.IsInParty())
{
if (ImGui::Button("Leave"))
{
PartyLeaveRequest request;
m_transport.Send(request);
}
}
else
{
if (ImGui::Button("Create Party"))
{
PartyCreateRequest request;
m_transport.Send(request);
}
}
ImGui::End();
}

View file

@ -1,92 +0,0 @@
#include <Services/DebugService.h>
#include <imgui.h>
#include <inttypes.h>
#include <Games/TES.h>
#include <PlayerCharacter.h>
#include <Forms/TESObjectCELL.h>
#include <Forms/TESWorldSpace.h>
void DebugService::DrawPlayerDebugView()
{
PlayerCharacter* pPlayer = PlayerCharacter::Get();
if (!pPlayer)
{
return;
}
ImGui::Begin("Player");
auto pLeftWeapon = pPlayer->GetEquippedWeapon(0);
auto pRightWeapon = pPlayer->GetEquippedWeapon(1);
uint32_t leftId = pLeftWeapon ? pLeftWeapon->formID : 0;
uint32_t rightId = pRightWeapon ? pRightWeapon->formID : 0;
ImGui::InputScalar("Left Item", ImGuiDataType_U32, (void*)&leftId, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("Right Item", ImGuiDataType_U32, (void*)&rightId, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
leftId = pPlayer->magicItems[0] ? pPlayer->magicItems[0]->formID : 0;
rightId = pPlayer->magicItems[1] ? pPlayer->magicItems[1]->formID : 0;
ImGui::InputScalar("Right Magic", ImGuiDataType_U32, (void*)&rightId, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("Left Magic", ImGuiDataType_U32, (void*)&leftId, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
auto* leftHandCaster = pPlayer->GetMagicCaster(MagicSystem::CastingSource::LEFT_HAND);
auto* rightHandCaster = pPlayer->GetMagicCaster(MagicSystem::CastingSource::RIGHT_HAND);
auto* otherHandCaster = pPlayer->GetMagicCaster(MagicSystem::CastingSource::OTHER);
auto* instantHandCaster = pPlayer->GetMagicCaster(MagicSystem::CastingSource::INSTANT);
ImGui::InputScalar("leftHandCaster", ImGuiDataType_U64, (void*)&leftHandCaster, 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("rightHandCaster", ImGuiDataType_U64, (void*)&rightHandCaster, 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("otherHandCaster", ImGuiDataType_U64, (void*)&otherHandCaster, 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("instantHandCaster", ImGuiDataType_U64, (void*)&instantHandCaster, 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("leftHandCasterSpell", ImGuiDataType_U64, (void*)&(leftHandCaster->pCurrentSpell), 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("rightHandCasterSpell", ImGuiDataType_U64, (void*)&(rightHandCaster->pCurrentSpell), 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("otherHandCasterSpell", ImGuiDataType_U64, (void*)&(otherHandCaster->pCurrentSpell), 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
ImGui::InputScalar("instantHandCasterSpell", ImGuiDataType_U64, (void*)&(instantHandCaster->pCurrentSpell), 0, 0, "%" PRIx64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
uint32_t shoutId = pPlayer->equippedShout ? pPlayer->equippedShout->formID : 0;
ImGui::InputScalar("Shout", ImGuiDataType_U32, (void*)&shoutId, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
if (auto pWorldSpace = pPlayer->GetWorldSpace())
{
auto worldFormId = pWorldSpace->formID;
ImGui::InputScalar("Worldspace", ImGuiDataType_U32, (void*)&worldFormId, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
}
if (auto pCell = pPlayer->GetParentCell())
{
auto cellFormId = pCell->formID;
ImGui::InputScalar("Cell Id", ImGuiDataType_U32, (void*)&cellFormId, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
}
if (const auto playerParentCell = pPlayer->parentCell)
{
ImGui::InputScalar("Player parent cell", ImGuiDataType_U32, (void*)&playerParentCell->formID, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
}
if (auto* pTES = TES::Get())
{
int32_t playerGrid[2] = {pTES->currentGridX, pTES->currentGridY};
int32_t centerGrid[2] = {pTES->centerGridX, pTES->centerGridY};
ImGui::InputInt2("Player grid", playerGrid, ImGuiInputTextFlags_ReadOnly);
ImGui::InputInt2("Center grid", centerGrid, ImGuiInputTextFlags_ReadOnly);
}
auto& modSystem = m_world.GetModSystem();
if (ImGui::CollapsingHeader("Worn armor"))
{
Inventory wornArmor{};
wornArmor = pPlayer->GetWornArmor();
for (const auto& armor : wornArmor.Entries)
{
const uint32_t armorId = armor.BaseId.BaseId;
ImGui::InputScalar("Item id", ImGuiDataType_U32, (void*)&armorId, nullptr, nullptr, "%" PRIx32, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_CharsHexadecimal);
}
}
ImGui::End();
}

View file

@ -1,76 +0,0 @@
#include <Services/DebugService.h>
#include <Games/TES.h>
#include <imgui.h>
#include <inttypes.h>
void DebugService::DrawProcessView()
{
ImGui::Begin("Processes");
ImGui::PushItemWidth(100.f);
int id = 0;
ProcessLists* pProcesses = ProcessLists::Get();
ImGui::InputScalar("Processing high?", ImGuiDataType_U8, &pProcesses->bProcessHigh, 0, 0, "%" PRIx8, ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
ImGui::PushID(id);
if (ImGui::Button("Toggle"))
{
m_world.GetRunner().Queue([pProcesses]() { pProcesses->bProcessHigh = !pProcesses->bProcessHigh; });
}
ImGui::PopID();
id++;
ImGui::InputScalar("Processing Low?", ImGuiDataType_U8, &pProcesses->bProcessLow, 0, 0, "%" PRIx8, ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
ImGui::PushID(id);
if (ImGui::Button("Toggle"))
{
m_world.GetRunner().Queue([pProcesses]() { pProcesses->bProcessLow = !pProcesses->bProcessLow; });
}
ImGui::PopID();
id++;
ImGui::InputScalar("Processing middle high?", ImGuiDataType_U8, &pProcesses->bProcessMHigh, 0, 0, "%" PRIx8, ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
ImGui::PushID(id);
if (ImGui::Button("Toggle"))
{
m_world.GetRunner().Queue([pProcesses]() { pProcesses->bProcessMHigh = !pProcesses->bProcessMHigh; });
}
ImGui::PopID();
id++;
ImGui::InputScalar("Processing middle low?", ImGuiDataType_U8, &pProcesses->bProcessMLow, 0, 0, "%" PRIx8, ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
ImGui::PushID(id);
if (ImGui::Button("Toggle"))
{
m_world.GetRunner().Queue([pProcesses]() { pProcesses->bProcessMLow = !pProcesses->bProcessMLow; });
}
ImGui::PopID();
id++;
ImGui::InputScalar("Processing schedule?", ImGuiDataType_U8, &pProcesses->bProcessSchedule, 0, 0, "%" PRIx8, ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
ImGui::PushID(id);
if (ImGui::Button("Toggle"))
{
m_world.GetRunner().Queue([pProcesses]() { pProcesses->bProcessSchedule = !pProcesses->bProcessSchedule; });
}
ImGui::PopID();
id++;
ImGui::PopItemWidth();
ImGui::End();
}

View file

@ -1,103 +0,0 @@
#include <Services/DebugService.h>
#include <Services/QuestService.h>
#include <Services/PapyrusService.h>
#include <Services/PartyService.h>
#include <PlayerCharacter.h>
#include <Forms/TESQuest.h>
#include <Forms/TESObjectCELL.h>
#include <Events/MoveActorEvent.h>
#include <imgui.h>
void DebugService::DrawQuestDebugView()
{
auto* pPlayer = PlayerCharacter::Get();
if (!pPlayer)
return;
ImGui::SetNextWindowSize(ImVec2(250, 440), ImGuiCond_FirstUseEver);
ImGui::Begin("Quests");
// TODO(cosideci): yes I'll refactor this
if (!m_world.GetPartyService().IsLeader())
{
ImGui::Text("You need to be the quest leader to access the quest debugger.");
}
else
{
Set<uint32_t> foundQuests{};
for (auto& objective : pPlayer->objectives)
{
TESQuest* pQuest = objective.instance->quest;
if (!pQuest)
continue;
if (QuestService::IsNonSyncableQuest(pQuest))
continue;
if (foundQuests.contains(pQuest->formID))
continue;
foundQuests.insert(pQuest->formID);
if (!pQuest->IsActive())
continue;
char questName[256];
sprintf_s(questName, std::size(questName), "%s (%s)", pQuest->fullName.value.AsAscii(), pQuest->idName.AsAscii());
if (!ImGui::CollapsingHeader(questName))
continue;
if (ImGui::CollapsingHeader("Stages"))
{
for (auto* pStage : pQuest->stages)
{
ImGui::TextColored({0.f, 255.f, 255.f, 255.f}, "Stage: %d, is done? %s", pStage->stageIndex, pStage->IsDone() ? "true" : "false");
char setStage[64];
sprintf_s(setStage, std::size(setStage), "Set stage (%d)", pStage->stageIndex);
if (ImGui::Button(setStage))
pQuest->ScriptSetStage(pStage->stageIndex);
}
}
if (ImGui::CollapsingHeader("Actors"))
{
Set<uint32_t> foundActors{};
for (BGSScene* pScene : pQuest->scenes)
{
for (uint32_t actorId : pScene->actorIds)
{
Actor* pActor = Cast<Actor>(pQuest->GetAliasedRef(actorId));
if (!pActor)
continue;
if (foundActors.contains(pActor->formID))
continue;
foundActors.insert(pActor->formID);
char name[256];
sprintf_s(name, std::size(name), "%s (%x)", pActor->baseForm->GetName(), pActor->formID);
ImGui::BulletText(name);
ImGui::PushID(pActor->formID);
if (ImGui::Button("Teleport to me"))
{
auto* pPlayer = PlayerCharacter::Get();
m_world.GetRunner().Trigger(MoveActorEvent(pActor->formID, pPlayer->parentCell->formID, pPlayer->position));
}
ImGui::PopID();
}
}
}
}
}
ImGui::End();
}

View file

@ -1,33 +0,0 @@
#include <Services/DebugService.h>
#include <imgui.h>
#include <PlayerCharacter.h>
void DebugService::DrawSkillView()
{
ImGui::Begin("Skills");
PlayerCharacter* pPlayer = PlayerCharacter::Get();
Skills* pSkills = *pPlayer->pSkills;
ImGui::InputFloat("Global XP", &pSkills->xp, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Global level threshold", &pSkills->levelThreshold, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
for (int i = 0; i < Skills::kTotal; i++)
{
const char* skillString = Skills::GetSkillString((Skills::Skill)i);
if (!ImGui::CollapsingHeader(skillString, ImGuiTreeNodeFlags_DefaultOpen))
continue;
Skills::SkillData pSkill = pSkills->skills[i];
ImGui::InputFloat("Level", &pSkill.level, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("XP", &pSkill.xp, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Level threshold", &pSkill.levelThreshold, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
uint32_t legendaryLevel = pSkills->legendaryLevels[i];
ImGui::InputScalar("Legendary level", ImGuiDataType_U32, (void*)&legendaryLevel, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
}
ImGui::End();
}

View file

@ -1,55 +0,0 @@
#include <Services/DebugService.h>
#include <imgui.h>
#include <inttypes.h>
#include <Sky/Sky.h>
#include <Forms/TESWeather.h>
void DebugService::DrawWeatherView()
{
ImGui::Begin("Weather");
Sky* pSky = Sky::Get();
TESWeather* pCurrentWeather = pSky->GetWeather();
if (pCurrentWeather)
{
ImGui::InputScalar("Current weather ID", ImGuiDataType_U32, &pCurrentWeather->formID, 0, 0, "%" PRIx32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly);
}
ImGui::Separator();
ImGui::InputScalar("Weather updates enabled?", ImGuiDataType_U8, &Sky::s_shouldUpdateWeather, 0, 0, "%" PRIx8, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::Button("Toggle weather updates"))
Sky::s_shouldUpdateWeather = !Sky::s_shouldUpdateWeather;
ImGui::Separator();
static uint32_t s_weatherId = 0;
ImGui::InputScalar("New weather ID", ImGuiDataType_U32, &s_weatherId, 0, 0, "%" PRIx32, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::Button("Set weather"))
{
TESWeather* pWeather = Cast<TESWeather>(TESForm::GetById(s_weatherId));
if (pWeather)
pSky->SetWeather(pWeather);
}
if (ImGui::Button("Force weather"))
{
TESWeather* pWeather = Cast<TESWeather>(TESForm::GetById(s_weatherId));
if (pWeather)
pSky->ForceWeather(pWeather);
}
ImGui::Separator();
if (ImGui::Button("Reset weather"))
{
pSky->ResetWeather();
}
ImGui::End();
}

View file

@ -1,19 +0,0 @@
<div *ngIf="isShown$ | async" class="app-debug">
<div *ngIf="debugData$ | async as debugData">
<span>
<fa-icon [icon]="faWifi"></fa-icon>
<b>{{ debugData.RTT }}</b> ms,
<b>{{ debugData.packetLoss }}%</b> Loss&nbsp;
</span>
<span>
<fa-icon [icon]="faArrowDown"></fa-icon>
<b>{{ debugData.receivedBandwidth / 1024 | number: '.0-2' }}</b> kB/s,
<b>{{ debugData.numPacketsReceived | number: '.0-2' }}</b> p/s&nbsp;
</span>
<span>
<fa-icon [icon]="faArrowUp"></fa-icon>
<b>{{ debugData.sentBandwidth / 1024 | number: '.0-2' }}</b> kB/s,
<b>{{ debugData.numPacketsSent | number: '.0-2' }}</b> p/s&nbsp;
</span>
</div>
</div>

View file

@ -1,25 +0,0 @@
.app-debug {
display: flex;
align-items: center;
justify-content: flex-start;
height: 1.6vw;
padding: 1px;
color: rgb(196, 196, 196);
b {
color: white;
}
fa-icon {
margin-right: 0.2vw;
margin-left: 0.15vw;
color: white;
}
span {
padding-top: 0.2vw;
padding-bottom: 0.2vw;
border: solid grey 1px;
background-color: rgba($color: #000000, $alpha: 0.5);
}
}

View file

@ -1,34 +0,0 @@
import { Component } from '@angular/core';
import {
faArrowDown,
faArrowUp,
faWifi,
IconDefinition,
} from '@fortawesome/free-solid-svg-icons';
import { Observable, startWith } from 'rxjs';
import { SettingService } from 'src/app/services/setting.service';
import { Debug } from '../../models/debug';
import { ClientService } from '../../services/client.service';
@Component({
selector: 'app-debug',
templateUrl: './debug.component.html',
styleUrls: ['./debug.component.scss'],
})
export class DebugComponent {
/* ### ICONS ### */
faWifi: IconDefinition = faWifi;
faArrowDown: IconDefinition = faArrowDown;
faArrowUp: IconDefinition = faArrowUp;
isShown$: Observable<boolean>;
debugData$: Observable<Debug>;
constructor(
private readonly client: ClientService,
private readonly settingService: SettingService,
) {
this.isShown$ = this.settingService.settings.isDebugShown;
this.debugData$ = this.client.debugDataChange.pipe(startWith(new Debug()));
}
}