remove-levelsystem
parent
a72dd6611e
commit
ef56d468c5
21 changed files with 302 additions and 94 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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); |
||||
} |
@ -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; |
||||
}; |
@ -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); |
||||
} |
@ -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 |
Loading…
Reference in new issue