服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
81 lines
2.7 KiB
C#
81 lines
2.7 KiB
C#
using System.Runtime.InteropServices;
|
|
|
|
namespace DNFLoadout;
|
|
|
|
internal sealed class HotkeyManager : IDisposable
|
|
{
|
|
private const int AltBit = 0x1;
|
|
private const int CtrlBit = 0x2;
|
|
private const int ShiftBit = 0x4;
|
|
private readonly System.Windows.Forms.Timer _timer = new() { Interval = 50 };
|
|
private readonly HashSet<int> _down = new();
|
|
private Config _config;
|
|
|
|
public event Action<Scheme>? Triggered;
|
|
|
|
public HotkeyManager(Config config)
|
|
{
|
|
_config = config;
|
|
_timer.Tick += (_, _) => Poll();
|
|
_timer.Start();
|
|
}
|
|
|
|
public void UpdateConfig(Config config) => _config = config;
|
|
|
|
private static bool Down(int vk) => (GetAsyncKeyState(vk) & 0x8000) != 0;
|
|
private static bool ModCtrl => Down((int)Keys.ControlKey);
|
|
private static bool ModAlt => Down((int)Keys.Menu);
|
|
private static bool ModShift => Down((int)Keys.ShiftKey);
|
|
|
|
private void Poll()
|
|
{
|
|
foreach (var scheme in _config.CurrentPage.Schemes)
|
|
{
|
|
if (scheme.KeyCode == 0) continue;
|
|
bool mods = ((scheme.Modifiers & CtrlBit) == 0 || ModCtrl)
|
|
&& ((scheme.Modifiers & AltBit) == 0 || ModAlt)
|
|
&& ((scheme.Modifiers & ShiftBit) == 0 || ModShift);
|
|
// Reject extra modifiers so Ctrl+F1 cannot fire plain F1.
|
|
if ((scheme.Modifiers & CtrlBit) == 0 && ModCtrl) mods = false;
|
|
if ((scheme.Modifiers & AltBit) == 0 && ModAlt) mods = false;
|
|
if ((scheme.Modifiers & ShiftBit) == 0 && ModShift) mods = false;
|
|
bool key = Down(scheme.KeyCode);
|
|
int id = scheme.KeyCode | (scheme.Modifiers << 16);
|
|
bool was = _down.Contains(id);
|
|
if (key && mods && !was)
|
|
{
|
|
_down.Add(id);
|
|
Triggered?.Invoke(scheme);
|
|
}
|
|
else if (!key || !mods)
|
|
{
|
|
_down.Remove(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static (int Modifiers, int KeyCode) Capture(Keys keyData)
|
|
{
|
|
var modifiers = 0;
|
|
if ((keyData & Keys.Control) != 0) modifiers |= CtrlBit;
|
|
if ((keyData & Keys.Alt) != 0) modifiers |= AltBit;
|
|
if ((keyData & Keys.Shift) != 0) modifiers |= ShiftBit;
|
|
var keyCode = (int)(keyData & Keys.KeyCode);
|
|
if (keyCode is (int)Keys.ControlKey or (int)Keys.Menu or (int)Keys.ShiftKey
|
|
or (int)Keys.LControlKey or (int)Keys.RControlKey
|
|
or (int)Keys.LMenu or (int)Keys.RMenu
|
|
or (int)Keys.LShiftKey or (int)Keys.RShiftKey)
|
|
return (modifiers, 0);
|
|
return (modifiers, keyCode);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_timer.Stop();
|
|
_timer.Dispose();
|
|
}
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern short GetAsyncKeyState(int vKey);
|
|
}
|