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
+12
-33
@@ -5,9 +5,10 @@
|
||||
// several event receivers (engine 0x618130, UI manager 0x757120, taskbar
|
||||
// 0x6e1970). This mod reads the raw key messages instead (virtual keys,
|
||||
// independent of the keyboard layout): a bound key's messages are swallowed
|
||||
// and its action runs, usually by posting the key event of the original
|
||||
// hotkey to the game's event kernel, the way the window procedure does (the
|
||||
// kernel dispatches it right away, on the window thread).
|
||||
// and its action runs. Slot keys call the taskbar's selection directly
|
||||
// (src/mods/skillbar.cpp); keys standing in for an original hotkey post that
|
||||
// hotkey's key event to the game's event kernel, the way the window
|
||||
// procedure does (the kernel dispatches it right away, on the window thread).
|
||||
//
|
||||
// While a text field has the keyboard, or Ctrl / Alt is held, keys pass
|
||||
// through unchanged.
|
||||
@@ -27,6 +28,8 @@
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include "game/engine/objects.h"
|
||||
#include "mods/skillbar.h"
|
||||
#include "mods/skillkeys.h"
|
||||
#include "mods/wasd.h"
|
||||
#include "re/config.h"
|
||||
@@ -41,15 +44,6 @@ namespace {
|
||||
constexpr uint32_t kUiManager = 0x017ecb3c; // cUI_Manager*: focused control at +0x148
|
||||
constexpr uint32_t kKeyModifiers = 0x0182cdec; // u16, as the in-game window procedure keeps it
|
||||
constexpr uint32_t kEventKeyVtable = 0x00895568;
|
||||
constexpr uint32_t kTypeControl = 0x009de168;
|
||||
constexpr uint32_t kTypeEditControl = 0x009e3608;
|
||||
|
||||
using DynamicCastFn = void*(__cdecl*)(void* object, int vfDelta, uint32_t source, uint32_t target, int isReference);
|
||||
using KernelInstanceFn = void*(__cdecl*)();
|
||||
using PostEventFn = void(__fastcall*)(void* kernel, void* edx, void* event, int a, int b);
|
||||
const auto DynamicCast = reinterpret_cast<DynamicCastFn>(0x00849701);
|
||||
const auto KernelInstance = reinterpret_cast<KernelInstanceFn>(0x00808490);
|
||||
const auto PostEvent = reinterpret_cast<PostEventFn>(0x00808930); // copies the event
|
||||
|
||||
// cEvent_key: +4 1 = pressed, 0 = released; +6 / +8 the key (character when
|
||||
// pressed, virtual key when released); +0xa modifiers.
|
||||
@@ -74,7 +68,7 @@ void PostGameKey(char key, bool pressed) {
|
||||
event.raw = code;
|
||||
event.key = code;
|
||||
event.modifiers = *reinterpret_cast<uint16_t*>(kKeyModifiers);
|
||||
PostEvent(KernelInstance(), nullptr, &event, 1, 0);
|
||||
game::PostEvent(&event);
|
||||
}
|
||||
|
||||
// --- bindings ------------------------------------------------------------------
|
||||
@@ -110,12 +104,6 @@ Binding g_bindings[] = {
|
||||
{"SaveGame", "K", Kind::GameKey, 0, 'S'},
|
||||
};
|
||||
|
||||
// Taskbar digits: weapon sets 1-5 on '1'-'5', combat-art slots 1-5 on
|
||||
// '6'-'9', '0'.
|
||||
constexpr unsigned kGameSlots = 5;
|
||||
char WeaponSetDigit(int set) { return static_cast<char>('1' + set); }
|
||||
char SkillSlotDigit(int slot) { return slot < 4 ? static_cast<char>('6' + slot) : '0'; }
|
||||
|
||||
int ParseKey(const std::string& text) {
|
||||
if (text.size() == 1 && isalnum(static_cast<unsigned char>(text[0]))) {
|
||||
return toupper(static_cast<unsigned char>(text[0])); // VK of A-Z / 0-9
|
||||
@@ -158,17 +146,14 @@ bool Reserved(int vk) {
|
||||
}
|
||||
|
||||
void Press(const Binding& binding, int vk) {
|
||||
const auto slot = static_cast<unsigned>(binding.index);
|
||||
switch (binding.kind) {
|
||||
case Kind::SkillSlot:
|
||||
if (binding.index < static_cast<int>(kGameSlots)) {
|
||||
mods::skillkeys::CastOnSelect(static_cast<unsigned>(binding.index), vk);
|
||||
PostGameKey(SkillSlotDigit(binding.index), true);
|
||||
}
|
||||
mods::skillkeys::CastOnSelect(slot, vk);
|
||||
mods::skillbar::SelectSkillSlot(slot);
|
||||
break;
|
||||
case Kind::WeaponSet:
|
||||
if (binding.index < static_cast<int>(kGameSlots)) {
|
||||
PostGameKey(WeaponSetDigit(binding.index), true);
|
||||
}
|
||||
mods::skillbar::SelectWeaponSet(slot);
|
||||
break;
|
||||
case Kind::GameKey:
|
||||
PostGameKey(binding.gameKey, true);
|
||||
@@ -180,14 +165,8 @@ void Release(const Binding& binding, int vk) {
|
||||
switch (binding.kind) {
|
||||
case Kind::SkillSlot:
|
||||
mods::skillkeys::KeyReleased(vk);
|
||||
if (binding.index < static_cast<int>(kGameSlots)) {
|
||||
PostGameKey(SkillSlotDigit(binding.index), false);
|
||||
}
|
||||
break;
|
||||
case Kind::WeaponSet:
|
||||
if (binding.index < static_cast<int>(kGameSlots)) {
|
||||
PostGameKey(WeaponSetDigit(binding.index), false);
|
||||
}
|
||||
break;
|
||||
case Kind::GameKey:
|
||||
PostGameKey(binding.gameKey, false);
|
||||
@@ -223,7 +202,7 @@ bool TypingIntoTextField() {
|
||||
if (!focus) {
|
||||
return false;
|
||||
}
|
||||
auto* edit = static_cast<uint8_t*>(DynamicCast(focus, 0, kTypeControl, kTypeEditControl, 0));
|
||||
auto* edit = static_cast<uint8_t*>(game::DynamicCast(focus, game::kTypeControl, game::kTypeEditControl));
|
||||
return edit && ((*reinterpret_cast<uint32_t*>(edit + 0x10) >> 16) & 1) == 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user