Feat/multi lan (#1336)

* 记录一次对hutaofisher的访谈,帮助开发者理解其算法

* 本地化HelloWorld

* .csproj取消windows版本号,此处导致了IDE在新建代码文件和自动生成代码时,默认命名空间丢失的问题。已知VisualStudio和ReSharper存在这个问题。

* 优化扩展方法写法,改为从localizer扩展;Converter优化写法,避免冲突;新增两种语言,待测试ocr效果

* Revert ".csproj取消windows版本号,此处导致了IDE在新建代码文件和自动生成代码时,默认命名空间丢失的问题。已知VisualStudio和ReSharper存在这个问题。"

This reverts commit 8bd7ee74c5.

* localizer改为由构造函数传入以支持单元测试;一个英语上钩的单元测试

* 传送任务支持英语游戏界面;本地化参数挪至OtherConfig类下,但界面位置暂不挪动,待定

* 调整resx位置风格,放在直接使用字符串的类下;一条龙合成树脂及领取每日奖励支持游戏内中英双语

* 删除无用碎片文件

* 删去两个不必要的Sdcb包引用

* Paddle服务类去掉分类模型;检测和识别新增支持繁中和法语,配有单元测试;因小语种识别效果不理想,使用正则匹配替换多处识别文本相等或包含判断;钓鱼、一条龙合成树脂及领取每日奖励支持游戏内繁中和法语;

* 检查今日奖励任务的多语言化;右侧联机的P图标检测区域宽度缩减,避免英语角色名被误识别成P

* AutoDomainTask的游戏多语言化,由于我的游戏账号无法测试,仅配一些测试用例

* 修复有3个Mizuki导致异常的bug,临时用拼音代替新角色英文名,并为该数据初始化方法添加单元测试

* 瓦雷莎删去别名“牛牛”,因荒泷一斗已占用此别名;别名加载和读取优化

* 加个锁避免单元测试中多线程初始化paddle崩溃
This commit is contained in:
FishmanTheMurloc
2025-03-28 11:00:08 +08:00
committed by GitHub
parent b6a54c5272
commit 6d4f8b80e8
88 changed files with 13630 additions and 126 deletions

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -22,7 +23,9 @@ using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.Common.Map;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.QuickTeleport.Assets;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.Extensions;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using Vanara.PInvoke;
@@ -48,7 +51,7 @@ public class TpTask(CancellationToken ct)
/// 直接通过缩放比例按钮计算缩小按钮的Y坐标
/// </summary>
private readonly int _zoomOutButtonY = TaskContext.Instance().Config.TpConfig.ZoomEndY + 24; // y-coordinate for zoom-out button = _zoomEndY + 24
private const double DisplayTpPointZoomLevel = 4.4; // 传送点显示的时候的地图比例
/// <summary>
@@ -57,7 +60,7 @@ public class TpTask(CancellationToken ct)
public async Task TpToStatueOfTheSeven()
{
await CheckInBigMapUi();
// 提前调整至恰当的缩放以更快的传送
if (_tpConfig.MapZoomEnabled)
{
@@ -103,7 +106,7 @@ public class TpTask(CancellationToken ct)
await new PathExecutor(ct).MoveTo(waypointForTrack);
Simulation.SendInput.SimulateAction(GIActions.Drop);
}
await Delay((int)(_tpConfig.HpRestoreDuration * 1000), ct);
}
@@ -153,9 +156,9 @@ public class TpTask(CancellationToken ct)
}
}
// 获取最近的神像位置
return nearestGiTpPosition ?? throw new InvalidOperationException("没找到最近的七天神像");;
return nearestGiTpPosition ?? throw new InvalidOperationException("没找到最近的七天神像");
}
/// <summary>
/// 通过大地图传送到指定坐标最近的传送点,然后移动到指定坐标
/// </summary>
@@ -275,7 +278,7 @@ public class TpTask(CancellationToken ct)
Logger.LogWarning("传送等待超时,换台电脑吧");
}
/// <summary>
/// 传送点是否在大地图窗口内
/// </summary>
@@ -423,13 +426,13 @@ public class TpTask(CancellationToken ct)
++exceptionTimes;
mapCenterPoint = new Point2f(0f, 0f); // 其他恰当的初始值?
}
var (xOffset, yOffset) = (x - mapCenterPoint.X, y - mapCenterPoint.Y);
double totalMoveMouseX = _tpConfig.MapScaleFactor * Math.Abs(xOffset) / currentZoomLevel;
double totalMoveMouseY = _tpConfig.MapScaleFactor * Math.Abs(yOffset) / currentZoomLevel;
double mouseDistance = Math.Sqrt(totalMoveMouseX * totalMoveMouseX + totalMoveMouseY * totalMoveMouseY);
// 缩小地图到恰当的缩放
if (_tpConfig.MapZoomEnabled)
if (_tpConfig.MapZoomEnabled)
{
if (mouseDistance > _tpConfig.MapZoomOutDistance)
{
@@ -443,7 +446,7 @@ public class TpTask(CancellationToken ct)
currentZoomLevel = nextZoomLevel;
}
}
// 开始移动并放大地图
for (var iteration = 0; iteration < _tpConfig.MaxIterations; iteration++)
{
@@ -476,7 +479,7 @@ public class TpTask(CancellationToken ct)
int moveMouseY = (int)Math.Min(totalMoveMouseY, _tpConfig.MaxMouseMove * totalMoveMouseY / mouseDistance) * Math.Sign(yOffset);
double moveMouseLength = Math.Sqrt(moveMouseX * moveMouseX + moveMouseY * moveMouseY);
int moveSteps = Math.Max((int)moveMouseLength / 10, 3); // 每次移动的步数最小为 3避免除 0 错误
await MouseMoveMap(moveMouseX, moveMouseY, moveSteps);
try
{
@@ -526,7 +529,7 @@ public class TpTask(CancellationToken ct)
/// 调整地图缩放级别以加速移动
/// </summary>
/// <param name="zoomIn">是否放大地图</param>
[Obsolete]
[Obsolete]
private async Task AdjustMapZoomLevel(bool zoomIn)
{
if (zoomIn)
@@ -598,33 +601,37 @@ public class TpTask(CancellationToken ct)
Simulation.SendInput.Mouse.LeftButtonUp();
}
private int[] GenerateSteps(int delta, int steps) {
private int[] GenerateSteps(int delta, int steps)
{
double[] factors = new double[steps];
double sum = 0;
for (int i = 0; i < steps; i++) {
for (int i = 0; i < steps; i++)
{
factors[i] = Math.Cos(i * Math.PI / (2 * steps));
sum += factors[i];
}
int[] stepsArr = new int[steps];
int remaining = delta;
// 两阶段分配:基础值 + 余数补偿
for (int i = 0; i < steps; i++) {
for (int i = 0; i < steps; i++)
{
double ratio = factors[i] / sum;
stepsArr[i] = (int)(delta * ratio); // 基础值
remaining -= stepsArr[i];
}
int center = steps / 2;
for (int r = 0; r < Math.Abs(remaining); r++) {
for (int r = 0; r < Math.Abs(remaining); r++)
{
int target = (center + r) % steps; // 从中点开始螺旋分配
stepsArr[target] += remaining > 0 ? 1 : -1;
}
return stepsArr;
}
public Point2f GetPositionFromBigMap()
{
return GetBigMapCenterPoint();
@@ -796,14 +803,26 @@ public class TpTask(CancellationToken ct)
GameCaptureRegion.GameRegionClick((rect, scale) => (rect.Width - 160 * scale, rect.Height - 60 * scale));
await Delay(300, ct);
var ra = CaptureToRectArea();
using var ra = CaptureToRectArea();
var list = ra.FindMulti(new RecognitionObject
{
RecognitionType = RecognitionTypes.Ocr,
RegionOfInterest = new Rect(ra.Width / 2, 0, ra.Width / 2, ra.Height)
});
list.FirstOrDefault(r => r.Text.Length == minCountry.Length && !r.Text.Contains("委托") && r.Text.Contains(minCountry))?.Click();
Logger.LogInformation("切换到区域:{Country}", minCountry);
IStringLocalizer<MapLazyAssets> stringLocalizer = App.GetService<IStringLocalizer<MapLazyAssets>>() ?? throw new NullReferenceException(nameof(stringLocalizer));
CultureInfo cultureInfo = new CultureInfo(TaskContext.Instance().Config.OtherConfig.GameCultureInfoName);
string minCountryLocalized = stringLocalizer.WithCultureGet(cultureInfo, minCountry);
string commissionLocalized = stringLocalizer.WithCultureGet(cultureInfo, "委托");
Region? matchRect = list.FirstOrDefault(r => r.Text.Length == minCountryLocalized.Length && !r.Text.Contains(commissionLocalized) && r.Text.Contains(minCountryLocalized));
if (matchRect == null)
{
Logger.LogWarning("切换区域失败:{Country}", minCountry);
}
else
{
matchRect.Click();
Logger.LogInformation("切换到区域:{Country}", minCountry);
}
await Delay(500, ct);
return true;
}