Hero menu: moved handling to DLL, opens on Quick Stats hotkey, added widescreen support
This commit is contained in:
parent
a72dd6611e
commit
ef56d468c5
Binary file not shown.
BIN
SKSE/Plugins/EnderalSE.dll
(Stored with Git LFS)
BIN
SKSE/Plugins/EnderalSE.dll
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
BIN
scripts/_00E_Func_ComputeNeededExp.pex
Normal file
BIN
scripts/_00E_Func_ComputeNeededExp.pex
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -26,6 +26,8 @@ set(sources
|
|||||||
src/Main.cpp
|
src/Main.cpp
|
||||||
src/EventListener.cpp
|
src/EventListener.cpp
|
||||||
src/Papyrus.cpp
|
src/Papyrus.cpp
|
||||||
|
src/Patches/TweenMenuPatch.cpp
|
||||||
|
src/Patches/HeroMenuPatch.cpp
|
||||||
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "EventListener.h"
|
#include "EventListener.h"
|
||||||
|
#include "Patches/HeroMenuPatch.h"
|
||||||
|
|
||||||
auto EventListener::GetSingleton() -> EventListener*
|
auto EventListener::GetSingleton() -> EventListener*
|
||||||
{
|
{
|
||||||
@ -22,9 +23,6 @@ auto EventListener::ProcessEvent(
|
|||||||
if (a_event->eventName == "Enderal_OpenHelpURL") {
|
if (a_event->eventName == "Enderal_OpenHelpURL") {
|
||||||
ShowWindow(GetForegroundWindow(), SW_MINIMIZE);
|
ShowWindow(GetForegroundWindow(), SW_MINIMIZE);
|
||||||
ShellExecuteA(NULL, "open", "https://go.eddoursul.win/enderal-se-support", NULL, NULL, SW_SHOWNORMAL);
|
ShellExecuteA(NULL, "open", "https://go.eddoursul.win/enderal-se-support", NULL, NULL, SW_SHOWNORMAL);
|
||||||
|
|
||||||
//} else if (a_event->eventName == "Enderal_OpenQuestJournal") {
|
|
||||||
// RE::UIMessageQueue::GetSingleton()->AddMessage(RE::InterfaceStrings::GetSingleton()->journalMenu, RE::UI_MESSAGE_TYPE::kShow, 0i64);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return RE::BSEventNotifyControl::kContinue;
|
return RE::BSEventNotifyControl::kContinue;
|
||||||
@ -49,8 +47,14 @@ auto EventListener::ProcessEvent(
|
|||||||
if (a_event->menuName == RE::DialogueMenu::MENU_NAME) {
|
if (a_event->menuName == RE::DialogueMenu::MENU_NAME) {
|
||||||
// Make sure Tab is not blocked. If it should be, a Papyrus script will block it a few frames later.
|
// Make sure Tab is not blocked. If it should be, a Papyrus script will block it a few frames later.
|
||||||
DialogueMenuPatch::BlockTab(false);
|
DialogueMenuPatch::BlockTab(false);
|
||||||
} else if (a_event->menuName == "CustomMenu" && RE::UI::GetSingleton()->IsMenuOpen(RE::TweenMenu::MENU_NAME)) {
|
} else if (a_event->menuName == "CustomMenu") {
|
||||||
CloseTweenMenu();
|
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
|
||||||
|
if (uiMovie && std::strstr(uiMovie->GetMovieDef()->GetFileURL(), "00e_heromenu.swf") != NULL) {
|
||||||
|
if (RE::UI::GetSingleton()->IsMenuOpen(RE::TweenMenu::MENU_NAME)) {
|
||||||
|
CloseTweenMenu();
|
||||||
|
}
|
||||||
|
HeroMenuPatch::FillMenuValues();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (a_event->menuName == RE::DialogueMenu::MENU_NAME) {
|
if (a_event->menuName == RE::DialogueMenu::MENU_NAME) {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "BinkInterruptPatch.h"
|
#include "BinkInterruptPatch.h"
|
||||||
#include "DialogueMenuPatch.h"
|
#include "DialogueMenuPatch.h"
|
||||||
#include "Patches/TweenMenuPatch.h"
|
#include "Patches/TweenMenuPatch.h"
|
||||||
|
#include "Patches/HeroMenuPatch.h"
|
||||||
|
|
||||||
using namespace SKSE;
|
using namespace SKSE;
|
||||||
|
|
||||||
@ -170,6 +171,7 @@ SKSEPluginLoad(const LoadInterface* skse) {
|
|||||||
GetPapyrusInterface()->Register(Papyrus::Bind);
|
GetPapyrusInterface()->Register(Papyrus::Bind);
|
||||||
|
|
||||||
DialogueMenuPatch::Install();
|
DialogueMenuPatch::Install();
|
||||||
|
HeroMenuPatch::Install();
|
||||||
TweenMenuPatch::Install();
|
TweenMenuPatch::Install();
|
||||||
|
|
||||||
if (g_settings.at("VideoInterruptPatch")) {
|
if (g_settings.at("VideoInterruptPatch")) {
|
||||||
|
@ -98,6 +98,11 @@ namespace Papyrus::PapyrusFunctions
|
|||||||
DialogueMenuPatch::BlockTab(false);
|
DialogueMenuPatch::BlockTab(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float ComputeNeededExp(RE::StaticFunctionTag*, std::uint32_t CurrentLevel, float Slope, float Mult, float fExpAcc = 1.0f, float fExpAcc_Level20 = 1.2f, float fExpAcc_Level30 = 1.5f, float fExpAcc_Level40 = 2.0f)
|
||||||
|
{
|
||||||
|
return ComputeNeededExpPoints(CurrentLevel, Slope, Mult, fExpAcc, fExpAcc_Level20, fExpAcc_Level30, fExpAcc_Level40);
|
||||||
|
}
|
||||||
|
|
||||||
inline void Bind(VM& a_vm)
|
inline void Bind(VM& a_vm)
|
||||||
{
|
{
|
||||||
BIND(CreatePotion);
|
BIND(CreatePotion);
|
||||||
@ -114,7 +119,7 @@ namespace Papyrus::PapyrusFunctions
|
|||||||
logger::info("{}", "Registered StringToHex"sv);
|
logger::info("{}", "Registered StringToHex"sv);
|
||||||
BIND(DisableDialogueQuitting);
|
BIND(DisableDialogueQuitting);
|
||||||
logger::info("{}", "Registered DisableDialogueQuitting"sv);
|
logger::info("{}", "Registered DisableDialogueQuitting"sv);
|
||||||
BIND(EnableDialogueQuitting);
|
BIND(ComputeNeededExp);
|
||||||
logger::info("{}", "Registered EnableDialogueQuitting"sv);
|
logger::info("{}", "Registered ComputeNeededExp"sv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
153
source/Enderal DLL/src/Patches/HeroMenuPatch.cpp
Normal file
153
source/Enderal DLL/src/Patches/HeroMenuPatch.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#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
|
||||||
|
REL::Relocation<std::uintptr_t> vTable(RE::VTABLE_MenuControls[0]);
|
||||||
|
_ProcessEvent = vTable.write_vfunc(0x1, &HeroMenuPatch::ProcessEvent_Hook);
|
||||||
|
|
||||||
|
// Patch the Quick Stats hotkey
|
||||||
|
REL::Relocation<std::uintptr_t> target{ REL::RelocationID(51400, 52249), REL::Relocate(0x41E, 0x421) };
|
||||||
|
REL::safe_fill(target.address(), REL::NOP, 45);
|
||||||
|
|
||||||
|
auto& trampoline = SKSE::GetTrampoline();
|
||||||
|
SKSE::AllocTrampoline(14);
|
||||||
|
_OpenStats = trampoline.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)
|
||||||
|
{
|
||||||
|
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
|
||||||
|
uiMovie->SetVariable("_root.heromenu_mc.stats.playerclass.stat_value.text", a_result.GetString());
|
||||||
|
}
|
||||||
|
bool CanSave() { return false; }
|
||||||
|
void SetObject(const RE::BSTSmartPointer<RE::BSScript::Object>&) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto uiMovie = RE::UI::GetSingleton()->GetMovieView("CustomMenu");
|
||||||
|
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("_root.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("_root.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("_root.heromenu_mc.SetModifier", nullptr, args3, 21);
|
||||||
|
}
|
22
source/Enderal DLL/src/Patches/HeroMenuPatch.h
Normal file
22
source/Enderal DLL/src/Patches/HeroMenuPatch.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
|
class HeroMenuPatch : public RE::MenuControls
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Install();
|
||||||
|
|
||||||
|
static uint64_t OpenStats(uint32_t arg_1, uint32_t arg_2, uint32_t arg_3, uint32_t arg_4, uint32_t arg_5);
|
||||||
|
|
||||||
|
static void FillMenuValues();
|
||||||
|
|
||||||
|
static void OpenHeroMenu();
|
||||||
|
|
||||||
|
inline static REL::Relocation<decltype(OpenStats)> _OpenStats;
|
||||||
|
|
||||||
|
RE::BSEventNotifyControl ProcessEvent_Hook(RE::InputEvent** a_event, RE::BSTEventSource<RE::InputEvent*>* a_source);
|
||||||
|
|
||||||
|
using ProcessEvent_t = decltype(static_cast<RE::BSEventNotifyControl (RE::MenuControls::*)(RE::InputEvent* const*, RE::BSTEventSource<RE::InputEvent*>*)>(&RE::MenuControls::ProcessEvent));
|
||||||
|
static inline REL::Relocation<ProcessEvent_t> _ProcessEvent;
|
||||||
|
};
|
8
source/Enderal DLL/src/Patches/TweenMenuPatch.cpp
Normal file
8
source/Enderal DLL/src/Patches/TweenMenuPatch.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "TweenMenuPatch.h"
|
||||||
|
|
||||||
|
void TweenMenuPatch::Install()
|
||||||
|
{
|
||||||
|
// Patch the Tween Menu
|
||||||
|
REL::Relocation<uintptr_t> vtbl(RE::VTABLE_TweenMenu[0]);
|
||||||
|
_AcceptFn = vtbl.write_vfunc(0x1, &AcceptEx);
|
||||||
|
}
|
@ -1,34 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "HeroMenuPatch.h"
|
||||||
|
|
||||||
class TweenMenuPatch final : public RE::TweenMenu
|
class TweenMenuPatch final : public RE::TweenMenu
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void Install()
|
static void Install();
|
||||||
{
|
|
||||||
REL::Relocation<uintptr_t> vtbl(RE::VTABLE_TweenMenu[0]);
|
|
||||||
_AcceptFn = vtbl.write_vfunc(0x1, &AcceptEx);
|
|
||||||
|
|
||||||
REL::Relocation<std::uintptr_t> target{ REL::RelocationID(51400, 52249), REL::Relocate(0x41E, 0x421) };
|
|
||||||
REL::safe_fill(target.address(), REL::NOP, 45);
|
|
||||||
|
|
||||||
auto& trampoline = SKSE::GetTrampoline();
|
|
||||||
SKSE::AllocTrampoline(14);
|
|
||||||
_OpenStats = trampoline.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));
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t OpenStats(uint32_t arg_1, uint32_t arg_2, uint32_t arg_3, uint32_t arg_4, uint32_t arg_5)
|
|
||||||
{
|
|
||||||
SKSE::ModCallbackEvent modEvent{ "Enderal_OpenHeroMenu", "", 0.0f, nullptr };
|
|
||||||
SKSE::GetModCallbackEventSource()->SendEvent(&modEvent);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static void OpenMenu(RE::IMenu* tweenMenu, std::int32_t index)
|
static void OpenMenu(RE::IMenu* tweenMenu, std::int32_t index)
|
||||||
{
|
{
|
||||||
using func_t = decltype(&OpenMenu);
|
using func_t = decltype(&OpenMenu);
|
||||||
@ -44,8 +23,7 @@ private:
|
|||||||
a_cbReg->Process("OpenHighlightedMenu", [](const RE::FxDelegateArgs& args) {
|
a_cbReg->Process("OpenHighlightedMenu", [](const RE::FxDelegateArgs& args) {
|
||||||
auto index = args[0].GetSInt();
|
auto index = args[0].GetSInt();
|
||||||
if (index <= 1) {
|
if (index <= 1) {
|
||||||
SKSE::ModCallbackEvent modEvent{ "Enderal_OpenHeroMenu", "", 0.0f, nullptr };
|
HeroMenuPatch::OpenHeroMenu();
|
||||||
SKSE::GetModCallbackEventSource()->SendEvent(&modEvent);
|
|
||||||
} else if (index <= 4) {
|
} else if (index <= 4) {
|
||||||
OpenMenu(RE::UI::GetSingleton()->GetMenu(MENU_NAME).get(), index);
|
OpenMenu(RE::UI::GetSingleton()->GetMenu(MENU_NAME).get(), index);
|
||||||
}
|
}
|
||||||
@ -53,5 +31,4 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline static REL::Relocation<decltype(&Accept)> _AcceptFn;
|
inline static REL::Relocation<decltype(&Accept)> _AcceptFn;
|
||||||
inline static REL::Relocation<decltype(OpenStats)> _OpenStats;
|
|
||||||
};
|
};
|
||||||
|
@ -178,3 +178,29 @@ inline void CloseTweenMenu()
|
|||||||
return func();
|
return func();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline float ComputeNeededExpPoints(std::uint32_t CurrentLevel, float Slope, float Mult, float fExpAcc = 1.0f, float fExpAcc_Level20 = 1.2f, float fExpAcc_Level30 = 1.5f, float fExpAcc_Level40 = 2.0f)
|
||||||
|
{
|
||||||
|
Mult *= fExpAcc;
|
||||||
|
|
||||||
|
if (CurrentLevel <= 20) {
|
||||||
|
return pow(CurrentLevel, Slope) * Mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
float result = pow(20, Slope) * Mult;
|
||||||
|
|
||||||
|
if (CurrentLevel <= 30) {
|
||||||
|
return result + (pow(CurrentLevel, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurrentLevel <= 40) {
|
||||||
|
result += (pow(30, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20;
|
||||||
|
result += (pow(CurrentLevel, Slope) - pow(30, Slope)) * Mult * fExpAcc_Level30;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += (pow(30, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20;
|
||||||
|
result += (pow(40, Slope) - pow(30, Slope)) * Mult * fExpAcc_Level30;
|
||||||
|
result += (pow(CurrentLevel, Slope) - pow(40, Slope)) * Mult * fExpAcc_Level40;
|
||||||
|
return result;
|
||||||
|
}
|
@ -466,6 +466,12 @@ String Function GetPlayerClassName()
|
|||||||
EndIf
|
EndIf
|
||||||
EndFunction
|
EndFunction
|
||||||
|
|
||||||
|
String Function GetPlayerClassNameGlobal() Global
|
||||||
|
{Called by EnderalSE.dll}
|
||||||
|
Quest AffinityQuest = Game.GetFormFromFile(0x1597B, "Enderal - Forgotten Stories.esm") as Quest;
|
||||||
|
return (AffinityQuest.GetAlias(0) as _00E_AffinityControl).GetPlayerClassName()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
;=====================================================================================
|
;=====================================================================================
|
||||||
; PROPERTIES
|
; PROPERTIES
|
||||||
|
54
source/scripts/_00E_Func_ComputeNeededExp.psc
Normal file
54
source/scripts/_00E_Func_ComputeNeededExp.psc
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
Scriptname _00E_Func_ComputeNeededExp Hidden
|
||||||
|
|
||||||
|
;import Math
|
||||||
|
|
||||||
|
float Function Run(int CurrentLevel, float Slope, float Mult, float fExpAcc = 1.0, float fExpAcc_Level20 = 1.2, float fExpAcc_Level30 = 1.5, float fExpAcc_Level40 = 2.0) Global
|
||||||
|
{Computes the total Experience (EP) needed by the player to reach the level CurrentLevel + 1. This includes the experience needed for the previous level.}
|
||||||
|
|
||||||
|
return EnderalFunctions.ComputeNeededExp(CurrentLevel, Slope, Mult, fExpAcc, fExpAcc_Level20, fExpAcc_Level30, fExpAcc_Level40);
|
||||||
|
|
||||||
|
; This can be used as a quick way to scale the leveling process
|
||||||
|
; for all levels:
|
||||||
|
;/
|
||||||
|
float fExpAcc = 1.0
|
||||||
|
float fExpAcc_Level20 = 1.2
|
||||||
|
float fExpAcc_Level30 = 1.5
|
||||||
|
float fExpAcc_Level40 = 2.0
|
||||||
|
|
||||||
|
Mult *= fExpAcc
|
||||||
|
|
||||||
|
If CurrentLevel <= 20
|
||||||
|
; no changes to the old formula until level 20.
|
||||||
|
return pow(CurrentLevel, Slope) * Mult
|
||||||
|
Else
|
||||||
|
; no changes to the old formula for the first 20 levels.
|
||||||
|
float result = pow(20, Slope) * Mult
|
||||||
|
; Progressive taxation:
|
||||||
|
if CurrentLevel <= 30
|
||||||
|
result += (pow(CurrentLevel, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20
|
||||||
|
return result
|
||||||
|
elseif CurrentLevel <= 40
|
||||||
|
result += (pow(30, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20
|
||||||
|
result += (pow(CurrentLevel, Slope) - pow(30, Slope)) * Mult * fExpAcc_Level30
|
||||||
|
return result
|
||||||
|
else
|
||||||
|
result += (pow(30, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20
|
||||||
|
result += (pow(40, Slope) - pow(30, Slope)) * Mult * fExpAcc_Level30
|
||||||
|
result += (pow(CurrentLevel, Slope) - pow(40, Slope)) * Mult * fExpAcc_Level40
|
||||||
|
return result
|
||||||
|
endif
|
||||||
|
EndIf
|
||||||
|
/;
|
||||||
|
EndFunction
|
||||||
|
|
||||||
|
; A debugging function:
|
||||||
|
;function DumpLevelCurve()
|
||||||
|
;{Dump a table of required EP for each level from 0 to 100 to the Papyrus log}
|
||||||
|
; string aua = ""
|
||||||
|
; int iIndex = 0
|
||||||
|
; while iIndex < 100
|
||||||
|
; aua += "To get to level " + (iIndex + 1) + ", you need " + ComputeNeededExp(iIndex, EXPMultSlope.GetValue(), EXPMult.GetValue()) + "EP. \n "
|
||||||
|
; iIndex += 1
|
||||||
|
; endwhile
|
||||||
|
; Debug.Trace(aua)
|
||||||
|
;Endfunction
|
@ -284,7 +284,7 @@ bool function receiveEP(int amount)
|
|||||||
iLevelUpsNeeded = iLevelUpCount
|
iLevelUpsNeeded = iLevelUpCount
|
||||||
_UnlockLevelUpSystem()
|
_UnlockLevelUpSystem()
|
||||||
|
|
||||||
PlayerNeededExp.SetValue(ComputeNeededExp((PlayerLevel.GetValueInt()+iLevelUpCount), EXPMultSlope.GetValue(), EXPMult.GetValue()))
|
PlayerNeededExp.SetValue(_00E_Func_ComputeNeededExp.Run((PlayerLevel.GetValueInt()+iLevelUpCount), EXPMultSlope.GetValue(), EXPMult.GetValue()))
|
||||||
|
|
||||||
int iNeededExpNextLevel = (PlayerNeededExp.GetValueInt()-PlayerExp.GetValueInt())
|
int iNeededExpNextLevel = (PlayerNeededExp.GetValueInt()-PlayerExp.GetValueInt())
|
||||||
_00E_sEPNeeded.Show(amount, iNeededExpNextLevel)
|
_00E_sEPNeeded.Show(amount, iNeededExpNextLevel)
|
||||||
@ -303,7 +303,7 @@ int function getLevelUpCount()
|
|||||||
|
|
||||||
While LevelCheckRequired
|
While LevelCheckRequired
|
||||||
LevelCheckRequired = false
|
LevelCheckRequired = false
|
||||||
NeededExp = ComputeNeededExp(iCurrentLevel, EXPMultSlope.GetValue(), EXPMult.GetValue())
|
NeededExp = _00E_Func_ComputeNeededExp.Run(iCurrentLevel, EXPMultSlope.GetValue(), EXPMult.GetValue())
|
||||||
|
|
||||||
if iCurrentExp >= NeededExp
|
if iCurrentExp >= NeededExp
|
||||||
LevelCheckRequired = true
|
LevelCheckRequired = true
|
||||||
@ -317,60 +317,6 @@ int function getLevelUpCount()
|
|||||||
|
|
||||||
EndFunction
|
EndFunction
|
||||||
|
|
||||||
float Function ComputeNeededExp(int CurrentLevel, float Slope, float Mult)
|
|
||||||
{Computes the total Experience (EP) needed by the player to reach the level CurrentLevel + 1. This includes the experience needed for the previous level.}
|
|
||||||
|
|
||||||
; commented out as it is useless upon the Steam releases
|
|
||||||
; GlobalVariable _00E_FS_IsForgottenStoriesActivated = Game.GetForm(0x0004320E) as GlobalVariable
|
|
||||||
; If _00E_FS_IsForgottenStoriesActivated.GetValueInt() == 0
|
|
||||||
; Pre-DLC formula:
|
|
||||||
; return pow(CurrentLevel, Slope) * Mult
|
|
||||||
; EndIf
|
|
||||||
|
|
||||||
; This can be used as a quick way to scale the leveling process
|
|
||||||
; for all levels:
|
|
||||||
float fExpAcc = 1.0
|
|
||||||
float fExpAcc_Level20 = 1.2
|
|
||||||
float fExpAcc_Level30 = 1.5
|
|
||||||
float fExpAcc_Level40 = 2.0
|
|
||||||
|
|
||||||
Mult *= fExpAcc
|
|
||||||
|
|
||||||
If CurrentLevel <= 20
|
|
||||||
; no changes to the old formula until level 20.
|
|
||||||
return pow(CurrentLevel, Slope) * Mult
|
|
||||||
Else
|
|
||||||
; no changes to the old formula for the first 20 levels.
|
|
||||||
float result = pow(20, Slope) * Mult
|
|
||||||
; Progressive taxation:
|
|
||||||
if CurrentLevel <= 30
|
|
||||||
result += (pow(CurrentLevel, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20
|
|
||||||
return result
|
|
||||||
elseif CurrentLevel <= 40
|
|
||||||
result += (pow(30, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20
|
|
||||||
result += (pow(CurrentLevel, Slope) - pow(30, Slope)) * Mult * fExpAcc_Level30
|
|
||||||
return result
|
|
||||||
else
|
|
||||||
result += (pow(30, Slope) - pow(20, Slope)) * Mult * fExpAcc_Level20
|
|
||||||
result += (pow(40, Slope) - pow(30, Slope)) * Mult * fExpAcc_Level30
|
|
||||||
result += (pow(CurrentLevel, Slope) - pow(40, Slope)) * Mult * fExpAcc_Level40
|
|
||||||
return result
|
|
||||||
endif
|
|
||||||
EndIf
|
|
||||||
|
|
||||||
EndFunction
|
|
||||||
|
|
||||||
; A debugging function:
|
|
||||||
;function DumpLevelCurve()
|
|
||||||
;{Dump a table of required EP for each level from 0 to 100 to the Papyrus log}
|
|
||||||
; string aua = ""
|
|
||||||
; int iIndex = 0
|
|
||||||
; while iIndex < 100
|
|
||||||
; aua += "To get to level " + (iIndex + 1) + ", you need " + ComputeNeededExp(iIndex, EXPMultSlope.GetValue(), EXPMult.GetValue()) + "EP. \n "
|
|
||||||
; iIndex += 1
|
|
||||||
; endwhile
|
|
||||||
; Debug.Trace(aua)
|
|
||||||
;Endfunction
|
|
||||||
|
|
||||||
;=====================================================================================
|
;=====================================================================================
|
||||||
; PROPERTIES
|
; PROPERTIES
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
Scriptname _00E_HeroMenuAlias extends ReferenceAlias Hidden
|
Scriptname _00E_HeroMenuAlias extends ReferenceAlias Hidden
|
||||||
|
{This script is unused, hero menu handling has been moved to DLL in 2.1+.}
|
||||||
|
|
||||||
int function _GetScriptVersion() Global
|
int function _GetScriptVersion() Global
|
||||||
return 1
|
return 1
|
||||||
@ -99,8 +100,8 @@ Float[] Function GetFloats()
|
|||||||
int iPlayerLevel = PlayerLevel.GetValueInt()
|
int iPlayerLevel = PlayerLevel.GetValueInt()
|
||||||
float fEXPMultSlope = EXPMultSlope.GetValue()
|
float fEXPMultSlope = EXPMultSlope.GetValue()
|
||||||
float fEXPMult = EXPMult.GetValue()
|
float fEXPMult = EXPMult.GetValue()
|
||||||
float fEXPNeededForCurrentLevel = (PlayerREF as _00E_EPUpdateFunctions).ComputeNeededExp(iPlayerLevel - 1, fEXPMultSlope, fEXPMult)
|
float fEXPNeededForCurrentLevel = _00E_Func_ComputeNeededExp.Run(iPlayerLevel - 1, fEXPMultSlope, fEXPMult)
|
||||||
float fEXPNeededForNextLevel = (PlayerREF as _00E_EPUpdateFunctions).ComputeNeededExp(iPlayerLevel, fEXPMultSlope, fEXPMult)
|
float fEXPNeededForNextLevel = _00E_Func_ComputeNeededExp.Run(iPlayerLevel, fEXPMultSlope, fEXPMult)
|
||||||
int iPlayerExp = PlayerExp.GetValueInt()
|
int iPlayerExp = PlayerExp.GetValueInt()
|
||||||
|
|
||||||
SkillmenuFloats[0] = (AiHealth.GetBaseValue(PlayerREF)) as Int
|
SkillmenuFloats[0] = (AiHealth.GetBaseValue(PlayerREF)) as Int
|
||||||
|
@ -19,6 +19,8 @@ String Function GetPlayerHash() Global Native
|
|||||||
; RETURN - Returns the hexadecimal equivalent of the passed string.
|
; RETURN - Returns the hexadecimal equivalent of the passed string.
|
||||||
String Function StringToHex(String a_string) Global Native
|
String Function StringToHex(String a_string) Global Native
|
||||||
|
|
||||||
|
float function ComputeNeededExp(int CurrentLevel, float Slope, float Mult, float fExpAcc = 1.0, float fExpAcc_Level20 = 1.2, float fExpAcc_Level30 = 1.5, float fExpAcc_Level40 = 2.0) global native
|
||||||
|
|
||||||
; Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye.
|
; Disables the TAB Key during dialogue. Resets automatically upon dialogue exit via Goodbye.
|
||||||
Function DisableDialogueQuitting() Global Native
|
Function DisableDialogueQuitting() Global Native
|
||||||
Function EnableDialogueQuitting() Global Native
|
Function EnableDialogueQuitting() Global Native
|
||||||
|
Loading…
Reference in New Issue
Block a user