Skills, auto-attack, aura and chain lightning rework, Esc, cache fix
- 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>
This commit is contained in:
1 parent
f721d2a486
commit
1fd89524d3
41 files changed
+2347
-214
No files matched your search
+49
-77
@@ -1,24 +1,24 @@
|
||||
// Skill keys cast the skill, not only select it.
|
||||
// Skill keys cast their combat-art slot.
|
||||
//
|
||||
// The taskbar's combat-art slots are selected with a key (cUI_Taskbar2::
|
||||
// HandleEvent -> SelectSkillSlot 0x6e17d0); the selected art is then used with
|
||||
// the right mouse button. When a skill key (src/mods/input.cpp) selects a slot,
|
||||
// this mod also presses the right mouse button: a WM_RBUTTONDOWN is posted 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).
|
||||
// The button is released with the key, so holding the key is holding the
|
||||
// button. Slots selected by clicking them with the mouse are not cast.
|
||||
// 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 "mods/skillbar.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "re/hook.h"
|
||||
#include "game/engine/events.h"
|
||||
#include "mods/skillbar.h"
|
||||
|
||||
namespace mods::skillkeys {
|
||||
namespace {
|
||||
@@ -40,77 +40,49 @@ LPARAM CursorClientPos(HWND window) {
|
||||
return MAKELPARAM(cursor.x, cursor.y);
|
||||
}
|
||||
|
||||
// The key that is to cast slot `slot` when it gets selected, then the key
|
||||
// holding the right mouse button down. Set on the window thread, used on the
|
||||
// thread that handles UI events.
|
||||
struct State {
|
||||
std::mutex lock;
|
||||
int pendingKey = 0;
|
||||
unsigned pendingSlot = 0;
|
||||
bool keyDown = false;
|
||||
int heldKey = 0; // right button is down for this key
|
||||
HWND window = nullptr;
|
||||
};
|
||||
State g_state;
|
||||
// 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 CastOnSelect(unsigned slot, int vk) {
|
||||
std::lock_guard guard(g_state.lock);
|
||||
g_state.pendingKey = vk;
|
||||
g_state.pendingSlot = slot;
|
||||
g_state.keyDown = true;
|
||||
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) {
|
||||
std::lock_guard guard(g_state.lock);
|
||||
if (g_state.pendingKey == vk) {
|
||||
g_state.keyDown = false; // released before the slot was selected
|
||||
}
|
||||
if (g_state.heldKey == vk) {
|
||||
g_state.heldKey = 0;
|
||||
PostMessageW(g_state.window, WM_RBUTTONUP, 0, CursorClientPos(g_state.window));
|
||||
}
|
||||
}
|
||||
|
||||
// After a slot was selected: cast if a skill key asked for it.
|
||||
void OnSlotSelected(void* taskbar, unsigned slot) {
|
||||
std::lock_guard guard(g_state.lock);
|
||||
if (!g_state.pendingKey || g_state.pendingSlot != slot) {
|
||||
if (g_heldKey.load() != vk) {
|
||||
return;
|
||||
}
|
||||
const int key = g_state.pendingKey;
|
||||
g_state.pendingKey = 0;
|
||||
if (!mods::skillbar::HasSkillSlot(taskbar, slot)) {
|
||||
return; // empty slot: nothing was selected
|
||||
}
|
||||
HWND window = GameWindow();
|
||||
if (!window || g_state.heldKey) {
|
||||
return;
|
||||
}
|
||||
PostMessageW(window, WM_RBUTTONDOWN, MK_RBUTTON, CursorClientPos(window));
|
||||
if (g_state.keyDown) {
|
||||
g_state.heldKey = key;
|
||||
g_state.window = window;
|
||||
} else {
|
||||
PostMessageW(window, WM_RBUTTONUP, 0, CursorClientPos(window));
|
||||
}
|
||||
PostMessageW(g_window, WM_RBUTTONUP, 0, CursorClientPos(g_window));
|
||||
g_heldKey = 0;
|
||||
}
|
||||
|
||||
} // namespace mods::skillkeys
|
||||
|
||||
namespace game {
|
||||
|
||||
// cUI_Taskbar2::SelectSkillSlot (0x6e17d0): makes combat-art slot `slot` the
|
||||
// right mouse button's art (keys, clicks on the slot).
|
||||
void __fastcall UiTaskbar_SelectSkillSlot(void* self, void* edx, unsigned slot, int notify);
|
||||
RE_HOOK(0x006e17d0, UiTaskbar_SelectSkillSlot);
|
||||
void __fastcall UiTaskbar_SelectSkillSlot(void* self, void* edx, unsigned slot, int notify) {
|
||||
UiTaskbar_SelectSkillSlot_original(self, edx, slot, notify);
|
||||
if (self) {
|
||||
mods::skillkeys::OnSlotSelected(self, slot & 0xffff);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
||||
Reference in new issue
Block a user