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:
khallaandClaude Opus 5.5 committed 2026-10-15 00:38:34 +02:00
1 parent 998e315d98
commit dbc632750b
12 files changed
+364 -145

No files matched your search

+59 -97
View File
@@ -79,168 +79,130 @@ unsigned Slot(uint32_t value) {
return value & 0xffff;
}
// Runs a slot method: slots 0-4 on the inventory, 5-6 on its shadow (with
// slot - 5; created on demand if `create`), others not at all (result 0).
template <class Original, class... Args>
auto ForSlot(Original original, void* self, uint32_t slot, bool create, Args... args) {
if (Slot(slot) < kGameSlots) {
return original(self, nullptr, slot, args...);
}
uint8_t* shadow = Slot(slot) < kSlots ? ShadowFor(self, create) : nullptr;
if (!shadow) {
return decltype(original(self, nullptr, slot, args...))();
}
return original(shadow, nullptr, Slot(slot) - kGameSlots, args...);
}
// Runs a method on the inventory and, if it has one, on its shadow.
template <class Original, class... Args>
void WithShadow(Original original, void* self, Args... args) {
original(self, nullptr, args...);
if (uint8_t* shadow = ShadowFor(self, false)) {
original(shadow, nullptr, args...);
}
}
} // namespace
// --- weapon sets ---------------------------------------------------------------
// --- weapon sets: (set, hand, ...) ---------------------------------------------
uint32_t __fastcall Inv_GetWeaponSetItem(void* self, void* edx, uint32_t set, uint32_t hand);
RE_HOOK(0x005d86c0, Inv_GetWeaponSetItem);
uint32_t __fastcall Inv_GetWeaponSetItem(void* self, void* edx, uint32_t set, uint32_t hand) {
if (Slot(set) < kGameSlots) {
return Inv_GetWeaponSetItem_original(self, edx, set, hand);
}
uint8_t* shadow = Slot(set) < kSlots ? ShadowFor(self, false) : nullptr;
return shadow ? Inv_GetWeaponSetItem_original(shadow, edx, Slot(set) - kGameSlots, hand) : 0;
uint32_t __fastcall Inv_GetWeaponSetItem(void* self, void*, uint32_t set, uint32_t hand) {
return ForSlot(Inv_GetWeaponSetItem_original, self, set, false, hand);
}
void __fastcall Inv_SetWeaponSetItem(void* self, void* edx, uint32_t set, uint32_t hand, int id, int notify);
RE_HOOK(0x005d86f0, Inv_SetWeaponSetItem);
void __fastcall Inv_SetWeaponSetItem(void* self, void* edx, uint32_t set, uint32_t hand, int id, int notify) {
if (Slot(set) < kGameSlots) {
return Inv_SetWeaponSetItem_original(self, edx, set, hand, id, notify);
}
if (Slot(set) < kSlots) {
if (uint8_t* shadow = ShadowFor(self, true)) {
Inv_SetWeaponSetItem_original(shadow, edx, Slot(set) - kGameSlots, hand, id, notify);
}
}
void __fastcall Inv_SetWeaponSetItem(void* self, void*, uint32_t set, uint32_t hand, int id, int notify) {
ForSlot(Inv_SetWeaponSetItem_original, self, set, true, hand, id, notify);
}
void __fastcall Inv_SetWeaponSet(void* self, void* edx, uint32_t set, int left, int right, int notify);
RE_HOOK(0x005d8930, Inv_SetWeaponSet);
void __fastcall Inv_SetWeaponSet(void* self, void* edx, uint32_t set, int left, int right, int notify) {
if (Slot(set) < kGameSlots) {
return Inv_SetWeaponSet_original(self, edx, set, left, right, notify);
}
if (Slot(set) < kSlots) {
if (uint8_t* shadow = ShadowFor(self, true)) {
Inv_SetWeaponSet_original(shadow, edx, Slot(set) - kGameSlots, left, right, notify);
}
}
void __fastcall Inv_SetWeaponSet(void* self, void*, uint32_t set, int left, int right, int notify) {
ForSlot(Inv_SetWeaponSet_original, self, set, true, left, right, notify);
}
uint32_t __fastcall Inv_TakeWeaponSetItem(void* self, void* edx, uint32_t set, uint32_t hand, void* out, int flag);
RE_HOOK(0x005d8be0, Inv_TakeWeaponSetItem);
uint32_t __fastcall Inv_TakeWeaponSetItem(void* self, void* edx, uint32_t set, uint32_t hand, void* out, int flag) {
if (Slot(set) < kGameSlots) {
return Inv_TakeWeaponSetItem_original(self, edx, set, hand, out, flag);
}
uint8_t* shadow = Slot(set) < kSlots ? ShadowFor(self, false) : nullptr;
return shadow ? Inv_TakeWeaponSetItem_original(shadow, edx, Slot(set) - kGameSlots, hand, out, flag) : 0;
uint32_t __fastcall Inv_TakeWeaponSetItem(void* self, void*, uint32_t set, uint32_t hand, void* out, int flag) {
return ForSlot(Inv_TakeWeaponSetItem_original, self, set, false, hand, out, flag);
}
uint32_t __fastcall Inv_GetWeaponSetItemPos(void* self, void* edx, uint32_t set, uint32_t hand, void* out);
RE_HOOK(0x005d8d00, Inv_GetWeaponSetItemPos);
uint32_t __fastcall Inv_GetWeaponSetItemPos(void* self, void* edx, uint32_t set, uint32_t hand, void* out) {
if (Slot(set) < kGameSlots) {
return Inv_GetWeaponSetItemPos_original(self, edx, set, hand, out);
}
uint8_t* shadow = Slot(set) < kSlots ? ShadowFor(self, false) : nullptr;
return shadow ? Inv_GetWeaponSetItemPos_original(shadow, edx, Slot(set) - kGameSlots, hand, out) : 0;
uint32_t __fastcall Inv_GetWeaponSetItemPos(void* self, void*, uint32_t set, uint32_t hand, void* out) {
return ForSlot(Inv_GetWeaponSetItemPos_original, self, set, false, hand, out);
}
// --- combat-art slots ----------------------------------------------------------
// --- combat-art slots: (slot, ...) ---------------------------------------------
uint32_t __fastcall Inv_GetArtSlotType(void* self, void* edx, uint32_t slot);
RE_HOOK(0x005d93e0, Inv_GetArtSlotType);
uint32_t __fastcall Inv_GetArtSlotType(void* self, void* edx, uint32_t slot) {
if (Slot(slot) < kGameSlots) {
return Inv_GetArtSlotType_original(self, edx, slot);
}
uint8_t* shadow = Slot(slot) < kSlots ? ShadowFor(self, false) : nullptr;
return shadow ? Inv_GetArtSlotType_original(shadow, edx, Slot(slot) - kGameSlots) : 0;
uint32_t __fastcall Inv_GetArtSlotType(void* self, void*, uint32_t slot) {
return ForSlot(Inv_GetArtSlotType_original, self, slot, false);
}
uint32_t __fastcall Inv_GetArtSlotRef(void* self, void* edx, uint32_t slot);
RE_HOOK(0x005d9420, Inv_GetArtSlotRef);
uint32_t __fastcall Inv_GetArtSlotRef(void* self, void* edx, uint32_t slot) {
if (Slot(slot) < kGameSlots) {
return Inv_GetArtSlotRef_original(self, edx, slot);
}
uint8_t* shadow = Slot(slot) < kSlots ? ShadowFor(self, false) : nullptr;
return shadow ? Inv_GetArtSlotRef_original(shadow, edx, Slot(slot) - kGameSlots) : 0;
uint32_t __fastcall Inv_GetArtSlotRef(void* self, void*, uint32_t slot) {
return ForSlot(Inv_GetArtSlotRef_original, self, slot, false);
}
uint32_t __fastcall Inv_GetArtSlotData(void* self, void* edx, uint32_t slot);
RE_HOOK(0x005d9450, Inv_GetArtSlotData);
uint32_t __fastcall Inv_GetArtSlotData(void* self, void* edx, uint32_t slot) {
if (Slot(slot) < kGameSlots) {
return Inv_GetArtSlotData_original(self, edx, slot);
}
uint8_t* shadow = Slot(slot) < kSlots ? ShadowFor(self, false) : nullptr;
return shadow ? Inv_GetArtSlotData_original(shadow, edx, Slot(slot) - kGameSlots) : 0;
uint32_t __fastcall Inv_GetArtSlotData(void* self, void*, uint32_t slot) {
return ForSlot(Inv_GetArtSlotData_original, self, slot, false);
}
void __fastcall Inv_SetArtSlot(void* self, void* edx, uint32_t slot, uint32_t type, uint32_t data, void* refs);
RE_HOOK(0x005d9480, Inv_SetArtSlot);
void __fastcall Inv_SetArtSlot(void* self, void* edx, uint32_t slot, uint32_t type, uint32_t data, void* refs) {
if (Slot(slot) < kGameSlots) {
return Inv_SetArtSlot_original(self, edx, slot, type, data, refs);
}
if (Slot(slot) < kSlots) {
if (uint8_t* shadow = ShadowFor(self, true)) {
Inv_SetArtSlot_original(shadow, edx, Slot(slot) - kGameSlots, type, data, refs);
}
}
void __fastcall Inv_SetArtSlot(void* self, void*, uint32_t slot, uint32_t type, uint32_t data, void* refs) {
ForSlot(Inv_SetArtSlot_original, self, slot, true, type, data, refs);
}
void __fastcall Inv_ClearArtSlot(void* self, void* edx, uint32_t slot);
RE_HOOK(0x005d9680, Inv_ClearArtSlot);
void __fastcall Inv_ClearArtSlot(void* self, void* edx, uint32_t slot) {
if (Slot(slot) < kGameSlots) {
return Inv_ClearArtSlot_original(self, edx, slot);
}
if (uint8_t* shadow = Slot(slot) < kSlots ? ShadowFor(self, false) : nullptr) {
Inv_ClearArtSlot_original(shadow, edx, Slot(slot) - kGameSlots);
}
void __fastcall Inv_ClearArtSlot(void* self, void*, uint32_t slot) {
ForSlot(Inv_ClearArtSlot_original, self, slot, false);
}
// SelectSlot(slot, kind 0 weapon set / 1 art slot, apply, notify): stores the
// selection; for an art slot also tells the engine which art the right mouse
// button uses (read from the slot entry).
// button uses (read from the slot entry, hence run on the shadow for 5-6).
void __fastcall Inv_SelectSlot(void* self, void* edx, uint32_t slot, uint32_t kind, int apply, int notify);
RE_HOOK(0x005d9820, Inv_SelectSlot);
void __fastcall Inv_SelectSlot(void* self, void* edx, uint32_t slot, uint32_t kind, int apply, int notify) {
if (Slot(slot) < kGameSlots) {
return Inv_SelectSlot_original(self, edx, slot, kind, apply, notify);
}
if (Slot(slot) >= kSlots || Slot(kind) > 1) {
void __fastcall Inv_SelectSlot(void* self, void*, uint32_t slot, uint32_t kind, int apply, int notify) {
if (Slot(kind) > 1) {
return;
}
if (uint8_t* shadow = ShadowFor(self, true)) {
Inv_SelectSlot_original(shadow, edx, Slot(slot) - kGameSlots, kind, apply, notify);
ForSlot(Inv_SelectSlot_original, self, slot, true, kind, apply, notify);
if (Slot(slot) >= kGameSlots && Slot(slot) < kSlots) {
auto* selected = reinterpret_cast<uint16_t*>(static_cast<uint8_t*>(self) + mods::skillslots::kSelected);
selected[Slot(kind)] = static_cast<uint16_t>(Slot(slot));
}
auto* selected = reinterpret_cast<uint16_t*>(static_cast<uint8_t*>(self) + mods::skillslots::kSelected);
selected[Slot(kind)] = static_cast<uint16_t>(Slot(slot));
}
// --- methods over all slots ----------------------------------------------------
void __fastcall Inv_Clear(void* self, void* edx, int full, uint32_t arg);
RE_HOOK(0x005d7d30, Inv_Clear);
void __fastcall Inv_Clear(void* self, void* edx, int full, uint32_t arg) {
Inv_Clear_original(self, edx, full, arg);
if (uint8_t* shadow = ShadowFor(self, false)) {
Inv_Clear_original(shadow, edx, full, arg);
}
void __fastcall Inv_Clear(void* self, void*, int full, uint32_t arg) {
WithShadow(Inv_Clear_original, self, full, arg);
}
void __fastcall Inv_ReplaceItemId(void* self, void* edx, int oldId, int newId);
RE_HOOK(0x005d80e0, Inv_ReplaceItemId);
void __fastcall Inv_ReplaceItemId(void* self, void* edx, int oldId, int newId) {
Inv_ReplaceItemId_original(self, edx, oldId, newId);
if (uint8_t* shadow = ShadowFor(self, false)) {
Inv_ReplaceItemId_original(shadow, edx, oldId, newId);
}
void __fastcall Inv_ReplaceItemId(void* self, void*, int oldId, int newId) {
WithShadow(Inv_ReplaceItemId_original, self, oldId, newId);
}
void __fastcall Inv_FixInventory(void* self, void* edx);
RE_HOOK(0x005de8a0, Inv_FixInventory);
void __fastcall Inv_FixInventory(void* self, void* edx) {
Inv_FixInventory_original(self, edx);
if (uint8_t* shadow = ShadowFor(self, false)) {
Inv_FixInventory_original(shadow, edx);
}
void __fastcall Inv_FixInventory(void* self, void*) {
WithShadow(Inv_FixInventory_original, self);
}
} // namespace game