1
Fork 0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

165 lines
9.2 KiB

#include "HeroMenuPatch.h"
RE::BSEventNotifyControl HeroMenuPatch::ProcessEvent_Hook(RE::InputEvent** a_event, RE::BSTEventSource<RE::InputEvent*>* a_source)
{
if (RE::UI::GetSingleton()->GameIsPaused() && a_event && *a_event) {
auto buttonEvent = (*a_event)->AsButtonEvent();
if (buttonEvent && buttonEvent->IsDown() && RE::UI::GetSingleton()->IsMenuOpen("CustomMenu")) {
auto& eventName = (*a_event)->QUserEvent();
auto userEvents = RE::UserEvents::GetSingleton();
if (eventName == userEvents->cancel || eventName == userEvents->quickStats) {
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
if (uiMovie && std::strstr(uiMovie->GetMovieDef()->GetFileURL(), "00e_heromenu.swf") != NULL) {
RE::UIMessageQueue::GetSingleton()->AddMessage("CustomMenu", RE::UI_MESSAGE_TYPE::kHide, nullptr);
}
}
}
}
return _ProcessEvent(this, a_event, a_source);
}
void HeroMenuPatch::Install()
{
// Patch MenuControls to close the menu
REL::Relocation<std::uintptr_t> vTable(RE::VTABLE_MenuControls[0]);
_ProcessEvent = vTable.write_vfunc(0x1, &HeroMenuPatch::ProcessEvent_Hook);
// Patch the Quick Stats hotkey to open the menu
// Checked: 1.5.97, 1.6.640, 1.6.659, 1.6.1130
REL::Relocation<std::uintptr_t> target{ REL::RelocationID(51400, 52249), REL::Relocate(0x41E, 0x421) };
REL::safe_fill(target.address(), REL::NOP, 45);
SKSE::AllocTrampoline(14);
_OpenStats = SKSE::GetTrampoline().write_call<5>(target.address(), OpenStats);
REL::Relocation<std::uintptr_t> target2{ REL::RelocationID(51400, 52249), REL::Relocate(0x436, 0x439) };
std::uint8_t code[] = { 0x40, 0xB5, 0x01 };
REL::safe_write(target2.address(), code, sizeof(code));
}
uint64_t HeroMenuPatch::OpenStats(uint32_t arg_1, uint32_t arg_2, uint32_t arg_3, uint32_t arg_4, uint32_t arg_5)
{
OpenHeroMenu();
return 0;
}
void HeroMenuPatch::OpenHeroMenu()
{
RE::BSTSmartPointer<RE::BSScript::IStackCallbackFunctor> stackCallback;
RE::BSScript::Internal::VirtualMachine::GetSingleton()->DispatchStaticCall("UI", "OpenCustomMenu", RE::MakeFunctionArguments<RE::BSString, std::uint32_t>("00e_heromenu", 0), stackCallback);
}
void HeroMenuPatch::FillMenuValues()
{
class ScriptClassNameCallback : public RE::BSScript::IStackCallbackFunctor
{
public:
void operator()(RE::BSScript::Variable a_result)
{
if (a_result.IsString()) {
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
if (uiMovie) {
uiMovie->SetVariable("heromenu_mc.stats.playerclass.stat_value.text", a_result.GetString(), RE::GFxMovie::SetVarType::kNormal);
}
}
}
bool CanSave() { return false; }
void SetObject(const RE::BSTSmartPointer<RE::BSScript::Object>&) {}
};
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
if (!uiMovie) {
return;
}
// Fit the movie into the screen (widescreen support)
uiMovie->SetViewScaleMode(RE::BSScaleformManager::ScaleModeType::kShowAll);
const auto dataHandler = RE::TESDataHandler::GetSingleton();
RE::TESGlobal* playerLevel = dataHandler->LookupForm<RE::TESGlobal>(0x12595, "Skyrim.esm");
RE::TESGlobal* playerExp = dataHandler->LookupForm<RE::TESGlobal>(0x12596, "Skyrim.esm");
RE::TESGlobal* EXPMultSlope = dataHandler->LookupForm<RE::TESGlobal>(0xD0EDB, "Skyrim.esm");
RE::TESGlobal* EXPMult = dataHandler->LookupForm<RE::TESGlobal>(0x8D2B, "Skyrim.esm");
RE::TESGlobal* Lernpunkte = dataHandler->LookupForm<RE::TESGlobal>(0x31ACB, "Skyrim.esm");
RE::TESGlobal* Handwerkspunkte = dataHandler->LookupForm<RE::TESGlobal>(0x85A79, "Skyrim.esm");
RE::TESGlobal* TalentPoints = dataHandler->LookupForm<RE::TESGlobal>(0x5BCFA, "Skyrim.esm");
auto player = RE::PlayerCharacter::GetSingleton();
auto playerAV = player->AsActorValueOwner();
float fEXPNeededForCurrentLevel = ComputeNeededExpPoints(playerLevel->value - 1, EXPMultSlope->value, EXPMult->value);
float fEXPNeededForNextLevel = ComputeNeededExpPoints(playerLevel->value, EXPMultSlope->value, EXPMult->value);
RE::GFxValue args[2];
args[0].SetString(player->GetDisplayFullName());
args[1].SetString("");
uiMovie->Invoke("heromenu_mc.SetStringValues", nullptr, args, 2);
RE::BSTSmartPointer<RE::BSScript::IStackCallbackFunctor> stackCallback{ new ScriptClassNameCallback };
RE::BSScript::Internal::VirtualMachine::GetSingleton()->DispatchStaticCall("_00E_AffinityControl", "GetPlayerClassNameGlobal", RE::MakeFunctionArguments(), stackCallback);
RE::GFxValue args2[33];
args2[0].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kHealth));
args2[1].SetNumber(playerAV->GetActorValue(RE::ActorValue::kHealth));
args2[2].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kMagicka));
args2[3].SetNumber(playerAV->GetActorValue(RE::ActorValue::kMagicka));
args2[4].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kStamina));
args2[5].SetNumber(playerAV->GetActorValue(RE::ActorValue::kStamina));
args2[6].SetNumber(-playerAV->GetActorValue(RE::ActorValue::kLastFlattered));
args2[7].SetNumber(fEXPNeededForNextLevel);
args2[8].SetNumber(playerExp->value);
args2[9].SetNumber(playerLevel->value);
args2[10].SetNumber(Lernpunkte->value);
args2[11].SetNumber(Handwerkspunkte->value);
args2[12].SetNumber(TalentPoints->value);
args2[13].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kIllusion));
args2[14].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kDestruction));
args2[15].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kAlteration));
args2[16].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kOneHanded));
args2[17].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kBlock));
args2[18].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kArchery));
args2[19].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kConjuration));
args2[20].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kRestoration));
args2[21].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kTwoHanded));
args2[22].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kLightArmor));
args2[23].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kHeavyArmor));
args2[24].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kSneak));
args2[25].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kAlchemy));
args2[26].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kPickpocket));
args2[27].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kLockpicking));
args2[28].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kEnchanting));
args2[29].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kSmithing));
args2[30].SetNumber(playerAV->GetBaseActorValue(RE::ActorValue::kSpeech));
args2[31].SetNumber(playerExp->value - fEXPNeededForCurrentLevel);
args2[32].SetNumber(fEXPNeededForNextLevel - fEXPNeededForCurrentLevel);
uiMovie->Invoke("heromenu_mc.SetIntValues", nullptr, args2, 33);
RE::GFxValue args3[21];
args3[0].SetNumber(0);
args3[1].SetNumber(0);
args3[2].SetNumber(0);
args3[3].SetNumber(playerAV->GetActorValue(RE::ActorValue::kIllusion) - playerAV->GetBaseActorValue(RE::ActorValue::kIllusion));
args3[4].SetNumber(playerAV->GetActorValue(RE::ActorValue::kDestruction) - playerAV->GetBaseActorValue(RE::ActorValue::kDestruction));
args3[5].SetNumber(playerAV->GetActorValue(RE::ActorValue::kAlteration) - playerAV->GetBaseActorValue(RE::ActorValue::kAlteration));
args3[6].SetNumber(playerAV->GetActorValue(RE::ActorValue::kOneHanded) - playerAV->GetBaseActorValue(RE::ActorValue::kOneHanded));
args3[7].SetNumber(playerAV->GetActorValue(RE::ActorValue::kBlock) - playerAV->GetBaseActorValue(RE::ActorValue::kBlock));
args3[8].SetNumber(playerAV->GetActorValue(RE::ActorValue::kArchery) - playerAV->GetBaseActorValue(RE::ActorValue::kArchery));
args3[9].SetNumber(playerAV->GetActorValue(RE::ActorValue::kConjuration) - playerAV->GetBaseActorValue(RE::ActorValue::kConjuration));
args3[10].SetNumber(playerAV->GetActorValue(RE::ActorValue::kRestoration) - playerAV->GetBaseActorValue(RE::ActorValue::kRestoration));
args3[11].SetNumber(playerAV->GetActorValue(RE::ActorValue::kTwoHanded) - playerAV->GetBaseActorValue(RE::ActorValue::kTwoHanded));
args3[12].SetNumber(playerAV->GetActorValue(RE::ActorValue::kLightArmor) - playerAV->GetBaseActorValue(RE::ActorValue::kLightArmor));
args3[13].SetNumber(playerAV->GetActorValue(RE::ActorValue::kHeavyArmor) - playerAV->GetBaseActorValue(RE::ActorValue::kHeavyArmor));
args3[14].SetNumber(playerAV->GetActorValue(RE::ActorValue::kSneak) - playerAV->GetBaseActorValue(RE::ActorValue::kSneak));
args3[15].SetNumber(playerAV->GetActorValue(RE::ActorValue::kAlchemy) - playerAV->GetBaseActorValue(RE::ActorValue::kAlchemy));
args3[16].SetNumber(playerAV->GetActorValue(RE::ActorValue::kPickpocket) - playerAV->GetBaseActorValue(RE::ActorValue::kPickpocket));
args3[17].SetNumber(playerAV->GetActorValue(RE::ActorValue::kLockpicking) - playerAV->GetBaseActorValue(RE::ActorValue::kLockpicking));
args3[18].SetNumber(playerAV->GetActorValue(RE::ActorValue::kEnchanting) - playerAV->GetBaseActorValue(RE::ActorValue::kEnchanting));
args3[19].SetNumber(playerAV->GetActorValue(RE::ActorValue::kSmithing) - playerAV->GetBaseActorValue(RE::ActorValue::kSmithing));
args3[20].SetNumber(playerAV->GetActorValue(RE::ActorValue::kSpeech) - playerAV->GetBaseActorValue(RE::ActorValue::kSpeech));
uiMovie->Invoke("heromenu_mc.SetModifier", nullptr, args3, 21);
}