Merge branch 'main' into main

This commit is contained in:
辉鸭蛋 2025-01-07 00:12:41 +08:00 committed by GitHub
commit 9f46fb00be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 3105 additions and 216 deletions

View File

@ -101,6 +101,7 @@ public partial class App : Application
services.AddView<OneDragonFlowPage, OneDragonFlowViewModel>();
services.AddSingleton<PathingConfigViewModel>();
// services.AddView<PathingConfigView, PathingConfigViewModel>();
services.AddView<KeyBindingsSettingsPage, KeyBindingsSettingsPageViewModel>();
// 一条龙 ViewModels
// services.AddSingleton<CraftViewModel>();

View File

@ -165,6 +165,11 @@ public partial class AllConfig : ObservableObject
/// </summary>
public NotificationConfig NotificationConfig { get; set; } = new();
/// <summary>
/// 原神按键绑定配置
/// </summary>
public KeyBindingsConfig KeyBindingsConfig { get; set; } = new();
[JsonIgnore]
public Action? OnAnyChangedAction { get; set; }
@ -176,6 +181,7 @@ public partial class AllConfig : ObservableObject
GenshinStartConfig.PropertyChanged += OnAnyPropertyChanged;
NotificationConfig.PropertyChanged += OnAnyPropertyChanged;
NotificationConfig.PropertyChanged += OnNotificationPropertyChanged;
KeyBindingsConfig.PropertyChanged += OnAnyPropertyChanged;
AutoPickConfig.PropertyChanged += OnAnyPropertyChanged;
AutoSkipConfig.PropertyChanged += OnAnyPropertyChanged;

View File

@ -0,0 +1,964 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Windows.Forms;
using System.Windows.Input;
using Windows.Networking.PushNotifications;
using static Vanara.PInvoke.User32;
namespace BetterGenshinImpact.Core.Config;
/// <summary>
/// 原神按键绑定配置
/// </summary>
[Serializable]
public partial class KeyBindingsConfig:ObservableObject
{
#region Actions
/// <summary>
/// 向前移动
/// </summary>
[ObservableProperty]
private KeyId _moveForward = KeyId.W;
/// <summary>
/// 向后移动
/// </summary>
[ObservableProperty]
private KeyId _moveBackward = KeyId.S;
/// <summary>
/// 向左移动
/// </summary>
[ObservableProperty]
private KeyId _moveLeft = KeyId.A;
/// <summary>
/// 向右移动
/// </summary>
[ObservableProperty]
private KeyId _moveRight = KeyId.D;
/// <summary>
/// 切换走/跑;特定操作模式下向下移动
/// </summary>
[ObservableProperty]
private KeyId _switchToWalkOrRun = KeyId.LeftCtrl;
/// <summary>
/// 普通攻击
/// </summary>
[ObservableProperty]
private KeyId _normalAttack = KeyId.MouseLeftButton;
/// <summary>
/// 元素战技
/// </summary>
[ObservableProperty]
private KeyId _elementalSkill = KeyId.E;
/// <summary>
/// 元素爆发
/// </summary>
[ObservableProperty]
private KeyId _elementalBurst = KeyId.Q;
/// <summary>
/// 冲刺(键盘)
/// </summary>
[ObservableProperty]
private KeyId _sprintKeyboard = KeyId.LeftShift;
/// <summary>
/// 冲刺(鼠标)
/// </summary>
[ObservableProperty]
private KeyId _sprintMouse = KeyId.MouseRightButton;
/// <summary>
/// 切换瞄准模式
/// </summary>
[ObservableProperty]
private KeyId _switchAimingMode = KeyId.R;
/// <summary>
/// 跳跃;特定操作模式下向上移动
/// </summary>
[ObservableProperty]
private KeyId _jump = KeyId.Space;
/// <summary>
/// 落下
/// </summary>
[ObservableProperty]
private KeyId _drop = KeyId.X;
/// <summary>
/// 拾取/交互自动拾取由AutoPick模块管理
/// </summary>
[ObservableProperty]
private KeyId _pickUpOrInteract = KeyId.F;
/// <summary>
/// 快捷使用小道具
/// </summary>
[ObservableProperty]
private KeyId _quickUseGadget = KeyId.Z;
/// <summary>
/// 特定玩法内交互操作
/// </summary>
[ObservableProperty]
private KeyId _interactionInSomeMode = KeyId.T;
/// <summary>
/// 开启任务追踪
/// </summary>
[ObservableProperty]
private KeyId _questNavigation = KeyId.V;
/// <summary>
/// 中断挑战
/// </summary>
[ObservableProperty]
private KeyId _abandonChallenge = KeyId.P;
/// <summary>
/// 切换小队角色1
/// </summary>
[ObservableProperty]
private KeyId _switchMember1 = KeyId.D1;
/// <summary>
/// 切换小队角色2
/// </summary>
[ObservableProperty]
private KeyId _switchMember2 = KeyId.D2;
/// <summary>
/// 切换小队角色3
/// </summary>
[ObservableProperty]
private KeyId _switchMember3 = KeyId.D3;
/// <summary>
/// 切换小队角色4
/// </summary>
[ObservableProperty]
private KeyId _switchMember4 = KeyId.D4;
/// <summary>
/// 切换小队角色5
/// </summary>
[ObservableProperty]
private KeyId _switchMember5 = KeyId.D5;
/// <summary>
/// 呼出快捷轮盘
/// </summary>
[ObservableProperty]
private KeyId _shortcutWheel = KeyId.Tab;
#endregion
#region Menus
/// <summary>
/// 打开背包
/// </summary>
[ObservableProperty]
private KeyId _openInventory = KeyId.B;
/// <summary>
/// 打开角色界面
/// </summary>
[ObservableProperty]
private KeyId _openCharacterScreen = KeyId.C;
/// <summary>
/// 打开地图
/// </summary>
[ObservableProperty]
private KeyId _openMap = KeyId.M;
/// <summary>
/// 打开派蒙界面
/// </summary>
[ObservableProperty]
private KeyId _openPaimonMenu = KeyId.Escape;
/// <summary>
/// 打开冒险之证界面
/// </summary>
[ObservableProperty]
private KeyId _openAdventurerHandbook = KeyId.F1;
/// <summary>
/// 打开多人游戏界面
/// </summary>
[ObservableProperty]
private KeyId _openCoOpScreen = KeyId.F2;
/// <summary>
/// 打开祈愿界面
/// </summary>
[ObservableProperty]
private KeyId _openWishScreen = KeyId.F3;
/// <summary>
/// 打开纪行界面
/// </summary>
[ObservableProperty]
private KeyId _openBattlePassScreen = KeyId.F4;
/// <summary>
/// 打开活动面板
/// </summary>
[ObservableProperty]
private KeyId _openTheEventsMenu = KeyId.F5;
/// <summary>
/// 打开玩法系统界面(尘歌壶内猫尾酒馆内)
/// </summary>
[ObservableProperty]
private KeyId _openTheSettingsMenu = KeyId.F6;
/// <summary>
/// 打开摆设界面(尘歌壶内)
/// </summary>
[ObservableProperty]
private KeyId _openTheFurnishingScreen = KeyId.F7;
/// <summary>
/// 打开星之归还(条件符合期间生效)
/// </summary>
[ObservableProperty]
private KeyId _openStellarReunion = KeyId.F8;
/// <summary>
/// 开关任务菜单
/// </summary>
[ObservableProperty]
private KeyId _openQuestMenu = KeyId.J;
/// <summary>
/// 打开通知详情
/// </summary>
[ObservableProperty]
private KeyId _openNotificationDetails = KeyId.Y;
/// <summary>
/// 打开聊天界面
/// </summary>
[ObservableProperty]
private KeyId _openChatScreen = KeyId.Enter;
/// <summary>
/// 打开特殊环境说明
/// </summary>
[ObservableProperty]
private KeyId _openSpecialEnvironmentInformation = KeyId.U;
/// <summary>
/// 查看教程详情
/// </summary>
[ObservableProperty]
private KeyId _checkTutorialDetails = KeyId.G;
/// <summary>
/// 长按打开元素视野
/// </summary>
[ObservableProperty]
private KeyId _elementalSight = KeyId.MouseMiddleButton;
/// <summary>
/// 呼出鼠标
/// </summary>
[ObservableProperty]
private KeyId _showCursor = KeyId.LeftAlt;
/// <summary>
/// 打开队伍配置界面
/// </summary>
[ObservableProperty]
private KeyId _openPartySetupScreen = KeyId.L;
/// <summary>
/// 打开好友界面
/// </summary>
[ObservableProperty]
private KeyId _openFriendsScreen = KeyId.O;
/// <summary>
/// 隐藏主界面
/// </summary>
[ObservableProperty]
private KeyId _hideUI = KeyId.Slash;
#endregion
}
public static class KeyIdConverter
{
/// <summary>
/// 将KeyId转换为字符串可在后续支持多语言按键名称的显示尽量与原神UI一致
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToName(this KeyId value)
{
return ToChineseName(value);
}
private static string ToChineseName(KeyId value)
{
return value switch
{
// 需要单独翻译的按键
KeyId.None => "<未指定>",
KeyId.Unknown => "<未知>",
KeyId.MouseLeftButton => "鼠标左键",
KeyId.MouseRightButton => "鼠标右键",
KeyId.MouseMiddleButton => "鼠标中键",
KeyId.MouseSideButton1 => "鼠标侧键1",
KeyId.MouseSideButton2 => "鼠标侧键2",
KeyId.Apps => "菜单键",
// 无需单独翻译的部分
_ => EnglishKeyNameToChinese(value),
};
}
private static string EnglishKeyNameToChinese(KeyId value)
{
var engName = ToEnglishName(value);
if (engName.StartsWith("Left ") || engName.StartsWith("Right "))
{
return engName.Replace("Left ", "左").Replace("Right ", "右");
}
return engName;
}
private static string ToEnglishName(KeyId value)
{
return value switch
{
// 需要转换的部分
KeyId.None => "<None>",
KeyId.Unknown => "<Unknown>",
KeyId.MouseLeftButton => "Mouse LButton",
KeyId.MouseRightButton => "Mouse RButton",
KeyId.MouseMiddleButton => "Mouse MButton",
KeyId.MouseSideButton1 => "Mouse XButton1",
KeyId.MouseSideButton2 => "Mouse XButton2",
KeyId.Escape => "Esc",
KeyId.PageUp => "Page Up",
KeyId.PageDown => "Page Down",
KeyId.CapsLock => "Caps Lock",
KeyId.ScrollLock => "Scroll Lock",
KeyId.LeftShift => "Left Shift",
KeyId.RightShift => "Right Shift",
KeyId.LeftCtrl => "Left Crtl",
KeyId.RightCtrl => "Right Ctrl",
KeyId.LeftAlt => "Left Alt",
KeyId.RightAlt => "Right Alt",
KeyId.LeftWin => "Left Win",
KeyId.RightWin => "Right Win",
KeyId.Apps => "Menu",
KeyId.Left => "←",
KeyId.Up => "↑",
KeyId.Right => "→",
KeyId.Down => "↓",
KeyId.D0 => "0",
KeyId.D1 => "1",
KeyId.D2 => "2",
KeyId.D3 => "3",
KeyId.D4 => "4",
KeyId.D5 => "5",
KeyId.D6 => "6",
KeyId.D7 => "7",
KeyId.D8 => "8",
KeyId.D9 => "9",
KeyId.Apostrophe => "'",
KeyId.Comma => ",",
KeyId.Minus => "-",
KeyId.Equal => "=",
KeyId.Period => ".",
KeyId.Slash => "/",
KeyId.Backslash => @"\",
KeyId.Semicolon => ";",
KeyId.LeftSquareBracket => "[",
KeyId.RightSquareBracket => "]",
KeyId.Tilde => "`",
KeyId.NumLock => "Num Lock",
KeyId.NumPad0 => "Num 0",
KeyId.NumPad1 => "Num 1",
KeyId.NumPad2 => "Num 2",
KeyId.NumPad3 => "Num 3",
KeyId.NumPad4 => "Num 4",
KeyId.NumPad5 => "Num 5",
KeyId.NumPad6 => "Num 6",
KeyId.NumPad7 => "Num 7",
KeyId.NumPad8 => "Num 8",
KeyId.NumPad9 => "Num 9",
KeyId.Decimal => "Num .",
KeyId.Divide => "Num /",
KeyId.Multiply => "Num *",
KeyId.Subtract => "Num -",
KeyId.Add => "Num +",
KeyId.NumEnter => "Num Enter",
// 默认使用枚举名
_ => value.ToString(),
};
}
/// <summary>
/// 将KeyId转换为VK
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static VK ToVK(this KeyId value)
{
return value switch
{
// 这两个值在VK中没有抛异常
KeyId.None => throw new ArgumentOutOfRangeException(nameof(value), "未指定按键无法转换为VK。"),
KeyId.Unknown => throw new ArgumentOutOfRangeException(nameof(value), "未知按键无法转换为VK。"),
// 剩下的值相同,直接转
_ => (VK)value,
};
}
/// <summary>
/// 将KeyId转换为System.Windows.Input.Key
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static Key ToInputKey(this KeyId value)
{
// 部分按键名称相同,使用名称转换
try
{
return Enum.Parse<Key>(value.ToString());
}
catch
{
// 其他键使用查表法转换
return value switch
{
KeyId.LeftWin => Key.LWin,
KeyId.RightWin => Key.RWin,
KeyId.Apostrophe => Key.Oem7,
KeyId.Comma => Key.OemComma,
KeyId.Minus => Key.OemMinus,
KeyId.Equal => Key.OemPlus,
KeyId.Period => Key.OemPeriod,
KeyId.Slash => Key.Oem2,
KeyId.Semicolon => Key.Oem1,
KeyId.LeftSquareBracket => Key.Oem4,
KeyId.Backslash => Key.Oem5,
KeyId.RightSquareBracket => Key.Oem6,
KeyId.Tilde => Key.Oem3,
KeyId.Enter => Key.Enter,
KeyId.ScrollLock => Key.Scroll,
KeyId.PageUp => Key.Prior,
KeyId.PageDown => Key.Next,
KeyId.Backspace => Key.Back,
KeyId.CapsLock => Key.Capital,
// None、Unknown和鼠标按键抛异常
_ => throw new ArgumentOutOfRangeException(nameof(value)),
};
}
}
/// <summary>
/// 将KeyId转换为MouseButton
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static MouseButton ToMouseButton(this KeyId value)
{
return value switch
{
KeyId.MouseLeftButton => MouseButton.Left,
KeyId.MouseRightButton => MouseButton.Right,
KeyId.MouseMiddleButton => MouseButton.Middle,
KeyId.MouseSideButton1 => MouseButton.XButton1,
KeyId.MouseSideButton2 => MouseButton.XButton2,
_ => throw new ArgumentOutOfRangeException(nameof(value), "键盘按键请使用ToInputKey方法"),
};
}
/// <summary>
/// [实验] 将KeyId转换为WinForm中的Keys用于兼容按键连发功能
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static Keys ToWinFormKeys(this KeyId value)
{
try
{
return Enum.Parse<Keys>(value.ToInputKey().ToString());
}
catch
{
return default;
}
}
/// <summary>
/// 将VK转换为KeyId
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static KeyId FromVK(VK value)
{
// 尝试通过VK的值获取KeyId的枚举名。若成功表示对应的VK在KeyId支持的范围内直接转换否则返回Unknown
return string.IsNullOrEmpty(Enum.GetName(typeof(KeyId), value)) ? KeyId.Unknown : (KeyId)value;
}
/// <summary>
/// 将System.Windows.Input.Key转换为KeyId
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static KeyId FromInputKey(Key value)
{
// 部分按键名称相同,使用名称转换
try
{
return Enum.Parse<KeyId>(value.ToString());
}
catch
{
// 其他键使用查表法转换
return value switch
{
Key.LWin => KeyId.LeftWin,
Key.RWin => KeyId.RightWin,
Key.Oem7 => KeyId.Apostrophe,
Key.OemComma => KeyId.Comma,
Key.OemMinus => KeyId.Minus,
Key.OemPlus => KeyId.Equal,
Key.OemPeriod => KeyId.Period,
Key.Oem2 => KeyId.Slash,
Key.Oem1 => KeyId.Semicolon,
Key.Oem4 => KeyId.LeftSquareBracket,
Key.Oem5 => KeyId.Backslash,
Key.Oem6 => KeyId.RightSquareBracket,
Key.Oem3 => KeyId.Tilde,
Key.Enter => KeyId.Enter,
Key.Scroll => KeyId.ScrollLock,
Key.Prior => KeyId.PageUp,
Key.Next => KeyId.PageDown,
Key.Back => KeyId.Backspace,
Key.Capital => KeyId.CapsLock,
// 支持列表外的值返回Unknown
_ => KeyId.Unknown,
};
}
}
/// <summary>
/// 将MouseButton转换为KeyId
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static KeyId FromMouseButton(MouseButton value)
{
return value switch
{
MouseButton.Left => KeyId.MouseLeftButton,
MouseButton.Right => KeyId.MouseRightButton,
MouseButton.Middle => KeyId.MouseMiddleButton,
MouseButton.XButton1 => KeyId.MouseSideButton1,
MouseButton.XButton2 => KeyId.MouseSideButton2,
_ => KeyId.Unknown,
};
}
}
/// <summary>
/// 用于与VK/Windows.Input.Key解耦值与VK对应但是仅包含美式键盘(104键不包括媒体控制等)和鼠标中的按键
/// </summary>
public enum KeyId
{
/// <summary>无</summary>
None = 0x00,
/// <summary>未知按键</summary>
Unknown = 0xFF,
#region
/// <summary>鼠标左键</summary>
MouseLeftButton = 0x01,
/// <summary>鼠标右键</summary>
MouseRightButton = 0x02,
/// <summary>鼠标中键(滚轮)</summary>
MouseMiddleButton = 0x04,
/// <summary>鼠标侧键1后退</summary>
MouseSideButton1 = 0x05,
/// <summary>鼠标侧键2前进</summary>
MouseSideButton2 = 0x06,
#endregion
#region F键区
/// <summary>F1</summary>
F1 = 0x70,
/// <summary>F2</summary>
F2 = 0x71,
/// <summary>F3</summary>
F3 = 0x72,
/// <summary>F4</summary>
F4 = 0x73,
/// <summary>F5</summary>
F5 = 0x74,
/// <summary>F6</summary>
F6 = 0x75,
/// <summary>F7</summary>
F7 = 0x76,
/// <summary>F8</summary>
F8 = 0x77,
/// <summary>F9</summary>
F9 = 0x78,
/// <summary>F10</summary>
F10 = 0x79,
/// <summary>F11</summary>
F11 = 0x7A,
/// <summary>F12</summary>
F12 = 0x7B,
#endregion
#region &
/// <summary>Esc</summary>
Escape = 0x1B,
/// <summary>PrintScreen</summary>
PrintScreen = 0x2C,
/// <summary>ScrollLock</summary>
ScrollLock = 0x91,
/// <summary>Pause</summary>
Pause = 0x13,
/// <summary>Insert</summary>
Insert = 0x2D,
/// <summary>Delete</summary>
Delete = 0x2E,
/// <summary>Home</summary>
Home = 0x24,
/// <summary>End</summary>
End = 0x23,
/// <summary>Page Up</summary>
PageUp = 0x21,
/// <summary>Page Down</summary>
PageDown = 0x22,
/// <summary>Backspace退格</summary>
Backspace = 0x08,
/// <summary>Tab</summary>
Tab = 0x09,
/// <summary>Caps Lock大写锁定</summary>
CapsLock = 0x14,
/// <summary>Enter回车</summary>
Enter = 0x0D,
///// <summary>Shift</summary>
//Shift = 0x10,
/// <summary>左Shift</summary>
LeftShift = 0xA0,
/// <summary>右Shift</summary>
RightShift = 0xA1,
///// <summary>Ctrl</summary>
//Ctrl = 0x11,
/// <summary>左Ctrl</summary>
LeftCtrl = 0xA2,
/// <summary>右Ctrl</summary>
RightCtrl = 0xA3,
///// <summary>Alt</summary>
//Alt = 0x12,
/// <summary>左Alt</summary>
LeftAlt = 0xA4,
/// <summary>右Alt</summary>
RightAlt = 0xA5,
/// <summary>左Win键 (Microsoft Natural Keyboard)</summary>
LeftWin = 0x5B,
/// <summary>右Win键 (Microsoft Natural Keyboard)</summary>
RightWin = 0x5C,
/// <summary>菜单键 (Microsoft Natural Keyboard)</summary>
Apps = 0x5D,
/// <summary>Space空格键</summary>
Space = 0x20,
#endregion
#region
/// <summary>方向键 ←</summary>
Left = 0x25,
/// <summary>方向键 ↑</summary>
Up = 0x26,
/// <summary>方向键 →</summary>
Right = 0x27,
/// <summary>方向键 ↓</summary>
Down = 0x28,
#endregion
#region -
/// <summary>A</summary>
A = 0x41,
/// <summary>B</summary>
B = 0x42,
/// <summary>C</summary>
C = 0x43,
/// <summary>D</summary>
D = 0x44,
/// <summary>E</summary>
E = 0x45,
/// <summary>F</summary>
F = 0x46,
/// <summary>G</summary>
G = 0x47,
/// <summary>H</summary>
H = 0x48,
/// <summary>I</summary>
I = 0x49,
/// <summary>J</summary>
J = 0x4A,
/// <summary>K</summary>
K = 0x4B,
/// <summary>L</summary>
L = 0x4C,
/// <summary>M</summary>
M = 0x4D,
/// <summary>N</summary>
N = 0x4E,
/// <summary>O</summary>
O = 0x4F,
/// <summary>P</summary>
P = 0x50,
/// <summary>Q</summary>
Q = 0x51,
/// <summary>R</summary>
R = 0x52,
/// <summary>S</summary>
S = 0x53,
/// <summary>T</summary>
T = 0x54,
/// <summary>U</summary>
U = 0x55,
/// <summary>V</summary>
V = 0x56,
/// <summary>W</summary>
W = 0x57,
/// <summary>X</summary>
X = 0x58,
/// <summary>Y</summary>
Y = 0x59,
/// <summary>Z</summary>
Z = 0x5A,
#endregion
#region -
/// <summary>0</summary>
D0 = 0x30,
/// <summary>1</summary>
D1 = 0x31,
/// <summary>2</summary>
D2 = 0x32,
/// <summary>3</summary>
D3 = 0x33,
/// <summary>4</summary>
D4 = 0x34,
/// <summary>5</summary>
D5 = 0x35,
/// <summary>6</summary>
D6 = 0x36,
/// <summary>7</summary>
D7 = 0x37,
/// <summary>8</summary>
D8 = 0x38,
/// <summary>9</summary>
D9 = 0x39,
#endregion
#region -
/// <summary>引号 '</summary>
Apostrophe = 0xDE,
/// <summary>逗号 ,</summary>
Comma = 0xBC,
/// <summary>连接符 -</summary>
Minus = 0xBD,
/// <summary>等于号 =</summary>
Equal = 0xBB,
/// <summary>句号 .</summary>
Period = 0xBE,
/// <summary>斜杠 /</summary>
Slash = 0xBF,
/// <summary>反斜杠 \</summary>
Backslash = 0xE2,
/// <summary>分号 ;</summary>
Semicolon = 0xBA,
/// <summary>左方括号 [</summary>
LeftSquareBracket = 0xDB,
/// <summary>右方括号 ]</summary>
RightSquareBracket = 0xDD,
/// <summary>波浪号 `</summary>
Tilde = 0xC0,
#endregion
#region
/// <summary>Num Lock</summary>
NumLock = 0x90,
/// <summary>Num 0</summary>
NumPad0 = 0x60,
/// <summary>Num 1</summary>
NumPad1 = 0x61,
/// <summary>Num 2</summary>
NumPad2 = 0x62,
/// <summary>Num 3</summary>
NumPad3 = 0x63,
/// <summary>Num 4</summary>
NumPad4 = 0x64,
/// <summary>Num 5</summary>
NumPad5 = 0x65,
/// <summary>Num 6</summary>
NumPad6 = 0x66,
/// <summary>Num 7</summary>
NumPad7 = 0x67,
/// <summary>Num 8</summary>
NumPad8 = 0x68,
/// <summary>Num 9</summary>
NumPad9 = 0x69,
/// <summary>Num .</summary>
Decimal = 0x6E,
/// <summary>Num /</summary>
Divide = 0x6F,
/// <summary>Num *</summary>
Multiply = 0x6A,
/// <summary>Num -</summary>
Subtract = 0x6D,
/// <summary>Num +</summary>
Add = 0x6B,
/// <summary>Num Enter</summary>
NumEnter = 0x0E,
#endregion
}

View File

@ -1,4 +1,5 @@
using BetterGenshinImpact.Core.Recorder;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Recorder;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.Model;
@ -18,6 +19,10 @@ public class MouseKeyMonitor
/// </summary>
private readonly Timer _fTimer = new();
private Keys _pickUpKey = Keys.F;
private User32.VK _pickUpKeyCode = User32.VK.VK_F;
//private readonly Random _random = new();
/// <summary>
@ -25,6 +30,10 @@ public class MouseKeyMonitor
/// </summary>
private readonly Timer _spaceTimer = new();
private Keys _releaseControlKey = Keys.Space;
private User32.VK _releaseControlKeyCode = User32.VK.VK_SPACE;
private DateTime _firstFKeyDownTime = DateTime.MaxValue;
/// <summary>
@ -49,14 +58,19 @@ public class MouseKeyMonitor
_globalHook.MouseWheelExt += GlobalHookMouseWheelExt;
//_globalHook.KeyPress += GlobalHookKeyPress;
_pickUpKey = TaskContext.Instance().Config.KeyBindingsConfig.PickUpOrInteract.ToWinFormKeys();
_pickUpKeyCode = TaskContext.Instance().Config.KeyBindingsConfig.PickUpOrInteract.ToVK();
_releaseControlKey = TaskContext.Instance().Config.KeyBindingsConfig.Jump.ToWinFormKeys();
_releaseControlKeyCode = TaskContext.Instance().Config.KeyBindingsConfig.Jump.ToVK();
_firstSpaceKeyDownTime = DateTime.MaxValue;
var si = TaskContext.Instance().Config.MacroConfig.SpaceFireInterval;
_spaceTimer.Interval = si;
_spaceTimer.Elapsed += (sender, args) => { Simulation.PostMessage(_hWnd).KeyPress(User32.VK.VK_SPACE); };
_spaceTimer.Elapsed += (sender, args) => { Simulation.PostMessage(_hWnd).KeyPress(_releaseControlKeyCode); };
var fi = TaskContext.Instance().Config.MacroConfig.FFireInterval;
_fTimer.Interval = fi;
_fTimer.Elapsed += (sender, args) => { Simulation.PostMessage(_hWnd).KeyPress(User32.VK.VK_F); };
_fTimer.Elapsed += (sender, args) => { Simulation.PostMessage(_hWnd).KeyPress(_pickUpKeyCode); };
}
private void GlobalHookKeyDown(object? sender, KeyEventArgs e)
@ -67,7 +81,7 @@ public class MouseKeyMonitor
// 热键按下事件
HotKeyDown(sender, e);
if (e.KeyCode == Keys.Space)
if (e.KeyCode == _releaseControlKey)
{
if (_firstSpaceKeyDownTime == DateTime.MaxValue)
{
@ -81,7 +95,7 @@ public class MouseKeyMonitor
_spaceTimer.Start();
}
}
else if (e.KeyCode == Keys.F)
else if (e.KeyCode == _pickUpKey)
{
if (_firstFKeyDownTime == DateTime.MaxValue)
{
@ -105,7 +119,7 @@ public class MouseKeyMonitor
// 热键松开事件
HotKeyUp(sender, e);
if (e.KeyCode == Keys.Space)
if (e.KeyCode == _releaseControlKey)
{
if (_firstSpaceKeyDownTime != DateTime.MaxValue)
{
@ -115,7 +129,7 @@ public class MouseKeyMonitor
_spaceTimer.Stop();
}
}
else if (e.KeyCode == Keys.F)
else if (e.KeyCode == _pickUpKey)
{
if (_firstFKeyDownTime != DateTime.MaxValue)
{

View File

@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterGenshinImpact.Core.Simulator.Extensions;
/// <summary>
/// 模拟按键类型
/// </summary>
public enum KeyType
{
/// <summary>
/// 单击按键KeyPress
/// </summary>
KeyPress,
/// <summary>
/// 按住按键KeyDown
/// </summary>
KeyDown,
/// <summary>
/// 释放按键KeyUp
/// </summary>
KeyUp,
/// <summary>
/// 长按1s
/// </summary>
Hold,
}
/// <summary>
/// 原神按键动作
/// </summary>
public enum GIActions
{
/// <summary>
/// 向前移动
/// </summary>
MoveForward,
/// <summary>
/// 向后移动
/// </summary>
MoveBackward,
/// <summary>
/// 向左移动
/// </summary>
MoveLeft,
/// <summary>
/// 向右移动
/// </summary>
MoveRight,
/// <summary>
/// 切换走/跑;特定操作模式下向下移动
/// </summary>
SwitchToWalkOrRun,
/// <summary>
/// 普通攻击
/// </summary>
NormalAttack,
/// <summary>
/// 元素战技
/// </summary>
ElementalSkill,
/// <summary>
/// 元素爆发
/// </summary>
ElementalBurst,
/// <summary>
/// 冲刺(键盘)
/// </summary>
SprintKeyboard,
/// <summary>
/// 冲刺(鼠标)
/// </summary>
SprintMouse,
/// <summary>
/// 切换瞄准模式
/// </summary>
SwitchAimingMode,
/// <summary>
/// 跳跃;特定操作模式下向上移动
/// </summary>
Jump,
/// <summary>
/// 落下
/// </summary>
Drop,
/// <summary>
/// 拾取/交互自动拾取由AutoPick模块管理
/// </summary>
PickUpOrInteract,
/// <summary>
/// 快捷使用小道具
/// </summary>
QuickUseGadget,
/// <summary>
/// 特定玩法内交互操作
/// </summary>
InteractionInSomeMode,
/// <summary>
/// 开启任务追踪
/// </summary>
QuestNavigation,
/// <summary>
/// 中断挑战
/// </summary>
AbandonChallenge,
/// <summary>
/// 切换小队角色1
/// </summary>
SwitchMember1,
/// <summary>
/// 切换小队角色2
/// </summary>
SwitchMember2,
/// <summary>
/// 切换小队角色3
/// </summary>
SwitchMember3,
/// <summary>
/// 切换小队角色4
/// </summary>
SwitchMember4,
/// <summary>
/// 切换小队角色5
/// </summary>
SwitchMember5,
/// <summary>
/// 呼出快捷轮盘
/// </summary>
ShortcutWheel,
/// <summary>
/// 打开背包
/// </summary>
OpenInventory,
/// <summary>
/// 打开角色界面
/// </summary>
OpenCharacterScreen,
/// <summary>
/// 打开地图
/// </summary>
OpenMap,
/// <summary>
/// 打开派蒙界面
/// </summary>
OpenPaimonMenu,
/// <summary>
/// 打开冒险之证界面
/// </summary>
OpenAdventurerHandbook,
/// <summary>
/// 打开多人游戏界面
/// </summary>
OpenCoOpScreen,
/// <summary>
/// 打开祈愿界面
/// </summary>
OpenWishScreen,
/// <summary>
/// 打开纪行界面
/// </summary>
OpenBattlePassScreen,
/// <summary>
/// 打开活动面板
/// </summary>
OpenTheEventsMenu,
/// <summary>
/// 打开玩法系统界面(尘歌壶内猫尾酒馆内)
/// </summary>
OpenTheSettingsMenu,
/// <summary>
/// 打开摆设界面(尘歌壶内)
/// </summary>
OpenTheFurnishingScreen,
/// <summary>
/// 打开星之归还(条件符合期间生效)
/// </summary>
OpenStellarReunion,
/// <summary>
/// 开关任务菜单
/// </summary>
OpenQuestMenu,
/// <summary>
/// 打开通知详情
/// </summary>
OpenNotificationDetails,
/// <summary>
/// 打开聊天界面
/// </summary>
OpenChatScreen,
/// <summary>
/// 打开特殊环境说明
/// </summary>
OpenSpecialEnvironmentInformation,
/// <summary>
/// 查看教程详情
/// </summary>
CheckTutorialDetails,
/// <summary>
/// 长按打开元素视野
/// </summary>
ElementalSight,
/// <summary>
/// 呼出鼠标
/// </summary>
ShowCursor,
/// <summary>
/// 打开队伍配置界面
/// </summary>
OpenPartySetupScreen,
/// <summary>
/// 打开好友界面
/// </summary>
OpenFriendsScreen,
/// <summary>
/// 隐藏主界面
/// </summary>
HideUI,
}

View File

@ -0,0 +1,135 @@
using BetterGenshinImpact.Core.Config;
using System;
using System.Collections.Generic;
using System.Text;
using BetterGenshinImpact.Core.Simulator;
using System.Threading;
using Fischless.WindowsInput;
namespace BetterGenshinImpact.Core.Simulator.Extensions;
/// <summary>
/// 用于扩展<seealso cref="Fischless.WindowsInput.InputSimulator"/>的功能
/// </summary>
public static class InputSimulatorExtension
{
/// <summary>
/// 模拟玩家操作
/// </summary>
/// <param name="action">动作</param>
/// <param name="type">按键类型</param>
public static void SimulateAction(this InputSimulator self, GIActions action, KeyType type = KeyType.KeyPress)
{
var key = action.ToActionKey();
switch (type)
{
case KeyType.KeyPress:
KeyPress(self, key);
break;
case KeyType.KeyDown:
KeyDown(self, key);
break;
case KeyType.KeyUp:
KeyUp(self, key);
break;
case KeyType.Hold:
HoldKeyPress(self, key);
break;
default:
break;
}
}
private static void HoldKeyPress(InputSimulator self, KeyId key)
{
KeyDown(self, key);
Thread.Sleep(1000);
KeyUp(self, key);
}
private static void KeyPress(InputSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
self.Mouse.LeftButtonClick();
break;
case KeyId.MouseRightButton:
self.Mouse.RightButtonClick();
break;
case KeyId.MouseMiddleButton:
self.Mouse.MiddleButtonClick();
break;
case KeyId.MouseSideButton1:
self.Mouse.XButtonClick(0x0001);
break;
case KeyId.MouseSideButton2:
self.Mouse.XButtonClick(0x0001);
break;
default:
self.Keyboard.KeyPress(key.ToVK());
break;
}
}
private static void KeyDown(InputSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
self.Mouse.LeftButtonDown();
break;
case KeyId.MouseRightButton:
self.Mouse.RightButtonDown();
break;
case KeyId.MouseMiddleButton:
self.Mouse.MiddleButtonDown();
break;
case KeyId.MouseSideButton1:
self.Mouse.XButtonDown(0x0001);
break;
case KeyId.MouseSideButton2:
self.Mouse.XButtonDown(0x0001);
break;
default:
self.Keyboard.KeyDown(key.ToVK());
break;
}
}
private static void KeyUp(InputSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
self.Mouse.LeftButtonUp();
break;
case KeyId.MouseRightButton:
self.Mouse.RightButtonUp();
break;
case KeyId.MouseMiddleButton:
self.Mouse.MiddleButtonUp();
break;
case KeyId.MouseSideButton1:
self.Mouse.XButtonUp(0x0001);
break;
case KeyId.MouseSideButton2:
self.Mouse.XButtonUp(0x0001);
break;
default:
self.Keyboard.KeyUp(key.ToVK());
break;
}
}
}

View File

@ -0,0 +1,204 @@
using BetterGenshinImpact.Core.Config;
using Fischless.WindowsInput;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace BetterGenshinImpact.Core.Simulator.Extensions;
/// <summary>
/// 为<seealso cref="PostMessageSimulator"/>提供扩展功能
/// </summary>
public static class PostMessageSimulatorExtension
{
/// <summary>
/// 模拟玩家操作
/// </summary>
/// <param name="action">动作</param>
/// <param name="type">按键类型</param>
public static PostMessageSimulator SimulateAction(this PostMessageSimulator self, GIActions action, KeyType type = KeyType.KeyPress)
{
var key = action.ToActionKey();
switch (type)
{
case KeyType.KeyPress:
KeyPress(self, key);
break;
case KeyType.KeyDown:
KeyDown(self, key);
break;
case KeyType.KeyUp:
KeyUp(self, key);
break;
case KeyType.Hold:
HoldKeyPress(self, key);
break;
default:
break;
}
return self;
}
private static void HoldKeyPress(PostMessageSimulator self, KeyId key)
{
self.LongKeyPress(key.ToVK());
}
private static void KeyPress(PostMessageSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
self.LeftButtonClick();
break;
case KeyId.MouseRightButton:
self.RightButtonClick();
break;
case KeyId.MouseMiddleButton:
case KeyId.MouseSideButton1:
case KeyId.MouseSideButton2:
break;
default:
self.KeyPress(key.ToVK());
break;
}
}
private static void KeyDown(PostMessageSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
self.LeftButtonDown();
break;
case KeyId.MouseRightButton:
self.RightButtonDown();
break;
case KeyId.MouseMiddleButton:
case KeyId.MouseSideButton1:
case KeyId.MouseSideButton2:
break;
default:
self.KeyDown(key.ToVK());
break;
}
}
private static void KeyUp(PostMessageSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
self.LeftButtonUp();
break;
case KeyId.MouseRightButton:
self.RightButtonUp();
break;
case KeyId.MouseMiddleButton:
case KeyId.MouseSideButton1:
case KeyId.MouseSideButton2:
break;
default:
self.KeyUp(key.ToVK());
break;
}
}
/// <summary>
/// 模拟玩家操作(后台)
/// </summary>
/// <param name="action">动作</param>
/// <param name="type">按键类型</param>
public static PostMessageSimulator SimulateActionBackground(this PostMessageSimulator self, GIActions action, KeyType type = KeyType.KeyPress)
{
var key = action.ToActionKey();
switch (type)
{
case KeyType.KeyPress:
KeyPressBackground(self, key);
break;
case KeyType.KeyDown:
KeyDownBackground(self, key);
break;
case KeyType.KeyUp:
KeyUpBackground(self, key);
break;
case KeyType.Hold:
break;
default:
break;
}
return self;
}
private static void KeyPressBackground(PostMessageSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
self.LeftButtonClickBackground();
break;
case KeyId.MouseRightButton:
case KeyId.MouseMiddleButton:
case KeyId.MouseSideButton1:
case KeyId.MouseSideButton2:
break;
default:
self.KeyPressBackground(key.ToVK());
break;
}
}
private static void KeyDownBackground(PostMessageSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
case KeyId.MouseRightButton:
case KeyId.MouseMiddleButton:
case KeyId.MouseSideButton1:
case KeyId.MouseSideButton2:
break;
default:
self.KeyDownBackground(key.ToVK());
break;
}
}
private static void KeyUpBackground(PostMessageSimulator self, KeyId key)
{
switch (key)
{
case KeyId.None:
case KeyId.Unknown:
break;
case KeyId.MouseLeftButton:
case KeyId.MouseRightButton:
case KeyId.MouseMiddleButton:
case KeyId.MouseSideButton1:
case KeyId.MouseSideButton2:
break;
default:
self.KeyUpBackground(key.ToVK());
break;
}
}
}

View File

@ -0,0 +1,79 @@
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.GameTask;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace BetterGenshinImpact.Core.Simulator.Extensions;
public static class SimulateKeyHelper
{
private static KeyBindingsConfig KeyConfig => TaskContext.Instance().Config.KeyBindingsConfig;
public static KeyId ToActionKey(this GIActions action)
{
return GetActionKey(action);
}
/// <summary>
/// 通过ActionId取得实际的键盘绑定
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
public static KeyId GetActionKey(GIActions action)
{
return action switch
{
GIActions.MoveForward => KeyConfig.MoveForward,
GIActions.MoveBackward => KeyConfig.MoveBackward,
GIActions.MoveLeft => KeyConfig.MoveLeft,
GIActions.MoveRight => KeyConfig.MoveRight,
GIActions.SwitchToWalkOrRun => KeyConfig.SwitchToWalkOrRun,
GIActions.NormalAttack => KeyConfig.NormalAttack,
GIActions.ElementalSkill => KeyConfig.ElementalSkill,
GIActions.ElementalBurst => KeyConfig.ElementalBurst,
GIActions.SprintKeyboard => KeyConfig.SprintKeyboard,
GIActions.SprintMouse => KeyConfig.SprintMouse,
GIActions.SwitchAimingMode => KeyConfig.SwitchAimingMode,
GIActions.Jump => KeyConfig.Jump,
GIActions.Drop => KeyConfig.Drop,
GIActions.PickUpOrInteract => KeyConfig.PickUpOrInteract,
GIActions.QuickUseGadget => KeyConfig.QuickUseGadget,
GIActions.InteractionInSomeMode => KeyConfig.InteractionInSomeMode,
GIActions.QuestNavigation => KeyConfig.QuestNavigation,
GIActions.AbandonChallenge => KeyConfig.AbandonChallenge,
GIActions.SwitchMember1 => KeyConfig.SwitchMember1,
GIActions.SwitchMember2 => KeyConfig.SwitchMember2,
GIActions.SwitchMember3 => KeyConfig.SwitchMember3,
GIActions.SwitchMember4 => KeyConfig.SwitchMember4,
GIActions.SwitchMember5 => KeyConfig.SwitchMember5,
GIActions.ShortcutWheel => KeyConfig.ShortcutWheel,
GIActions.OpenInventory => KeyConfig.OpenInventory,
GIActions.OpenCharacterScreen => KeyConfig.OpenCharacterScreen,
GIActions.OpenMap => KeyConfig.OpenMap,
GIActions.OpenPaimonMenu => KeyConfig.OpenPaimonMenu,
GIActions.OpenAdventurerHandbook => KeyConfig.OpenAdventurerHandbook,
GIActions.OpenCoOpScreen => KeyConfig.OpenCoOpScreen,
GIActions.OpenWishScreen => KeyConfig.OpenWishScreen,
GIActions.OpenBattlePassScreen => KeyConfig.OpenBattlePassScreen,
GIActions.OpenTheEventsMenu => KeyConfig.OpenTheEventsMenu,
GIActions.OpenTheSettingsMenu => KeyConfig.OpenTheSettingsMenu,
GIActions.OpenTheFurnishingScreen => KeyConfig.OpenTheFurnishingScreen,
GIActions.OpenStellarReunion => KeyConfig.OpenStellarReunion,
GIActions.OpenQuestMenu => KeyConfig.OpenQuestMenu,
GIActions.OpenNotificationDetails => KeyConfig.OpenNotificationDetails,
GIActions.OpenChatScreen => KeyConfig.OpenChatScreen,
GIActions.OpenSpecialEnvironmentInformation => KeyConfig.OpenSpecialEnvironmentInformation,
GIActions.CheckTutorialDetails => KeyConfig.CheckTutorialDetails,
GIActions.ElementalSight => KeyConfig.ElementalSight,
GIActions.ShowCursor => KeyConfig.ShowCursor,
GIActions.OpenPartySetupScreen => KeyConfig.OpenPartySetupScreen,
GIActions.OpenFriendsScreen => KeyConfig.OpenFriendsScreen,
GIActions.HideUI => KeyConfig.HideUI,
_ => default,
};
}
}

View File

@ -2,6 +2,7 @@
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoFight;
using BetterGenshinImpact.GameTask.AutoFight.Assets;
using BetterGenshinImpact.GameTask.AutoFight.Model;
@ -223,31 +224,31 @@ public class AutoDomainTask : ISoloTask
if ("芬德尼尔之顶".Equals(_taskParam.DomainName))
{
Simulation.SendInput.Keyboard.KeyDown(VK.VK_S);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown);
Thread.Sleep(3000);
Simulation.SendInput.Keyboard.KeyUp(VK.VK_S);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp);
}
else if ("无妄引咎密宫".Equals(_taskParam.DomainName))
{
Simulation.SendInput.Keyboard.KeyDown(VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Thread.Sleep(500);
Simulation.SendInput.Keyboard.KeyUp(VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Thread.Sleep(100);
Simulation.SendInput.Keyboard.KeyDown(VK.VK_A);
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyDown);
Thread.Sleep(1600);
Simulation.SendInput.Keyboard.KeyUp(VK.VK_A);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
else if ("苍白的遗荣".Equals(_taskParam.DomainName))
{
Simulation.SendInput.Keyboard.KeyDown(VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Thread.Sleep(1000);
Simulation.SendInput.Keyboard.KeyUp(VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
else
{
Simulation.SendInput.Keyboard.KeyDown(VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Thread.Sleep(3000);
Simulation.SendInput.Keyboard.KeyUp(VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
await Delay(3000, _ct); // 站稳
@ -282,10 +283,10 @@ public class AutoDomainTask : ISoloTask
var fightAssets = AutoFightContext.Instance.FightAssets;
// 进入秘境
using var fRectArea = CaptureToRectArea().Find(AutoPickAssets.Instance.FRo);
using var fRectArea = CaptureToRectArea().Find(AutoPickAssets.Instance.PickRo);
if (!fRectArea.IsEmpty())
{
Simulation.SendInput.Keyboard.KeyPress(VK.VK_F);
Simulation.SendInput.Keyboard.KeyPress(AutoPickAssets.Instance.PickVk);
Logger.LogInformation("自动秘境:{Text}", "进入秘境");
// 秘境开门动画 5s
await Delay(5000, _ct);
@ -352,19 +353,19 @@ public class AutoDomainTask : ISoloTask
await Task.Run((Action)(() =>
{
_simulator.KeyDown(VK.VK_W);
_simulator.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Sleep(20);
// 组合键好像不能直接用 postmessage
if (!_config.WalkToF)
{
Simulation.SendInput.Keyboard.KeyDown(VK.VK_SHIFT);
Simulation.SendInput.SimulateAction(GIActions.SprintKeyboard, KeyType.KeyDown);
}
try
{
while (!_ct.IsCancellationRequested)
{
using var fRectArea = Common.TaskControl.CaptureToRectArea().Find(AutoPickAssets.Instance.FRo);
using var fRectArea = Common.TaskControl.CaptureToRectArea().Find(AutoPickAssets.Instance.PickRo);
if (fRectArea.IsEmpty())
{
Sleep(100, _ct);
@ -372,18 +373,18 @@ public class AutoDomainTask : ISoloTask
else
{
Logger.LogInformation("检测到交互键");
Simulation.SendInput.Keyboard.KeyPress(VK.VK_F);
Simulation.SendInput.Keyboard.KeyPress(AutoPickAssets.Instance.PickVk);
break;
}
}
}
finally
{
_simulator.KeyUp(VK.VK_W);
_simulator.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Sleep(50);
if (!_config.WalkToF)
{
Simulation.SendInput.Keyboard.KeyUp(VK.VK_SHIFT);
Simulation.SendInput.SimulateAction(GIActions.SprintKeyboard, KeyType.KeyUp);
}
}
}), _ct);
@ -519,7 +520,7 @@ public class AutoDomainTask : ISoloTask
if (Bv.CurrentAvatarIsLowHp(CaptureToRectArea()))
{
// 模拟按键 "Z"
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_Z);
Simulation.SendInput.SimulateAction(GIActions.QuickUseGadget);
Logger.LogInformation("检测到红血按Z吃药");
// TODO 吃饱了会一直吃
}
@ -569,12 +570,16 @@ public class AutoDomainTask : ISoloTask
{
return new Task(() =>
{
var keyConfig = TaskContext.Instance().Config.KeyBindingsConfig;
var moveLeftKey = keyConfig.MoveLeft.ToVK();
var moveRightKey = keyConfig.MoveRight.ToVK();
var moveForwardKey = keyConfig.MoveForward.ToVK();
var captureArea = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect;
var middleX = captureArea.Width / 2;
var leftKeyDown = false;
var rightKeyDown = false;
var noDetectCount = 0;
var prevKey = VK.VK_A;
var prevKey = moveLeftKey;
var backwardsAndForwardsCount = 0;
while (!_ct.IsCancellationRequested)
{
@ -590,13 +595,13 @@ public class AutoDomainTask : ISoloTask
if (rightKeyDown)
{
// 先松开D键
_simulator.KeyUp(VK.VK_D);
_simulator.KeyUp(moveRightKey);
rightKeyDown = false;
}
if (!leftKeyDown)
{
_simulator.KeyDown(VK.VK_A);
_simulator.KeyDown(moveLeftKey);
leftKeyDown = true;
}
}
@ -608,13 +613,13 @@ public class AutoDomainTask : ISoloTask
if (leftKeyDown)
{
// 先松开A键
_simulator.KeyUp(VK.VK_A);
_simulator.KeyUp(moveLeftKey);
leftKeyDown = false;
}
if (!rightKeyDown)
{
_simulator.KeyDown(VK.VK_D);
_simulator.KeyDown(moveRightKey);
rightKeyDown = true;
}
}
@ -623,42 +628,42 @@ public class AutoDomainTask : ISoloTask
// 树在中间 松开所有键
if (rightKeyDown)
{
_simulator.KeyUp(VK.VK_D);
prevKey = VK.VK_D;
_simulator.KeyUp(moveRightKey);
prevKey = moveRightKey;
rightKeyDown = false;
}
if (leftKeyDown)
{
_simulator.KeyUp(VK.VK_A);
prevKey = VK.VK_A;
_simulator.KeyUp(moveLeftKey);
prevKey = moveLeftKey;
leftKeyDown = false;
}
// 松开按键后使用小碎步移动
if (treeMiddleX < middleX)
{
if (prevKey == VK.VK_D)
if (prevKey == moveRightKey)
{
backwardsAndForwardsCount++;
}
_simulator.KeyPress(VK.VK_A, 60);
prevKey = VK.VK_A;
_simulator.KeyPress(moveLeftKey, 60);
prevKey = moveLeftKey;
}
else if (treeMiddleX > middleX)
{
if (prevKey == VK.VK_A)
if (prevKey == moveLeftKey)
{
backwardsAndForwardsCount++;
}
_simulator.KeyPress(VK.VK_D, 60);
prevKey = VK.VK_D;
_simulator.KeyPress(moveRightKey, 60);
prevKey = moveRightKey;
}
else
{
_simulator.KeyPress(VK.VK_W, 60);
_simulator.KeyPress(moveForwardKey, 60);
Sleep(500, _ct);
treeCts.Cancel();
break;
@ -674,13 +679,13 @@ public class AutoDomainTask : ISoloTask
{
if (leftKeyDown)
{
_simulator.KeyUp(VK.VK_A);
_simulator.KeyUp(moveLeftKey);
leftKeyDown = false;
}
if (!rightKeyDown)
{
_simulator.KeyDown(VK.VK_D);
_simulator.KeyDown(moveRightKey);
rightKeyDown = true;
}
}
@ -688,13 +693,13 @@ public class AutoDomainTask : ISoloTask
{
if (rightKeyDown)
{
_simulator.KeyUp(VK.VK_D);
_simulator.KeyUp(moveRightKey);
rightKeyDown = false;
}
if (!leftKeyDown)
{
_simulator.KeyDown(VK.VK_A);
_simulator.KeyDown(moveLeftKey);
leftKeyDown = true;
}
}
@ -703,7 +708,7 @@ public class AutoDomainTask : ISoloTask
if (backwardsAndForwardsCount >= _config.LeftRightMoveTimes)
{
// 左右移动5次说明已经在树中心了
_simulator.KeyPress(VK.VK_W, 60);
_simulator.KeyPress(moveForwardKey, 60);
Sleep(500, _ct);
treeCts.Cancel();
break;

View File

@ -1,5 +1,6 @@
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoFight.Model;
using BetterGenshinImpact.GameTask.AutoFight.Script;
using BetterGenshinImpact.GameTask.Model.Area;
@ -18,6 +19,7 @@ using BetterGenshinImpact.GameTask.Common.Job;
using OpenCvSharp;
using Vanara;
using Vanara.PInvoke;
using BetterGenshinImpact.Core.Config;
namespace BetterGenshinImpact.GameTask.AutoFight;
@ -345,7 +347,7 @@ public class AutoFightTask : ISoloTask
}
kazuha.UseSkill(true);
await Task.Delay(100);
Simulation.SendInput.Mouse.LeftButtonClick();
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
await Delay(1500, ct);
}
@ -418,9 +420,9 @@ public class AutoFightTask : ISoloTask
**/
await Delay(delayTime, _ct);
Logger.LogInformation("按L检查战斗是否结束");
Logger.LogInformation("打开编队界面检查战斗是否结束");
// 最终方案确认战斗结束
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_L);
Simulation.SendInput.SimulateAction(GIActions.OpenPartySetupScreen);
await Delay(450, _ct);
var ra = CaptureToRectArea();
var b3 = ra.SrcMat.At<Vec3b>(50, 790);
@ -428,11 +430,11 @@ public class AutoFightTask : ISoloTask
if (AreDifferencesWithinBounds(_finishDetectConfig.BattleEndProgressBarColor, (b3.Item0, b3.Item1, b3.Item2), _finishDetectConfig.BattleEndProgressBarColorTolerance))
{
Logger.LogInformation("识别到战斗结束");
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
Simulation.SendInput.SimulateAction(GIActions.Drop);
return true;
}
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
Simulation.SendInput.SimulateAction(GIActions.Drop);
Logger.LogInformation($"未识别到战斗结束{b3.Item0},{b3.Item1},{b3.Item2}");
_lastFightFlagTime = DateTime.Now;
return false;

View File

@ -2,6 +2,7 @@
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoFight.Config;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Helpers;
@ -16,6 +17,7 @@ using BetterGenshinImpact.GameTask.AutoTrackPath;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using BetterGenshinImpact.Core.Config;
namespace BetterGenshinImpact.GameTask.AutoFight.Model;
@ -89,6 +91,7 @@ public class Avatar
/// </summary>
public CombatScenes CombatScenes { get; set; }
public Avatar(CombatScenes combatScenes, string name, int index, Rect nameRect)
{
CombatScenes = combatScenes;
@ -116,9 +119,9 @@ public class Avatar
{
Logger.LogWarning("检测到复苏界面,存在角色被击败,前往七天神像复活");
// 先打开地图
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_ESCAPE);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_ESCAPE); // NOTE: 此处按下Esc是为了关闭复苏界面无需改键
Sleep(600, Ct);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_M);
Simulation.SendInput.SimulateAction(GIActions.OpenMap);
// tp 到七天神像复活
var tpTask = new TpTask(Ct);
tpTask.Tp(TpTask.ReviveStatueOfTheSevenPointX, TpTask.ReviveStatueOfTheSevenPointY, true).Wait(Ct);
@ -149,7 +152,26 @@ public class Avatar
return;
}
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_1 + (byte)Index - 1);
switch (Index)
{
case 1:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember1);
break;
case 2:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember2);
break;
case 3:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember3);
break;
case 4:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember4);
break;
case 5:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember5);
break;
default:
break;
}
// Debug.WriteLine($"切换到{Index}号位");
// Cv2.ImWrite($"log/切换.png", region.SrcMat);
Sleep(250, Ct);
@ -185,7 +207,26 @@ public class Avatar
return true;
}
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_1 + (byte)Index - 1);
switch (Index)
{
case 1:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember1);
break;
case 2:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember2);
break;
case 3:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember3);
break;
case 4:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember4);
break;
case 5:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember5);
break;
default:
break;
}
Sleep(250, Ct);
}
@ -210,7 +251,26 @@ public class Avatar
return;
}
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_1 + (byte)Index - 1);
switch (Index)
{
case 1:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember1);
break;
case 2:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember2);
break;
case 3:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember3);
break;
case 4:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember4);
break;
case 5:
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember5);
break;
default:
break;
}
Sleep(250);
}
}
@ -305,7 +365,7 @@ public class Avatar
return;
}
AutoFightContext.Instance.Simulator.LeftButtonClick();
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack);
ms -= 200;
Sleep(200, Ct);
}
@ -327,7 +387,7 @@ public class Avatar
{
if (Name == "纳西妲")
{
AutoFightContext.Instance.Simulator.KeyDown(User32.VK.VK_E);
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyDown);
Sleep(300, Ct);
for (int j = 0; j < 10; j++)
{
@ -336,22 +396,22 @@ public class Avatar
}
Sleep(300); // 持续操作不应该被cts取消
AutoFightContext.Instance.Simulator.KeyUp(User32.VK.VK_E);
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyUp);
}
else if (Name == "坎蒂丝")
{
AutoFightContext.Instance.Simulator.KeyDown(User32.VK.VK_E);
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyDown);
Thread.Sleep(3000);
AutoFightContext.Instance.Simulator.KeyUp(User32.VK.VK_E);
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyUp);
}
else
{
AutoFightContext.Instance.Simulator.LongKeyPress(User32.VK.VK_E);
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.Hold);
}
}
else
{
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_E);
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyPress);
}
Sleep(200, Ct);
@ -395,7 +455,7 @@ public class Avatar
return;
}
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_Q);
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalBurst);
Sleep(200, Ct);
var region = CaptureToRectArea();
@ -450,9 +510,9 @@ public class Avatar
ms = 200;
}
AutoFightContext.Instance.Simulator.RightButtonDown();
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SprintMouse, KeyType.KeyDown);
Sleep(ms); // 冲刺不能被cts取消
AutoFightContext.Instance.Simulator.RightButtonUp();
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SprintMouse, KeyType.KeyUp);
}
public void Walk(string key, int ms)
@ -465,19 +525,19 @@ public class Avatar
User32.VK vk = User32.VK.VK_NONAME;
if (key == "w")
{
vk = User32.VK.VK_W;
vk = GIActions.MoveForward.ToActionKey().ToVK();
}
else if (key == "s")
{
vk = User32.VK.VK_S;
vk = GIActions.MoveBackward.ToActionKey().ToVK();
}
else if (key == "a")
{
vk = User32.VK.VK_A;
vk = GIActions.MoveLeft.ToActionKey().ToVK();
}
else if (key == "d")
{
vk = User32.VK.VK_D;
vk = GIActions.MoveRight.ToActionKey().ToVK();
}
if (vk == User32.VK.VK_NONAME)
@ -514,7 +574,7 @@ public class Avatar
/// </summary>
public void Jump()
{
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_SPACE);
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.Jump);
}
/// <summary>
@ -530,7 +590,7 @@ public class Avatar
if (Name == "那维莱特")
{
var dpi = TaskContext.Instance().DpiScale;
AutoFightContext.Instance.Simulator.LeftButtonDown();
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack, KeyType.KeyDown);
while (ms >= 0)
{
if (Ct is { IsCancellationRequested: true })
@ -543,13 +603,13 @@ public class Avatar
Sleep(50); // 持续操作不应该被cts取消
}
AutoFightContext.Instance.Simulator.LeftButtonUp();
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack, KeyType.KeyUp);
}
else
{
AutoFightContext.Instance.Simulator.LeftButtonDown();
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack, KeyType.KeyDown);
Sleep(ms); // 持续操作不应该被cts取消
AutoFightContext.Instance.Simulator.LeftButtonUp();
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack, KeyType.KeyUp);
}
}

View File

@ -7,6 +7,7 @@ using BetterGenshinImpact.GameTask.AutoPathing.Model;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Simulator.Extensions;
namespace BetterGenshinImpact.GameTask.AutoPathing.Handler;
@ -19,7 +20,7 @@ public class ElementalSkillHandler : IActionHandler
public async Task RunAsync(CancellationToken ct, WaypointForTrack? waypointForTrack = null, object? config = null)
{
Logger.LogInformation("执行 {Text}", "释放元素战技");
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill);
await Delay(1000, ct);
}
}

View File

@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoPathing.Model;
using BetterGenshinImpact.Helpers;
using Microsoft.Extensions.Logging;
@ -55,7 +56,7 @@ public class NahidaCollectHandler : IActionHandler
await Delay(200, ct);
// 按住E技能 无死角扫码
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyDown);
await Delay(200, ct);
// 先地面来一圈
@ -77,7 +78,7 @@ public class NahidaCollectHandler : IActionHandler
Simulation.SendInput.Mouse.MoveMouseBy(x, y);
await Delay(30, ct);
}
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyUp);
lastETime = DateTime.Now;

View File

@ -1,5 +1,6 @@
using System;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
@ -18,7 +19,7 @@ public class NormalAttackHandler : IActionHandler
public async Task RunAsync(CancellationToken ct, WaypointForTrack? waypointForTrack = null, object? config = null)
{
Logger.LogInformation("执行 {Text}", "普通攻击");
Simulation.SendInput.Mouse.LeftButtonClick();
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
await Delay(1000, ct);
}
}

View File

@ -9,6 +9,7 @@ using BetterGenshinImpact.Helpers;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Simulator.Extensions;
namespace BetterGenshinImpact.GameTask.AutoPathing.Handler;
@ -47,7 +48,7 @@ public class PickAroundHandler() : IActionHandler
public async Task MoveCircle(double edgeT, int n)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_A);
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyDown);
await Delay(30, _ct);
while (n-- > 0)
{
@ -55,7 +56,7 @@ public class PickAroundHandler() : IActionHandler
await Delay((int)Math.Round(edgeT), _ct);
}
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_A);
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyUp);
await Delay(200, _ct);
}
@ -67,9 +68,9 @@ public class PickAroundHandler() : IActionHandler
await Delay(500, _ct);
if (ms > 0)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
await Delay(ms, _ct);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
await Delay(200, _ct);
}
}
@ -80,8 +81,8 @@ public class PickAroundHandler() : IActionHandler
double y = oldRadius * Math.Sin(angle);
Simulation.SendInput.Mouse.MiddleButtonClick();
await Delay(500, _ct);
await MoveAfterTurn(User32.VK.VK_S, (int)Math.Round(y) + 200);
await MoveAfterTurn(User32.VK.VK_A, (int)Math.Round(x));
await MoveAfterTurn(GIActions.MoveBackward.ToActionKey().ToVK(), (int)Math.Round(y) + 200);
await MoveAfterTurn(GIActions.MoveLeft.ToActionKey().ToVK(), (int)Math.Round(x));
}
}

View File

@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoPathing.Model;
using Microsoft.Extensions.Logging;
using Vanara.PInvoke;
@ -25,7 +26,7 @@ public class UpDownGrabLeaf : IActionHandler
// kbPress('w'); // 飞行
while (i > 0 && !ct.IsCancellationRequested)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_T);
Simulation.SendInput.SimulateAction(GIActions.InteractionInSomeMode, KeyType.KeyDown);
if (i % 10 == 0)
{
y = -y;

View File

@ -30,6 +30,7 @@ using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using static BetterGenshinImpact.GameTask.SystemControl;
using ActionEnum = BetterGenshinImpact.GameTask.AutoPathing.Model.Enum.ActionEnum;
using BetterGenshinImpact.Core.Simulator.Extensions;
namespace BetterGenshinImpact.GameTask.AutoPathing;
@ -244,6 +245,24 @@ public class PathExecutor
}
}
catch (RetryException retryException)
{
StartSkipOtherOperations();
Logger.LogWarning(retryException.Message);
}
catch (RetryNoCountException retryException)
{
//特殊情况下,重试不消耗次数
i--;
StartSkipOtherOperations();
Logger.LogWarning(retryException.Message);
}
finally
{
// 不管咋样,松开所有按键
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.SprintMouse, KeyType.KeyUp);
}
}
}
finally
@ -528,9 +547,9 @@ public class PathExecutor
if (avatar.TrySwitch())
{
//1命白术能两次
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill);
await Delay(800, ct);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill);
await Delay(800, ct);
await SwitchAvatar(PartyConfig.MainAvatarIndex);
await Delay(4000, ct);
@ -543,7 +562,7 @@ public class PathExecutor
{
if (avatar.TrySwitch())
{
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill);
await Delay(11000, ct);
await SwitchAvatar(PartyConfig.MainAvatarIndex);
return true;
@ -555,10 +574,10 @@ public class PathExecutor
{
if (avatar.TrySwitch())
{
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill);
await Delay(500, ct);
//尝试Q全队回血
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_Q);
Simulation.SendInput.SimulateAction(GIActions.ElementalBurst);
//单人血只给行走位加血
await SwitchAvatar(PartyConfig.MainAvatarIndex);
await Delay(5000, ct);
@ -632,7 +651,7 @@ public class PathExecutor
var num = 0;
// 按下w一直走
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
while (!ct.IsCancellationRequested)
{
num++;
@ -694,7 +713,7 @@ public class PathExecutor
//调用脱困代码由TrapEscaper接管移动
await _trapEscaper.RotateAndMove();
await _trapEscaper.MoveTo(waypoint);
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Logger.LogInformation("卡死脱离结束");
continue;
}
@ -714,7 +733,7 @@ public class PathExecutor
if (!isFlying)
{
Debug.WriteLine("未进入飞行状态,按下空格");
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
Simulation.SendInput.SimulateAction(GIActions.Jump);
await Delay(200, ct);
}
@ -723,7 +742,7 @@ public class PathExecutor
if (waypoint.MoveMode == MoveModeEnum.Jump.Code)
{
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
Simulation.SendInput.SimulateAction(GIActions.Jump);
await Delay(200, ct);
continue;
}
@ -735,11 +754,11 @@ public class PathExecutor
{
if (fastMode)
{
Simulation.SendInput.Mouse.RightButtonUp();
Simulation.SendInput.SimulateAction(GIActions.SprintMouse, KeyType.KeyUp);
}
else
{
Simulation.SendInput.Mouse.RightButtonDown();
Simulation.SendInput.SimulateAction(GIActions.SprintMouse, KeyType.KeyDown);
}
fastMode = !fastMode;
@ -777,7 +796,7 @@ public class PathExecutor
if (Math.Abs((fastModeColdTime - now).TotalMilliseconds) > 2500) //冷却时间2.5s,回复体力用
{
fastModeColdTime = now;
Simulation.SendInput.Mouse.RightButtonClick();
Simulation.SendInput.SimulateAction(GIActions.SprintMouse);
}
}
}
@ -787,7 +806,7 @@ public class PathExecutor
{
if ((DateTime.UtcNow - _useGadgetLastUseTime).TotalMilliseconds > PartyConfig.UseGadgetIntervalMs)
{
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_Z);
Simulation.SendInput.SimulateAction(GIActions.QuickUseGadget);
_useGadgetLastUseTime = DateTime.UtcNow;
}
}
@ -796,7 +815,7 @@ public class PathExecutor
}
// 抬起w键
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
private async Task UseElementalSkill()
@ -819,29 +838,29 @@ public class PathExecutor
// 钟离往身后放柱子
if (avatar.Name == "钟离")
{
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
await Delay(50, ct);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_S);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward);
await Delay(200, ct);
}
if (PartyConfig.GuardianElementalSkillLongPress)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyDown);
await Task.Delay(800); // 不能取消
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyUp);
await Delay(700, ct);
}
else
{
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill);
await Delay(300, ct);
}
// 钟离往身后放柱子 后继续走路
if (avatar.Name == "钟离")
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
}
}
@ -855,7 +874,7 @@ public class PathExecutor
{
//下落攻击接近目的地
Logger.LogInformation("动作:下落攻击");
Simulation.SendInput.Mouse.LeftButtonClick();
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
await Delay(1000, ct);
}
@ -884,11 +903,14 @@ public class PathExecutor
targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 2);
// 小碎步接近
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W).Sleep(60).KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Thread.Sleep(60);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
// Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W).Sleep(60).KeyUp(User32.VK.VK_W);
await Delay(20, ct);
}
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
// 到达目的地后停顿一秒
await Delay(1000, ct);

View File

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using BetterGenshinImpact.Core.Simulator.Extensions;
namespace BetterGenshinImpact.GameTask.AutoPathing;
@ -40,7 +41,7 @@ public class TrapEscaper(CancellationToken ct)
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 5);
// 按下w一直走
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
while (!ct.IsCancellationRequested)
{
var now = DateTime.UtcNow;
@ -70,7 +71,7 @@ public class TrapEscaper(CancellationToken ct)
//执行旋转
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 5);
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
//
//这里是随机角度的归零逻辑在脱困执行一秒后将randomAngle设为0以将实际角度重置为正面向点位的角度
//其实就是在一段时间内进行角度的修改以实现自动避障
@ -91,12 +92,12 @@ public class TrapEscaper(CancellationToken ct)
waypoint.MoveMode != MoveModeEnum.Fly.Code)
if (Bv.GetMotionStatus(screen) == MotionStatus.Climb)
{
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.Drop);
Sleep(75);
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_S);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown);
Sleep(700);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_S);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp);
LastActionTime = DateTime.UtcNow;
@ -117,17 +118,17 @@ public class TrapEscaper(CancellationToken ct)
}
// 抬起w键
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
public async Task RotateAndMove()
{
IncreaseRandomAngle();
// 脱离攀爬状态
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.Drop);
await Delay(75, ct);
Simulation.SendInput.Mouse.LeftButtonClick();
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
await Delay(500, ct);
TimeSpan timeSinceLastAction = DateTime.UtcNow - LastActionTime;
@ -166,30 +167,30 @@ public class TrapEscaper(CancellationToken ct)
private void MoveBackward(int delay)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_S);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown);
Sleep(500);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
Simulation.SendInput.SimulateAction(GIActions.Jump);
Sleep(delay);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_S);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp);
}
private void MoveLeft(int delay)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_A);
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyDown);
Sleep(300);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
Simulation.SendInput.SimulateAction(GIActions.Jump);
Sleep(delay);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_A);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.Drop);
}
private void MoveRight(int delay)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_D);
Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyDown);
Sleep(300);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
Simulation.SendInput.SimulateAction(GIActions.Jump);
Sleep(delay);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_D);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.Drop);
}
}

View File

@ -112,7 +112,7 @@ public partial class AutoPickTrigger : ITaskTrigger
if (_externalConfig is { ForceInteraction: true })
{
LogPick(content, "直接拾取");
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_F);
Simulation.SendInput.Keyboard.KeyPress(AutoPickAssets.Instance.PickVk);
return;
}

View File

@ -3,6 +3,7 @@ using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoSkip.Assets;
using BetterGenshinImpact.GameTask.AutoSkip.Model;
using BetterGenshinImpact.GameTask.Common;
@ -203,7 +204,7 @@ public partial class AutoSkipTrigger : ITaskTrigger
{
if (IsUseInteractionKey)
{
_postMessageSimulator?.KeyPressBackground(User32.VK.VK_F); // 注意这里不是交互键
_postMessageSimulator? .SimulateActionBackground(GIActions.PickUpOrInteract); // 注意这里不是交互键 NOTE By Ayu0K: 这里确实是交互键
}
else
{

View File

@ -3,6 +3,7 @@ using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Script;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.AutoSkip.Model;
using BetterGenshinImpact.GameTask.Common;
@ -113,7 +114,7 @@ public class AutoTrackTask(AutoTrackParam param) : BaseIndependentTask
{
// 距离大于150米先传送到最近的传送点
// J 打开任务 切换追踪打开地图 中心点就是任务点
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_J);
Simulation.SendInput.SimulateAction(GIActions.OpenQuestMenu);
Sleep(800, _ct);
// TODO 识别是否在任务界面
// 切换追踪
@ -179,7 +180,7 @@ public class AutoTrackTask(AutoTrackParam param) : BaseIndependentTask
private void StartTrackPoint()
{
// V键直接追踪
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_V);
Simulation.SendInput.SimulateAction(GIActions.QuestNavigation);
Sleep(3000, _ct);
var ra = CaptureToRectArea();
@ -216,7 +217,7 @@ public class AutoTrackTask(AutoTrackParam param) : BaseIndependentTask
Simulation.SendInput.Mouse.MoveMouseBy(-50, 0);
if (wDown)
{
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
wDown = false;
}
Debug.WriteLine("使追踪点位于俯视角上方");
@ -242,7 +243,7 @@ public class AutoTrackTask(AutoTrackParam param) : BaseIndependentTask
{
if (!wDown)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
wDown = true;
}
}
@ -251,7 +252,7 @@ public class AutoTrackTask(AutoTrackParam param) : BaseIndependentTask
{
if (wDown)
{
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
wDown = false;
}
// 识别距离

View File

@ -1,6 +1,7 @@
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Script;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.AutoTrackPath.Model;
using BetterGenshinImpact.GameTask.Common;
@ -319,7 +320,10 @@ public class AutoTrackPathTask
var angle1 = GetCharacterOrientationAngle();
Simulation.SendInput.Mouse.MoveMouseBy(CharMovingUnit, 0);
Sleep(500);
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W).Sleep(100).KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Sleep(100);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
// Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W).Sleep(100).KeyUp(User32.VK.VK_W);
Sleep(1000);
var angle2 = GetCharacterOrientationAngle();
var angleOffset = angle2 - angle1;

View File

@ -1,4 +1,5 @@
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.Model;
using System;
using Vanara.PInvoke;
@ -15,7 +16,7 @@ public class MovementControl : Singleton<MovementControl>
if (!_wDown)
{
_wDown = true;
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
}
}
@ -24,12 +25,12 @@ public class MovementControl : Singleton<MovementControl>
if (_wDown)
{
_wDown = false;
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
}
public void SpacePress()
{
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
Simulation.SendInput.SimulateAction(GIActions.Jump);
}
}

View File

@ -19,6 +19,7 @@ using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.Common.Exceptions;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using BetterGenshinImpact.Core.Simulator.Extensions;
namespace BetterGenshinImpact.GameTask.AutoTrackPath;
@ -163,7 +164,7 @@ public class TpTask(CancellationToken ct)
await Delay(1000, ct);
ra1 = CaptureToRectArea();
}
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_M);
Simulation.SendInput.SimulateAction(GIActions.OpenMap);
await Delay(1000, ct);
for (int i = 0; i < 3; i++)
{

View File

@ -10,6 +10,7 @@ using BetterGenshinImpact.Helpers.Extensions;
using Microsoft.Extensions.Logging;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using BetterGenshinImpact.Core.Simulator.Extensions;
namespace BetterGenshinImpact.GameTask.Common.Job;
@ -25,7 +26,7 @@ public class ArtifactSalvageTask
await _returnMainUiTask.Start(ct);
// B键打开背包
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_B);
Simulation.SendInput.SimulateAction(GIActions.OpenInventory);
await Delay(1000, ct);
var openBagSuccess = await NewRetry.WaitForAction(() =>
@ -51,7 +52,7 @@ public class ArtifactSalvageTask
if (Bv.IsInMainUi(ra))
{
Debug.WriteLine("背包打开失败,再次尝试打开背包");
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_B);
Simulation.SendInput.SimulateAction(GIActions.OpenInventory);
}
return false;

View File

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Helpers.Extensions;
@ -37,7 +38,7 @@ public class ClaimBattlePassRewardsTask
await _returnMainUiTask.Start(ct);
await Delay(200, ct);
TaskContext.Instance().PostMessageSimulator.KeyPress(User32.VK.VK_F4); // F4 开纪行
TaskContext.Instance().PostMessageSimulator.SimulateAction(GIActions.OpenBattlePassScreen); // F4 开纪行
// 领取战令1
await Delay(500, ct);

View File

@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using Microsoft.Extensions.Logging;
using Vanara.PInvoke;
@ -37,7 +38,7 @@ public class ClaimEncounterPointsRewardsTask
await Delay(200, ct);
TaskContext.Instance().PostMessageSimulator.KeyPress(User32.VK.VK_F1); // F1 开书
TaskContext.Instance().PostMessageSimulator.SimulateAction(GIActions.OpenAdventurerHandbook); // F1 开书
await Delay(1000, ct);

View File

@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using Microsoft.Extensions.Logging;
using Vanara.PInvoke;
@ -37,7 +38,7 @@ public class ClaimMailRewardsTask
await Delay(200, ct);
TaskContext.Instance().PostMessageSimulator.KeyPress(User32.VK.VK_ESCAPE); // ESC
TaskContext.Instance().PostMessageSimulator.SimulateAction(GIActions.OpenPaimonMenu); // ESC
await Delay(1300, ct);

View File

@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.View.Drawable;
using Microsoft.Extensions.Logging;
@ -84,9 +85,9 @@ public class ScanPickTask
private static async Task WalkForward(CancellationToken ct)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
await Delay(1000, ct);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
private void MoveCursorTo(Rect item, ImageRegion ra)

View File

@ -1,5 +1,6 @@
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Model.Area;
@ -35,7 +36,7 @@ public class SwitchPartyTask
await Delay(200, ct);
}
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_L);
Simulation.SendInput.SimulateAction(GIActions.OpenPartySetupScreen);
await Delay(1000, ct); // 加载2s // 由于胡桃可以不等待直接进入所以这里只等待1s
}

View File

@ -1,4 +1,5 @@
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Model.Area;
@ -61,7 +62,7 @@ public class QuickSereniteaPotTask
try
{
// 打开背包
Simulation.SendInput.Keyboard.KeyPress(VK.VK_B);
Simulation.SendInput.SimulateAction(GIActions.OpenInventory);
TaskControl.CheckAndSleep(500);
WaitForBagToOpen();
@ -80,7 +81,7 @@ public class QuickSereniteaPotTask
TaskControl.CheckAndSleep(800);
// 按F进入
Simulation.SendInput.Keyboard.KeyPress(VK.VK_F);
Simulation.SendInput.SimulateAction(GIActions.PickUpOrInteract);
TaskControl.CheckAndSleep(200);
// 点击进入尘歌壶

View File

@ -0,0 +1,50 @@
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Model;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using static Vanara.PInvoke.User32;
namespace BetterGenshinImpact.Model;
public partial class KeyBindingSettingModel:ObservableObject
{
/// <summary>
/// 按键绑定值
/// </summary>
[ObservableProperty]
private KeyId _keyValue;
[ObservableProperty]
private ObservableCollection<KeyBindingSettingModel> _children = [];
public string ActionName { get; set; }
public bool IsExpanded => true;
/// <summary>
/// 界面上显示是文件夹而不是按键绑定
/// </summary>
[ObservableProperty]
private bool _isDirectory;
public string ConfigPropertyName { get; set; }
public KeyBindingSettingModel(string name)
{
IsDirectory = true;
ActionName = name;
}
public KeyBindingSettingModel(string actionName, string configPropertyName, KeyId keyValue)
{
ActionName = actionName;
ConfigPropertyName = configPropertyName;
KeyValue = keyValue;
IsDirectory = false;
}
}

View File

@ -0,0 +1,91 @@
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Model;
using Fischless.WindowsInput;
using System;
using System.Windows;
using System.Windows.Input;
using static Vanara.PInvoke.User32;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using MouseButton = System.Windows.Input.MouseButton;
using TextBox = Wpf.Ui.Controls.TextBox;
namespace BetterGenshinImpact.View.Controls.KeyBindings;
public class KeyBindingTextBox:TextBox
{
public static readonly DependencyProperty KeyBindingProperty = DependencyProperty.Register(
nameof(KeyBinding),
typeof(KeyId),
typeof(KeyBindingTextBox),
new FrameworkPropertyMetadata(
KeyId.None,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(sender, _) =>
{
var control = (KeyBindingTextBox)sender;
control.Text = control.KeyBinding.ToName();
}
)
);
/// <summary>
/// 按键绑定
/// </summary>
public KeyId KeyBinding
{
get => (KeyId)GetValue(KeyBindingProperty);
set => SetValue(KeyBindingProperty, value);
}
public KeyBindingTextBox()
{
IsReadOnly = true;
IsReadOnlyCaretVisible = false;
IsUndoEnabled = false;
if (ContextMenu is not null)
ContextMenu.Visibility = Visibility.Collapsed;
Text = KeyBinding.ToName();
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
var key = e.Key;
if (key == Key.System)
{
key = e.SystemKey;
}
// 将按键转换为KeyId
KeyBinding = KeyIdConverter.FromInputKey(key);
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
var key = e.ChangedButton;
// 首次点击(未获得焦点)时,忽略一次鼠标左键的输入
if (key is MouseButton.Left && !IsFocused)
{
return;
}
// 将鼠标按键转换为KeyId
KeyBinding = KeyIdConverter.FromMouseButton(key);
}
protected override void OnGotFocus(RoutedEventArgs e)
{
Text = "等待按键...";
base.OnGotFocus(e);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
Text = KeyBinding.ToName();
base.OnLostFocus(e);
}
}

View File

@ -141,6 +141,13 @@
<ui:SymbolIcon Symbol="Flash24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="按键绑定"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:KeyBindingsSettingsPage}" Cursor="Hand">
<ui:NavigationViewItem.Icon>
<ui:SymbolIcon Symbol="KeyboardMouse16" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<!--<ui:NavigationViewItem Content="通知"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:NotificationSettingsPage}">
@ -171,9 +178,17 @@
<ui:ImageIcon Source="pack://application:,,,/Assets/Images/logo.png" />
</ui:TitleBar.Icon>
<ui:TitleBar.Header>
<StackPanel>
<ui:Button Width="44"
Height="30"
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<ui:Button Width="46"
Height="32"
Background="Transparent"
BorderBrush="Transparent"
Command="{Binding SwitchBackdropCommand}"
CornerRadius="0"
Icon="{ui:SymbolIcon Blur24}"
ToolTip="{Binding CurrentBackdropType}" />
<ui:Button Width="46"
Height="32"
Background="Transparent"
BorderBrush="Transparent"
Command="{Binding HideCommand}"

View File

@ -71,18 +71,18 @@ public partial class MainWindow : FluentWindow, INavigationWindow
private void TryApplySystemBackdrop()
{
if (WindowBackdrop.IsSupported(WindowBackdropType.Acrylic)) // 改用Acrylic效果
{
Background = new SolidColorBrush(Color.FromArgb(100, 0, 0, 0));
WindowBackdrop.ApplyBackdrop(this, WindowBackdropType.Acrylic);
return;
}
if (WindowBackdrop.IsSupported(WindowBackdropType.Mica))
{
Background = new SolidColorBrush(Colors.Transparent);
WindowBackdrop.ApplyBackdrop(this, WindowBackdropType.Mica);
return;
}
if (WindowBackdrop.IsSupported(WindowBackdropType.Tabbed))
{
Background = new SolidColorBrush(Colors.Transparent);
WindowBackdrop.ApplyBackdrop(this, WindowBackdropType.Tabbed);
return;
}
}
}

View File

@ -0,0 +1,84 @@
<UserControl x:Class="BetterGenshinImpact.View.Pages.KeyBindingsSettingsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:controls="clr-namespace:BetterGenshinImpact.View.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:kb="clr-namespace:BetterGenshinImpact.View.Controls.KeyBindings"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="clr-namespace:BetterGenshinImpact.Model"
xmlns:pages="clr-namespace:BetterGenshinImpact.ViewModel.Pages"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
d:DataContext="{d:DesignInstance Type=pages:KeyBindingsSettingsPageViewModel}"
d:DesignHeight="850"
d:DesignWidth="600"
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
FontFamily="{StaticResource TextThemeFontFamily}"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
mc:Ignorable="d">
<Grid Margin="42,16,42,12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ui:TextBlock Grid.Row="0"
Margin="0,0,0,8"
FontTypography="BodyStrong"
Text="按键绑定设置" />
<ui:TextBlock Grid.Row="1"
Margin="0,0,0,8"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="此处的按键绑定设置对除自动拾取、钓鱼、音游、伐木外的功能有效。请手动设置拾取按键、星之归还按键、隐藏主界面按键。"
TextWrapping="Wrap" />
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,8">
<ui:Button Command="{Binding FetchFromRegistryCommand}"
Content="从注册表读取"
Icon="{ui:SymbolIcon Notepad32}" />
</StackPanel>
<ui:Border Grid.Row="3"
Background="{DynamicResource CardBackground}"
BorderBrush="{DynamicResource CardBorderBrush}"
BorderThickness="1"
CornerRadius="{DynamicResource ControlCornerRadius}">
<Grid Margin="4">
<ui:Grid ColumnDefinitions="*,135" Visibility="Hidden">
<Grid x:Name="TreeColumnStar" Grid.Column="0" />
</ui:Grid>
<ui:TreeListView BorderThickness="0" ItemsSource="{Binding KeyBindingSettingModels}">
<ui:TreeListView.Columns>
<GridViewColumnCollection>
<ui:GridViewColumn Width="{Binding ActualWidth, ElementName=TreeColumnStar}" Header="动作">
<ui:GridViewColumn.CellTemplate>
<DataTemplate>
<ui:TreeRowExpander Content="{Binding ActionName}" />
</DataTemplate>
</ui:GridViewColumn.CellTemplate>
</ui:GridViewColumn>
<ui:GridViewColumn Width="120" Header="按键绑定">
<ui:GridViewColumn.CellTemplate>
<DataTemplate>
<kb:KeyBindingTextBox Padding="16,6,16,6"
VerticalAlignment="Center"
KeyBinding="{Binding KeyValue}"
Style="{StaticResource DefaultTextBoxStyle}"
TextAlignment="Center"
Visibility="{Binding IsDirectory, Converter={StaticResource BooleanToVisibilityRevertConverter}, Mode=OneWay}" />
</DataTemplate>
</ui:GridViewColumn.CellTemplate>
</ui:GridViewColumn>
</GridViewColumnCollection>
</ui:TreeListView.Columns>
<ui:TreeListView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" />
</ui:TreeListView.ItemTemplate>
</ui:TreeListView>
</Grid>
</ui:Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,30 @@
using BetterGenshinImpact.ViewModel.Pages;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BetterGenshinImpact.View.Pages;
/// <summary>
/// KeyBindingsSettingsPage.xaml 的交互逻辑
/// </summary>
public partial class KeyBindingsSettingsPage : UserControl
{
private KeyBindingsSettingsPageViewModel ViewModel { get; }
public KeyBindingsSettingsPage(KeyBindingsSettingsPageViewModel viewModel)
{
DataContext = ViewModel = viewModel;
InitializeComponent();
}
}

View File

@ -1,37 +1,183 @@
<Window x:Class="BetterGenshinImpact.View.PickerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="选择捕获窗口"
Width="800"
Height="450"
FontFamily="{DynamicResource TextThemeFontFamily}"
mc:Ignorable="d">
<Grid Background="Black">
WindowStartupLocation="CenterScreen"
Background="#FF1E1E1E">
<Window.Resources>
<!-- ListBoxItem Fluent UI 样式 -->
<Style TargetType="ListBoxItem">
<Setter Property="Margin" Value="8,4" />
<Setter Property="Padding" Value="12" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Border"
Background="{TemplateBinding Background}"
CornerRadius="8"
BorderThickness="1"
BorderBrush="Transparent">
<Grid>
<Border x:Name="SelectionBackground"
Background="#30FFFFFF"
CornerRadius="8"
Opacity="0"/>
<ContentPresenter />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="SelectionBackground" Property="Opacity" Value="0.1" />
<Setter TargetName="Border" Property="BorderBrush" Value="#20FFFFFF" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="SelectionBackground" Property="Opacity" Value="0.2" />
<Setter TargetName="Border" Property="BorderBrush" Value="#40FFFFFF" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Margin="5,0"
Background="White"
Orientation="Horizontal">
<TextBlock FontSize="20" Text="双击选中要捕获的窗口" />
</StackPanel>
<!-- 标题区域 -->
<Border Grid.Row="0"
Background="#FF252525"
Padding="24,20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox x:Name="WindowList"
Grid.Row="1"
Padding="0,10"
BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl MouseDoubleClick="WindowsOnMouseDoubleClick">
<TextBlock FontSize="14" Text="{Binding Name}" />
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Text="选择捕获窗口"
FontSize="24"
FontWeight="SemiBold"
Foreground="#FFFFFF"/>
<TextBlock Grid.Row="1"
Text="双击选中要捕获的窗口"
Margin="0,8,0,0"
Foreground="#99FFFFFF"
FontSize="14"/>
</Grid>
</Border>
<ScrollViewer Grid.Row="1"
PanningMode="VerticalOnly"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox x:Name="WindowList"
Background="Transparent"
BorderThickness="0"
MouseDoubleClick="WindowsOnMouseDoubleClick"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
ScrollViewer.CanContentScroll="False">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="4"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Border"
Background="{TemplateBinding Background}"
CornerRadius="8"
BorderThickness="1"
BorderBrush="Transparent">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="#22FFFFFF"
Duration="0:0:0.2">
<ColorAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseOut"/>
</ColorAnimation.EasingFunction>
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="#00000000"
Duration="0:0:0.2">
<ColorAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseOut"/>
</ColorAnimation.EasingFunction>
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="#33FFFFFF"/>
<Setter TargetName="Border" Property="BorderBrush" Value="#44FFFFFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="12,8" Height="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 内容层 -->
<Border Grid.Column="0"
Width="40"
Height="40"
Background="#22FFFFFF"
CornerRadius="4"
Margin="0,0,12,0">
<Image Width="24"
Height="24"
Source="{Binding Icon}"
RenderOptions.BitmapScalingMode="HighQuality"/>
</Border>
<StackPanel Grid.Column="1"
VerticalAlignment="Center">
<TextBlock Text="{Binding Name}"
FontSize="14"
FontWeight="SemiBold"
Foreground="#FFFFFF"/>
<TextBlock Text="{Binding ProcessName}"
FontSize="12"
Foreground="#99FFFFFF"
Margin="0,4,0,0"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
</Window>
</Window>

View File

@ -7,71 +7,160 @@ using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using Vanara.PInvoke;
using System.Windows.Media;
using System.Collections.Generic;
using System.Windows.Media.Imaging;
using System.Runtime.InteropServices;
namespace BetterGenshinImpact.View;
public partial class PickerWindow : Window
{
private static readonly string[] _ignoreProcesses = ["applicationframehost", "shellexperiencehost", "systemsettings", "winstore.app", "searchui"];
private bool _isSelected = false;
public PickerWindow()
{
InitializeComponent();
this.InitializeDpiAwareness();
Loaded += OnLoaded;
}
public class CapturableWindow
{
public string Name { get; set; }
public string ProcessName { get; set; }
public IntPtr Handle { get; set; }
public ImageSource Icon { get; set; }
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
FindWindows();
}
public IntPtr PickCaptureTarget(IntPtr hWnd)
public bool PickCaptureTarget(IntPtr hWnd,out IntPtr PickedWindow)
{
new WindowInteropHelper(this).Owner = hWnd;
ShowDialog();
return ((CapturableWindow?)WindowList.SelectedItem)?.Handle ?? IntPtr.Zero;
if(!_isSelected)
{
PickedWindow = IntPtr.Zero;
return false;
}
PickedWindow = ((CapturableWindow?)WindowList.SelectedItem)?.Handle ?? IntPtr.Zero;
return true;
}
private unsafe void FindWindows()
{
var wih = new WindowInteropHelper(this);
var windows = new List<CapturableWindow>();
User32.EnumWindows((hWnd, lParam) =>
{
// ignore invisible windows
if (!User32.IsWindowVisible(hWnd))
if (!User32.IsWindowVisible(hWnd) || wih.Handle == hWnd)
return true;
// ignore untitled windows
var title = new StringBuilder(1024);
_ = User32.GetWindowText(hWnd, title, title.Capacity);
if (string.IsNullOrWhiteSpace(title.ToString()))
return true;
// ignore me
if (wih.Handle == hWnd)
return true;
_ = User32.GetWindowThreadProcessId(hWnd, out var processId);
// ignore by process name
var process = Process.GetProcessById((int)processId);
if (_ignoreProcesses.Contains(process.ProcessName.ToLower()))
return true;
WindowList.Items.Add(new CapturableWindow
// 获取窗口图标
var icon = GetWindowIcon((IntPtr)hWnd);
windows.Add(new CapturableWindow
{
Handle = (IntPtr)hWnd,
Name = $"{title} ({process.ProcessName}.exe)"
Name = title.ToString(),
ProcessName = process.ProcessName,
Icon = icon
});
return true;
}, IntPtr.Zero);
WindowList.ItemsSource = windows;
}
private ImageSource GetWindowIcon(IntPtr hWnd)
{
try
{
const int ICON_BIG = 1; // WM_GETICON large icon constant
const int ICON_SMALL = 0; // WM_GETICON small icon constant
const int GCL_HICON = -14; // GetClassLong index for icon
// 尝试获取窗口大图标
var iconHandle = User32.SendMessage(hWnd, User32.WindowMessage.WM_GETICON, (IntPtr)ICON_BIG, IntPtr.Zero);
if (iconHandle == IntPtr.Zero)
{
// 尝试获取窗口小图标
iconHandle = User32.SendMessage(hWnd, User32.WindowMessage.WM_GETICON, (IntPtr)ICON_SMALL, IntPtr.Zero);
}
if (iconHandle == IntPtr.Zero)
{
// 尝试获取窗口类图标
iconHandle = User32.GetClassLong(hWnd, GCL_HICON);
}
if (iconHandle != IntPtr.Zero)
{
return Imaging.CreateBitmapSourceFromHIcon(
iconHandle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
}
catch (Exception ex)
{
Debug.WriteLine($"获取窗口图标失败: {ex.Message}");
}
// 如果获取失败返回一个默认图标或null
return null;
}
private bool IsGenshinWindow(string windowName)
{
// 判断是否包含原神相关的进程名 TODO更加健壮的判断
return windowName == "原神";
}
private bool AskIsThisGenshinImpact(string windowName)
{
var res = MessageBox.Question(
$"""
{windowName}
""",
"确认选择",
MessageBoxButton.YesNo,
MessageBoxResult.No
);
return res == MessageBoxResult.Yes;
}
private void WindowsOnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var selectedWindow = WindowList.SelectedItem as CapturableWindow;
if (selectedWindow == null) return;
// 如果不是原神窗口,询问用户是否确认
if (!IsGenshinWindow(selectedWindow.Name))
{
if (!AskIsThisGenshinImpact(selectedWindow.Name))
{
return;
}
}
_isSelected = true;
Close();
}
}

View File

@ -5,6 +5,7 @@ using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Model;
using BetterGenshinImpact.Service.Interface;
using BetterGenshinImpact.View;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Fischless.GameCapture.BitBlt;
@ -18,7 +19,9 @@ using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using Wpf.Ui;
using Wpf.Ui.Controls;
namespace BetterGenshinImpact.ViewModel;
@ -34,6 +37,9 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
[ObservableProperty]
public WindowState _windowState = WindowState.Normal;
[ObservableProperty]
public WindowBackdropType _currentBackdropType = WindowBackdropType.Auto;
public AllConfig Config { get; set; }
public MainWindowViewModel(INavigationService navigationService, IConfigService configService)
@ -54,6 +60,24 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
{
IsVisible = false;
}
[RelayCommand]
private void OnSwitchBackdrop()
{
CurrentBackdropType = CurrentBackdropType switch
{
WindowBackdropType.Auto => WindowBackdropType.Mica,
WindowBackdropType.Mica => WindowBackdropType.Acrylic,
WindowBackdropType.Acrylic => WindowBackdropType.Auto,
_ => WindowBackdropType.Auto
};
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
mainWindow.Background = new SolidColorBrush(Color.FromArgb(100, 0, 0, 0));
WindowBackdrop.ApplyBackdrop(mainWindow, CurrentBackdropType);
}
}
[RelayCommand]
private void OnClosing(CancelEventArgs e)

View File

@ -139,29 +139,42 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware, IVi
private void OnStartCaptureTest()
{
var picker = new PickerWindow();
var hWnd = picker.PickCaptureTarget(new WindowInteropHelper(UIDispatcherHelper.MainWindow).Handle);
if (hWnd != IntPtr.Zero)
if (picker.PickCaptureTarget(new WindowInteropHelper(UIDispatcherHelper.MainWindow).Handle, out var hWnd))
{
var captureWindow = new CaptureTestWindow();
captureWindow.StartCapture(hWnd, Config.CaptureMode.ToCaptureMode());
captureWindow.Show();
if (hWnd != IntPtr.Zero)
{
var captureWindow = new CaptureTestWindow();
captureWindow.StartCapture(hWnd, Config.CaptureMode.ToCaptureMode());
captureWindow.Show();
}
else
{
MessageBox.Error("选择的窗体句柄为空");
}
}
}
[RelayCommand]
private void OnManualPickWindow()
{
var picker = new PickerWindow();
var hWnd = picker.PickCaptureTarget(new WindowInteropHelper(UIDispatcherHelper.MainWindow).Handle);
if (hWnd != IntPtr.Zero)
if(picker.PickCaptureTarget(new WindowInteropHelper(UIDispatcherHelper.MainWindow).Handle,out var hWnd))
{
_hWnd = hWnd;
Start(hWnd);
}
else
{
MessageBox.Error("选择的窗体句柄为空!");
if (hWnd != IntPtr.Zero)
{
_hWnd = hWnd;
Start(hWnd);
}
else
{
MessageBox.Error("选择的窗体句柄为空!");
}
}
}
[RelayCommand]
@ -181,8 +194,13 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware, IVi
var hWnd = SystemControl.FindGenshinImpactHandle();
if (hWnd == IntPtr.Zero)
{
if (Config.GenshinStartConfig.LinkedStartEnabled && !string.IsNullOrEmpty(Config.GenshinStartConfig.InstallPath))
if (Config.GenshinStartConfig.LinkedStartEnabled)
{
if (string.IsNullOrEmpty(Config.GenshinStartConfig.InstallPath))
{
MessageBox.Error("没有找到原神的安装路径");
return;
}
hWnd = await SystemControl.StartFromLocalAsync(Config.GenshinStartConfig.InstallPath);
if (hWnd != IntPtr.Zero)
{

View File

@ -0,0 +1,558 @@
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Model;
using BetterGenshinImpact.Service.Interface;
using BetterGenshinImpact.ViewModel;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;
using Wpf.Ui.Controls;
using BetterGenshinImpact.Genshin.Settings;
using CommunityToolkit.Mvvm.Input;
using static Vanara.PInvoke.User32;
namespace BetterGenshinImpact.ViewModel.Pages;
public partial class KeyBindingsSettingsPageViewModel : ObservableObject, INavigationAware, IViewModel
{
private readonly ILogger<KeyBindingsSettingsPageViewModel> _logger;
/// <summary>
/// 配置文件
/// </summary>
private readonly KeyBindingsConfig _config;
[ObservableProperty]
private ObservableCollection<KeyBindingSettingModel> _keyBindingSettingModels = [];
public void OnNavigatedFrom()
{
}
public void OnNavigatedTo()
{
}
public KeyBindingsSettingsPageViewModel(IConfigService configService, ILogger<KeyBindingsSettingsPageViewModel> logger)
{
_logger = logger;
// 获取本模块的配置文件
_config = configService.Get().KeyBindingsConfig;
BuildKeyBindingsList();
var list = GetAllNonDirectoryKeyBinding(KeyBindingSettingModels);
foreach (var keyConfig in list)
{
keyConfig.PropertyChanged += (sender, e) =>
{
if (sender is KeyBindingSettingModel model)
{
// 使用反射更新配置文件
if (e.PropertyName == nameof(model.KeyValue))
{
Debug.WriteLine($"按键绑定 \"{model.ActionName}\" 变更为 {model.KeyValue}");
var pi = _config.GetType().GetProperty(model.ConfigPropertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (pi != null && pi.CanWrite)
{
pi.SetValue(_config, model.KeyValue, null);
}
}
}
};
}
}
public static List<KeyBindingSettingModel> GetAllNonDirectoryKeyBinding(IEnumerable<KeyBindingSettingModel> modelList)
{
var list = new List<KeyBindingSettingModel>();
foreach (var keyBindingSettingModel in modelList)
{
if (!keyBindingSettingModel.IsDirectory)
{
list.Add(keyBindingSettingModel);
}
list.AddRange(GetAllNonDirectoryChildren(keyBindingSettingModel));
}
return list;
}
public static List<KeyBindingSettingModel> GetAllNonDirectoryChildren(KeyBindingSettingModel model)
{
var result = new List<KeyBindingSettingModel>();
if (model.Children.Count == 0)
{
return result;
}
foreach (var child in model.Children)
{
if (!child.IsDirectory)
{
result.Add(child);
}
// 递归调用以获取子节点中的非目录对象
result.AddRange(GetAllNonDirectoryChildren(child));
}
return result;
}
/// <summary>
/// 构造按键绑定列表
/// </summary>
private void BuildKeyBindingsList()
{
// 动作按键
var actionDirectory = new KeyBindingSettingModel("动作");
// 菜单按键
var menuDirectory = new KeyBindingSettingModel("菜单");
// 添加动作按键的二级菜单
actionDirectory.Children.Add(new KeyBindingSettingModel(
"向前移动",
nameof(_config.MoveForward),
_config.MoveForward
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"向后移动",
nameof(_config.MoveBackward),
_config.MoveBackward
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"向左移动",
nameof(_config.MoveLeft),
_config.MoveLeft
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"向右移动",
nameof(_config.MoveRight),
_config.MoveRight
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"切换走/跑;特定操作模式下向下移动",
nameof(_config.SwitchToWalkOrRun),
_config.SwitchToWalkOrRun
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"普通攻击",
nameof(_config.NormalAttack),
_config.NormalAttack
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"元素战技",
nameof(_config.ElementalSkill),
_config.ElementalSkill
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"元素爆发",
nameof(_config.ElementalBurst),
_config.ElementalBurst
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"冲刺(键盘)",
nameof(_config.SprintKeyboard),
_config.SprintKeyboard
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"冲刺(鼠标)",
nameof(_config.SprintMouse),
_config.SprintMouse
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"切换瞄准模式",
nameof(_config.SwitchAimingMode),
_config.SwitchAimingMode
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"跳跃;特定操作模式下向上移动",
nameof(_config.Jump),
_config.Jump
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"落下",
nameof(_config.Drop),
_config.Drop
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"拾取/交互自动拾取由AutoPick模块管理",
nameof(_config.PickUpOrInteract),
_config.PickUpOrInteract
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"快捷使用小道具",
nameof(_config.QuickUseGadget),
_config.QuickUseGadget
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"特定玩法内交互操作",
nameof(_config.InteractionInSomeMode),
_config.InteractionInSomeMode
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"开启任务追踪",
nameof(_config.QuestNavigation),
_config.QuestNavigation
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"中断挑战",
nameof(_config.AbandonChallenge),
_config.AbandonChallenge
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"切换小队角色1",
nameof(_config.SwitchMember1),
_config.SwitchMember1
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"切换小队角色2",
nameof(_config.SwitchMember2),
_config.SwitchMember2
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"切换小队角色3",
nameof(_config.SwitchMember3),
_config.SwitchMember3
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"切换小队角色4",
nameof(_config.SwitchMember4),
_config.SwitchMember4
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"切换小队角色5",
nameof(_config.SwitchMember5),
_config.SwitchMember5
));
actionDirectory.Children.Add(new KeyBindingSettingModel(
"呼出快捷轮盘",
nameof(_config.ShortcutWheel),
_config.ShortcutWheel
));
// 添加菜单按键的二级菜单
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开背包",
nameof(_config.OpenInventory),
_config.OpenInventory
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开角色界面",
nameof(_config.OpenCharacterScreen),
_config.OpenCharacterScreen
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开地图",
nameof(_config.OpenMap),
_config.OpenMap
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开派蒙界面",
nameof(_config.OpenPaimonMenu),
_config.OpenPaimonMenu
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开冒险之证界面",
nameof(_config.OpenAdventurerHandbook),
_config.OpenAdventurerHandbook
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开多人游戏界面",
nameof(_config.OpenCoOpScreen),
_config.OpenCoOpScreen
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开祈愿界面",
nameof(_config.OpenWishScreen),
_config.OpenWishScreen
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开纪行界面",
nameof(_config.OpenBattlePassScreen),
_config.OpenBattlePassScreen
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开活动面板",
nameof(_config.OpenTheEventsMenu),
_config.OpenTheEventsMenu
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开玩法系统界面(尘歌壶内猫尾酒馆内)",
nameof(_config.OpenTheSettingsMenu),
_config.OpenTheSettingsMenu
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开摆设界面(尘歌壶内)",
nameof(_config.OpenTheFurnishingScreen),
_config.OpenTheFurnishingScreen
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开星之归还(条件符合期间生效)",
nameof(_config.OpenStellarReunion),
_config.OpenStellarReunion
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"开关任务菜单",
nameof(_config.OpenQuestMenu),
_config.OpenQuestMenu
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开通知详情",
nameof(_config.OpenNotificationDetails),
_config.OpenNotificationDetails
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开聊天界面",
nameof(_config.OpenChatScreen),
_config.OpenChatScreen
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开特殊环境说明",
nameof(_config.OpenSpecialEnvironmentInformation),
_config.OpenSpecialEnvironmentInformation
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"查看教程详情",
nameof(_config.CheckTutorialDetails),
_config.CheckTutorialDetails
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"长按打开元素视野",
nameof(_config.ElementalSight),
_config.ElementalSight
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"呼出鼠标",
nameof(_config.ShowCursor),
_config.ShowCursor
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开队伍配置界面",
nameof(_config.OpenPartySetupScreen),
_config.OpenPartySetupScreen
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"打开好友界面",
nameof(_config.OpenFriendsScreen),
_config.OpenFriendsScreen
));
menuDirectory.Children.Add(new KeyBindingSettingModel(
"隐藏主界面",
nameof(_config.HideUI),
_config.HideUI
));
KeyBindingSettingModels.Add(actionDirectory);
KeyBindingSettingModels.Add(menuDirectory);
}
/// <summary>
/// 从注册表中读取按键
/// </summary>
[RelayCommand]
private void FetchFromRegistry()
{
// 读取注册表
SettingsContainer settings = new();
settings.FromReg();
var keySetting = settings.OverrideController?.KeyboardMap?.ActionElementMap;
if (keySetting != null)
{
foreach (var item in keySetting)
{
if (item.ElementIdentifierId is ElementIdentifierId keyId)
{
// 跳过无法识别的按键
if (keyId.ToName() == "Unknown" || keyId.ToName() == "None")
{
continue;
}
var key = KeyIdConverter.FromVK(keyId.ToVK());
switch (item.ActionId)
{
case ActionId.MoveForwardShow:
_config.MoveForward = key;
break;
case ActionId.MoveBackShow:
_config.MoveBackward = key;
break;
case ActionId.MoveLeftShow:
_config.MoveLeft = key;
break;
case ActionId.MoveRightShow:
_config.MoveRight = key;
break;
case ActionId.WalkRun:
_config.SwitchToWalkOrRun = key;
break;
case ActionId.Attack:
_config.NormalAttack = key;
break;
case ActionId.ElementalSkill:
_config.ElementalSkill = key;
break;
case ActionId.ElementalBurst:
_config.ElementalBurst = key;
break;
case ActionId.Sprint:
if (key >= KeyId.MouseLeftButton && key <= KeyId.MouseSideButton2)
{
_config.SprintMouse = key;
}
else
{
_config.SprintKeyboard = key;
}
break;
case ActionId.AimMode:
_config.SwitchAimingMode = key;
break;
case ActionId.Jump:
_config.Jump = key;
break;
case ActionId.CancelClimb:
_config.Drop = key;
break;
// TODO: 拾取&交互可能不正确
case ActionId.UseTalk:
_config.PickUpOrInteract = key;
break;
case ActionId.Gadget:
_config.QuickUseGadget = key;
break;
case ActionId.InteractionSomeModes:
_config.InteractionInSomeMode = key;
break;
case ActionId.Navigation:
_config.QuestNavigation = key;
break;
case ActionId.AbandonChallenge:
_config.AbandonChallenge = key;
break;
case ActionId.Char1:
_config.SwitchMember1 = key;
break;
case ActionId.Char2:
_config.SwitchMember2 = key;
break;
case ActionId.Char3:
_config.SwitchMember3 = key;
break;
case ActionId.Char4:
_config.SwitchMember4 = key;
break;
case ActionId.Char5:
_config.SwitchMember5 = key;
break;
case ActionId.QuickWheel:
_config.ShortcutWheel = key;
break;
case ActionId.Inventory:
_config.OpenInventory = key;
break;
case ActionId.CharacterList:
_config.OpenCharacterScreen = key;
break;
case ActionId.Map:
_config.OpenMap = key;
break;
// TODO: 派蒙界面可能不正确
case ActionId.MenuGamepad:
_config.OpenPaimonMenu = key;
break;
case ActionId.AdventurerHandbook:
_config.OpenAdventurerHandbook = key;
break;
case ActionId.CoOp:
_config.OpenCoOpScreen = key;
break;
case ActionId.Wish:
_config.OpenWishScreen = key;
break;
case ActionId.BattlePass:
_config.OpenBattlePassScreen = key;
break;
case ActionId.Events:
_config.OpenTheEventsMenu = key;
break;
case ActionId.PotTasks:
_config.OpenTheSettingsMenu = key;
break;
case ActionId.PotEdit:
_config.OpenTheFurnishingScreen = key;
break;
// TODO: 星之归还未找到
case ActionId.QuestList:
_config.OpenQuestMenu = key;
break;
case ActionId.NotificationDetails:
_config.OpenNotificationDetails = key;
break;
case ActionId.Chat:
_config.OpenChatScreen = key;
break;
case ActionId.EnvironmentInfo:
_config.OpenSpecialEnvironmentInformation = key;
break;
case ActionId.Tutorial:
_config.CheckTutorialDetails = key;
break;
case ActionId.ElementalSight:
_config.ElementalSight = key;
break;
case ActionId.ShowCursor:
_config.ShowCursor = key;
break;
case ActionId.PartySetup:
_config.OpenPartySetupScreen = key;
break;
case ActionId.Friends:
_config.OpenFriendsScreen = key;
break;
// TODO: 隐藏主界面未找到
default:
break;
}
}
}
// 重新加载按键绑定列表
KeyBindingSettingModels.Clear();
BuildKeyBindingsList();
var list = GetAllNonDirectoryKeyBinding(KeyBindingSettingModels);
foreach (var keyConfig in list)
{
keyConfig.PropertyChanged += (sender, e) =>
{
if (sender is KeyBindingSettingModel model)
{
// 使用反射更新配置文件
if (e.PropertyName == nameof(model.KeyValue))
{
Debug.WriteLine($"按键绑定 \"{model.ActionName}\" 变更为 {model.KeyValue}");
var pi = _config.GetType().GetProperty(model.ConfigPropertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (pi != null && pi.CanWrite)
{
pi.SetValue(_config, model.KeyValue, null);
}
}
}
};
}
}
}
}

1
View/PickerWindow.xaml Normal file
View File

@ -0,0 +1 @@