mirror of
https://github.com/babalae/better-genshin-impact
synced 2025-01-08 11:57:53 +08:00
伐木计数
This commit is contained in:
parent
9378a134bd
commit
cfe9fc6389
5
.gitignore
vendored
5
.gitignore
vendored
@ -25,4 +25,7 @@ Tmp/
|
|||||||
/packages/
|
/packages/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
|
# Rider
|
||||||
|
.idea
|
||||||
|
@ -14,8 +14,14 @@ public class AutoWoodAssets : BaseAssets<AutoWoodAssets>
|
|||||||
public RecognitionObject ConfirmRo;
|
public RecognitionObject ConfirmRo;
|
||||||
public RecognitionObject EnterGameRo;
|
public RecognitionObject EnterGameRo;
|
||||||
|
|
||||||
|
// 木头数量
|
||||||
|
public Rect WoodCountUpperRect;
|
||||||
|
|
||||||
private AutoWoodAssets()
|
private AutoWoodAssets()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
WoodCountUpperRect = new Rect((int)(100 * AssetScale), (int)(450 * AssetScale), (int)(300 * AssetScale), (int)(250 * AssetScale));
|
||||||
|
|
||||||
//「王树瑞佑」
|
//「王树瑞佑」
|
||||||
TheBoonOfTheElderTreeRo = new RecognitionObject
|
TheBoonOfTheElderTreeRo = new RecognitionObject
|
||||||
{
|
{
|
||||||
|
@ -10,11 +10,15 @@ using BetterGenshinImpact.View.Drawable;
|
|||||||
using BetterGenshinImpact.ViewModel.Pages;
|
using BetterGenshinImpact.ViewModel.Pages;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||||
using static Vanara.PInvoke.User32;
|
using static Vanara.PInvoke.User32;
|
||||||
using GC = System.GC;
|
using GC = System.GC;
|
||||||
|
using BetterGenshinImpact.Core.Recognition.OCR;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace BetterGenshinImpact.GameTask.AutoWood;
|
namespace BetterGenshinImpact.GameTask.AutoWood;
|
||||||
|
|
||||||
@ -26,6 +30,12 @@ public class AutoWoodTask
|
|||||||
private readonly AutoWoodAssets _assets;
|
private readonly AutoWoodAssets _assets;
|
||||||
|
|
||||||
private bool _first = true;
|
private bool _first = true;
|
||||||
|
|
||||||
|
private bool _shouldContinue = true;
|
||||||
|
private int _nothingCount;
|
||||||
|
private bool _firstWoodOcr = true;
|
||||||
|
private readonly ConcurrentDictionary<string, int> _woodTotalDict;
|
||||||
|
private readonly Dictionary<string, int> _enumWoodDict;
|
||||||
|
|
||||||
private readonly Login3rdParty _login3rdParty;
|
private readonly Login3rdParty _login3rdParty;
|
||||||
|
|
||||||
@ -36,6 +46,8 @@ public class AutoWoodTask
|
|||||||
_login3rdParty = new();
|
_login3rdParty = new();
|
||||||
AutoWoodAssets.DestroyInstance();
|
AutoWoodAssets.DestroyInstance();
|
||||||
_assets = AutoWoodAssets.Instance;
|
_assets = AutoWoodAssets.Instance;
|
||||||
|
_woodTotalDict = new ConcurrentDictionary<string, int>();
|
||||||
|
_enumWoodDict = new Dictionary<string, int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(WoodTaskParam taskParam)
|
public void Start(WoodTaskParam taskParam)
|
||||||
@ -77,8 +89,13 @@ public class AutoWoodTask
|
|||||||
SystemControl.ActivateWindow();
|
SystemControl.ActivateWindow();
|
||||||
for (var i = 0; i < taskParam.WoodRoundNum; i++)
|
for (var i = 0; i < taskParam.WoodRoundNum; i++)
|
||||||
{
|
{
|
||||||
|
if (_nothingCount >= 3)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("连续{Cnt}次获取木材数量为0。已达每日上限或者当前地图中没有树木", _nothingCount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
Logger.LogInformation("第{Cnt}次伐木", i + 1);
|
Logger.LogInformation("第{Cnt}次伐木", i + 1);
|
||||||
if (taskParam.Cts.IsCancellationRequested)
|
if (taskParam.Cts.IsCancellationRequested || !_shouldContinue)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -111,6 +128,176 @@ public class AutoWoodTask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RecognizeWoodCount(WoodTaskParam taskParam)
|
||||||
|
{
|
||||||
|
var firstTextFound = false;
|
||||||
|
var recognizedText = "";
|
||||||
|
var firstOcrResultList = new List<string>();
|
||||||
|
|
||||||
|
// 创建一个计时器,循环识别文本,直到超时
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
while (stopwatch.ElapsedMilliseconds < 3000) // 3秒超时
|
||||||
|
{
|
||||||
|
// OCR识别木材文本
|
||||||
|
recognizedText = PerformOcr(taskParam, firstTextFound, firstOcrResultList);
|
||||||
|
if (firstTextFound)
|
||||||
|
{
|
||||||
|
_nothingCount = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstTextFound = StartRecognizedText(recognizedText);
|
||||||
|
}
|
||||||
|
stopwatch.Stop(); // 停止计时
|
||||||
|
|
||||||
|
ProcessRecognizedText(taskParam, recognizedText, firstOcrResultList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string PerformOcr(WoodTaskParam taskParam, bool firstTextFound, List<string> firstOcrResultList)
|
||||||
|
{
|
||||||
|
if (_firstWoodOcr)
|
||||||
|
{
|
||||||
|
var firstWoodCountRect = CaptureToRectArea().DeriveCrop(_assets.WoodCountUpperRect);
|
||||||
|
var recognizedText = OcrFactory.Paddle.Ocr(firstWoodCountRect.SrcGreyMat);
|
||||||
|
firstOcrResultList.Add(recognizedText);
|
||||||
|
Sleep(500, taskParam.Cts);
|
||||||
|
return recognizedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstTextFound)
|
||||||
|
{
|
||||||
|
// 多个木材时休眠下,然后重新OCR识别所有的文本。因为多个时木材文本是依次出现的,有延迟
|
||||||
|
Sleep(200 * _woodTotalDict.Keys.Count, taskParam.Cts);
|
||||||
|
}
|
||||||
|
|
||||||
|
var woodCountRect = CaptureToRectArea().DeriveCrop(_assets.WoodCountUpperRect);
|
||||||
|
return OcrFactory.Paddle.Ocr(woodCountRect.SrcGreyMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool StartRecognizedText(string recognizedText)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrEmpty(recognizedText) &&
|
||||||
|
recognizedText.Contains("获得") &&
|
||||||
|
(recognizedText.Contains('×') || recognizedText.Contains('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessRecognizedText(WoodTaskParam taskParam, string recognizedText, List<string> firstOcrResultList)
|
||||||
|
{
|
||||||
|
if (_firstWoodOcr)
|
||||||
|
{
|
||||||
|
// 首次识别时,找到最长的OCR结果
|
||||||
|
recognizedText = FindLongestOcrResult(firstOcrResultList, recognizedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(recognizedText))
|
||||||
|
{
|
||||||
|
ParseWoodCount(taskParam, recognizedText);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_nothingCount++;
|
||||||
|
Logger.LogWarning("未能识别到伐木数量");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckWoodQuantitiesAndContinue(taskParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseWoodCount(WoodTaskParam taskParam, string text)
|
||||||
|
{
|
||||||
|
// 从识别的文本中提取木材名称和数量
|
||||||
|
// 格式示例:"获得\n竹节×30\n杉木×20"
|
||||||
|
int index = text.IndexOf('×');
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
index = text.IndexOf('X');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
// 匹配模式 "名称×数量",其中名称可能包含中文或字母,数量为数字
|
||||||
|
var matches = Regex.Matches(text, @"([^\d\n]+)[×x](\d+)");
|
||||||
|
|
||||||
|
// 如果OCR识别木材的种类小于等于最初保存的一样时,直接使用最初的木材数量。
|
||||||
|
if (!_firstWoodOcr && 1 <= matches.Count && matches.Count <= _enumWoodDict.Count)
|
||||||
|
{
|
||||||
|
foreach (var entry in _enumWoodDict.Where(entry => entry.Value <= taskParam.WoodDailyMaxCount))
|
||||||
|
{
|
||||||
|
UpdateWoodCount(entry.Key, entry.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (Match match in matches)
|
||||||
|
{
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var materialName = match.Groups[1].Value.Trim();
|
||||||
|
var quantityStr = match.Groups[2].Value.Trim();
|
||||||
|
var quantity = int.Parse(quantityStr);
|
||||||
|
Debug.WriteLine($"首次获取木材的名称:{materialName}, 数量:{quantity}");
|
||||||
|
UpdateWoodCount(materialName, quantity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogWarning("识别到的数量不是有效的整数:{woodText}", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有数据都保存一遍后,首次OCR识别结束
|
||||||
|
_firstWoodOcr = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogWarning("未能正确解析木材信息格式:{woodText}", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateWoodCount(string materialName, int quantity)
|
||||||
|
{
|
||||||
|
// 检查字典中是否已包含这种木材名称
|
||||||
|
if (!_firstWoodOcr && !_woodTotalDict.ContainsKey(materialName))
|
||||||
|
{
|
||||||
|
Logger.LogWarning("未知的木材名:{woodName},数量{Cnt}", materialName, quantity);
|
||||||
|
}
|
||||||
|
_woodTotalDict.AddOrUpdate(
|
||||||
|
key: materialName,
|
||||||
|
addValue: quantity,
|
||||||
|
updateValueFactory: (_, existingValue) => existingValue + quantity
|
||||||
|
);
|
||||||
|
if (_firstWoodOcr)
|
||||||
|
{
|
||||||
|
// 保存木材单次获取的值
|
||||||
|
_enumWoodDict.Add(materialName, quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FindLongestOcrResult(List<string> firstOcrResultList, string recognizedText)
|
||||||
|
{
|
||||||
|
foreach (var str in firstOcrResultList.Where(str => str.Length > recognizedText.Length))
|
||||||
|
{
|
||||||
|
recognizedText = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return recognizedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckWoodQuantitiesAndContinue(WoodTaskParam taskParam)
|
||||||
|
{
|
||||||
|
var allMax = true;
|
||||||
|
foreach (var entry in _woodTotalDict)
|
||||||
|
{
|
||||||
|
// 打印每个条目的键(木材名称)和值(数量)
|
||||||
|
Logger.LogInformation("木材{woodName}累积获取数量:{Cnt}", entry.Key, entry.Value);
|
||||||
|
// 检查木材是否超过每日上限
|
||||||
|
if (entry.Value < taskParam.WoodDailyMaxCount) allMax = false;
|
||||||
|
else Logger.LogInformation("木材{Name}已达到每日数量上限:{Count}", entry.Key, entry.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_shouldContinue = !_woodTotalDict.IsEmpty || !allMax;
|
||||||
|
}
|
||||||
|
|
||||||
private void Felling(WoodTaskParam taskParam, bool isLast = false)
|
private void Felling(WoodTaskParam taskParam, bool isLast = false)
|
||||||
{
|
{
|
||||||
@ -121,6 +308,9 @@ public class AutoWoodTask
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算木材数量
|
||||||
|
RecognizeWoodCount(taskParam);
|
||||||
|
|
||||||
// 2. 按下 ESC 打开菜单 并退出游戏
|
// 2. 按下 ESC 打开菜单 并退出游戏
|
||||||
PressEsc(taskParam);
|
PressEsc(taskParam);
|
||||||
|
@ -7,6 +7,7 @@ namespace BetterGenshinImpact.GameTask.AutoWood;
|
|||||||
public class WoodTaskParam : BaseTaskParam
|
public class WoodTaskParam : BaseTaskParam
|
||||||
{
|
{
|
||||||
public int WoodRoundNum { get; set; }
|
public int WoodRoundNum { get; set; }
|
||||||
|
public int WoodDailyMaxCount { get; set; }
|
||||||
|
|
||||||
public WoodTaskParam(CancellationTokenSource cts, int woodRoundNum) : base(cts)
|
public WoodTaskParam(CancellationTokenSource cts, int woodRoundNum) : base(cts)
|
||||||
{
|
{
|
||||||
@ -15,5 +16,7 @@ public class WoodTaskParam : BaseTaskParam
|
|||||||
{
|
{
|
||||||
WoodRoundNum = 9999;
|
WoodRoundNum = 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WoodDailyMaxCount = 2000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user