Taskbar: 7 weapon sets and 7 combat-art slots
cUI_Taskbar2's slot arrays move behind the (enlarged) object with room for 7 each; all instructions addressing them, the relative accesses between the two arrays, the slot loops and the count clamp are patched from tables in game/addresses.h (belt loops untouched). Slots 6 / 7 unlock at levels 45 / 65; AssignArtToSlot accepts slots up to 6. Slot keys call the taskbar's selection directly (honouring COMBINE_SLOTS). Shared code: re::PatchAll / PatchInstructionByte for patch tables, game/engine/objects.h for object, RTTI and kernel calls, slot-hook templates in skillslots.cpp. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
998e315d98
commit
dbc632750b
12 files changed
+364
-145
No files matched your search
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "re/patch.h"
|
||||
|
||||
namespace game::addr {
|
||||
|
||||
// --- functions ---------------------------------------------------------------
|
||||
@@ -167,4 +169,45 @@ constexpr uint32_t kLabelAreaPushes[] = {0x006272f8, 0x0060e940};
|
||||
// stored as immediates.
|
||||
constexpr uint32_t kLetterboxRightImmediates[] = {0x0075895d, 0x007589f0, 0x00758aa5, 0x00758b29};
|
||||
|
||||
// --- taskbar slots (docs/skill-slots.md) -----------------------------------------
|
||||
// cUI_Taskbar2 keeps 5 weapon-set controls at +0x164 and 5 combat-art slot
|
||||
// controls right after them at +0x178. For 7 slots each, both arrays move to
|
||||
// the end of an enlarged object (allocated with `push 0D98h` here), 0x1c apart.
|
||||
constexpr uint32_t kTaskbarAllocSizePush = 0x007593fa;
|
||||
constexpr uint32_t kTaskbarSize = 0xd98;
|
||||
constexpr uint32_t kTaskbarWeaponSlots = 0x164;
|
||||
constexpr uint32_t kTaskbarSkillSlots = 0x178;
|
||||
// Instructions addressing the arrays by displacement.
|
||||
constexpr uint32_t kTaskbarWeaponSlotUsers[] = {
|
||||
0x006df40f, 0x006df9db, 0x006dfd4e, 0x006dffea, 0x006e1289, 0x006e14b8, 0x006e14fe,
|
||||
0x006e1544, 0x006e156a, 0x006e16b1, 0x006e1713, 0x006e1a49, 0x006e1bfb, 0x006e1e7c,
|
||||
0x006e20d5, 0x006e258d, 0x006e280b, 0x006e5dee,
|
||||
};
|
||||
constexpr uint32_t kTaskbarSkillSlotUsers[] = { // incl. `add ebp, 178h`
|
||||
0x006df438, 0x006df7bf, 0x006dfcee, 0x006e004a, 0x006e182a, 0x006e1831, 0x006e187a,
|
||||
0x006e18b6, 0x006e1953, 0x006e19f9, 0x006e1aa0, 0x006e1ecf, 0x006e2081, 0x006e3263,
|
||||
0x006e4496, 0x006e4cac, 0x006e5e27,
|
||||
};
|
||||
// Loops that walk one array and reach the other with a disp8 of -0x14 / +0x14.
|
||||
constexpr re::BytePatch kTaskbarSlotDistanceDown[] = { // [reg-14h]
|
||||
{0x006df7ca, 2, 0xec}, {0x006e44b0, 2, 0xec}, {0x006e44e5, 2, 0xec}, {0x006e4d89, 2, 0xec},
|
||||
};
|
||||
constexpr re::BytePatch kTaskbarSlotDistanceUp[] = { // [reg+14h]
|
||||
{0x006dfa85, 2, 0x14}, {0x006e2598, 2, 0x14},
|
||||
};
|
||||
// Slot loop bounds and the slot count clamp (5). The belt loops (5 x 0xbc at
|
||||
// +0x258 / +0x604) stay.
|
||||
constexpr re::BytePatch kTaskbarSlotLoopBounds[] = { // cmp reg, 5
|
||||
{0x006dfaaa, 2, 5}, {0x006dfd21, 2, 5}, {0x006dfd81, 2, 5}, {0x006e0043, 2, 5},
|
||||
{0x006e0074, 2, 5}, {0x006e1a30, 2, 5}, {0x006e1a86, 2, 5}, {0x006e1eb1, 2, 5},
|
||||
{0x006e1f04, 2, 5}, {0x006e20bc, 2, 5}, {0x006e2110, 2, 5}, {0x006e3199, 2, 5},
|
||||
{0x006e39a6, 2, 5}, {0x006e49ba, 2, 5}, {0x006e4e49, 2, 5},
|
||||
{0x006dffd2, 3, 5}, // SetSlotCount: cmp si, 5
|
||||
{0x006df7c5, 1, 5}, {0x006e2593, 1, 5}, {0x006dffd8, 1, 5}, // mov reg, 5 (count / clamp)
|
||||
};
|
||||
// cCreature::AssignArtToSlot (0x55fac0) rejects slots > 4 (`cmp eax, 4`).
|
||||
constexpr re::BytePatch kAssignArtMaxSlot[] = {{0x0055fae3, 2, 4}};
|
||||
// SlotCountForLevel (stdcall, level): (level, count) table at 0x95503c, max 5.
|
||||
constexpr uint32_t SlotCountForLevel = 0x0057f750;
|
||||
|
||||
} // namespace game::addr
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
// Game objects, RTTI casts and the event kernel: calls shared by the mods.
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace game {
|
||||
|
||||
// MSVC RTTI type descriptors (for DynamicCast).
|
||||
constexpr uint32_t kTypeObject = 0x008e9648;
|
||||
constexpr uint32_t kTypeCreature = 0x008e9660;
|
||||
constexpr uint32_t kTypeControl = 0x009de168;
|
||||
constexpr uint32_t kTypeEditControl = 0x009e3608;
|
||||
|
||||
// __RTDynamicCast (0x849701).
|
||||
inline void* DynamicCast(void* object, uint32_t sourceType, uint32_t targetType) {
|
||||
using Fn = void*(__cdecl*)(void*, int, uint32_t, uint32_t, int);
|
||||
return object ? reinterpret_cast<Fn>(0x00849701)(object, 0, sourceType, targetType, 0) : nullptr;
|
||||
}
|
||||
|
||||
// cObjectManager::GetObject (0x5fe060) on the manager at *0xad3bc0.
|
||||
inline void* GetObjectById(uint32_t id) {
|
||||
using Fn = void*(__fastcall*)(void* manager, void* edx, uint32_t id);
|
||||
void* manager = *reinterpret_cast<void**>(0x00ad3bc0);
|
||||
return manager ? reinterpret_cast<Fn>(0x005fe060)(manager, nullptr, id) : nullptr;
|
||||
}
|
||||
|
||||
// The object `id` as a cCreature, or null.
|
||||
inline void* GetCreature(uint32_t id) {
|
||||
return DynamicCast(GetObjectById(id), kTypeObject, kTypeCreature);
|
||||
}
|
||||
|
||||
// cKernel::PostEvent (0x808930) on Kernel_Instance() (0x808490): the kernel
|
||||
// copies the event and dispatches it (key events right away).
|
||||
inline void PostEvent(void* event) {
|
||||
using InstanceFn = void*(__cdecl*)();
|
||||
using PostFn = void(__fastcall*)(void* kernel, void* edx, void* event, int a, int b);
|
||||
reinterpret_cast<PostFn>(0x00808930)(reinterpret_cast<InstanceFn>(0x00808490)(), nullptr, event, 1, 0);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
Reference in new issue
Block a user