- Skill bar: no art selection; slot 0 is the right-click art, keys 0-6 cast slots directly; cooldown labels in exact seconds; slot 0 set apart. - Arts regenerate on their own (casting no longer refills the group). - Auto-attack after targeting or casting at a hostile creature, at the speed of a held button. - Auras can be recast while active (renew, no second visual). - Blicke wie Klingen hits every enemy in range, physical, with life leech. - Blitzschlag steers the game's own chain: 3 + level/5 hits, 1 + level/10 chains, re-hits a lone enemy. - Weapon sets: click selects, drag only from the selected set; the leftmost set is clickable (signed hit tests). - Esc closes open windows before opening the game menu. - HUD: standalone buttons and texts are centred. - Record cache sized for widened views (town lag while walking). - Shared game code: orders, fx events, text drawing, cooldowns, effects. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
89 lines
2.5 KiB
C++
89 lines
2.5 KiB
C++
// Skill keys cast their combat-art slot.
|
|
//
|
|
// The game casts one art with the right mouse button: the one in the
|
|
// "selected" art slot, which is always slot 0 now (src/mods/skillbar.cpp).
|
|
// Slot 0's key presses the right mouse button; the keys of slots 1-6 first
|
|
// make their slot the selected one. The button is pressed by posting
|
|
// WM_RBUTTONDOWN to the game window, so the game handles it exactly like a
|
|
// real right-click at the cursor (UI hit tests, target under the cursor,
|
|
// cEvent_creature 0x105), and released with the key: holding the key is
|
|
// holding the button. While no skill key is held, slot 0 is selected again
|
|
// (checked every engine tick, which also covers saves loaded with another
|
|
// selection).
|
|
|
|
#include "mods/skillkeys.h"
|
|
|
|
#include <atomic>
|
|
|
|
#include <windows.h>
|
|
|
|
#include "game/engine/events.h"
|
|
#include "mods/skillbar.h"
|
|
|
|
namespace mods::skillkeys {
|
|
namespace {
|
|
|
|
// The game window, if it has the focus (it does while a skill key is used).
|
|
HWND GameWindow() {
|
|
HWND window = GetForegroundWindow();
|
|
DWORD process = 0;
|
|
if (!window || !GetWindowThreadProcessId(window, &process) || process != GetCurrentProcessId()) {
|
|
return nullptr;
|
|
}
|
|
return window;
|
|
}
|
|
|
|
LPARAM CursorClientPos(HWND window) {
|
|
POINT cursor = {};
|
|
GetCursorPos(&cursor);
|
|
ScreenToClient(window, &cursor);
|
|
return MAKELPARAM(cursor.x, cursor.y);
|
|
}
|
|
|
|
// The key holding the right mouse button down, and the window it was pressed
|
|
// in. Set on the window thread, read by the engine tick.
|
|
std::atomic<int> g_heldKey{0};
|
|
HWND g_window = nullptr;
|
|
|
|
void OnTick(uint8_t*) {
|
|
if (!g_heldKey.load() && mods::skillbar::SelectedSkillSlot() != 0) {
|
|
mods::skillbar::SelectSkillSlot(0);
|
|
}
|
|
}
|
|
|
|
struct Registration {
|
|
Registration() { game::OnEngineTick(&OnTick); }
|
|
} g_registration;
|
|
|
|
} // namespace
|
|
|
|
void KeyPressed(unsigned slot, int vk) {
|
|
if (g_heldKey.load() || !mods::skillbar::SkillSlotHasArt(slot)) {
|
|
return;
|
|
}
|
|
HWND window = GameWindow();
|
|
if (!window) {
|
|
return;
|
|
}
|
|
g_window = window;
|
|
g_heldKey = vk; // before selecting: the tick leaves the selection alone
|
|
if (slot != 0) {
|
|
mods::skillbar::SelectSkillSlot(slot);
|
|
}
|
|
PostMessageW(window, WM_RBUTTONDOWN, MK_RBUTTON, CursorClientPos(window));
|
|
}
|
|
|
|
bool KeyHeld() {
|
|
return g_heldKey.load() != 0;
|
|
}
|
|
|
|
void KeyReleased(int vk) {
|
|
if (g_heldKey.load() != vk) {
|
|
return;
|
|
}
|
|
PostMessageW(g_window, WM_RBUTTONUP, 0, CursorClientPos(g_window));
|
|
g_heldKey = 0;
|
|
}
|
|
|
|
} // namespace mods::skillkeys
|