using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Assets;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.Service.Notification;
using BetterGenshinImpact.View.Drawable;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace BetterGenshinImpact.GameTask.AutoGeniusInvokation.Model;
///
/// 对局
///
public class Duel
{
private readonly ILogger _logger = App.GetLogger();
public Character CurrentCharacter { get; set; } = default!;
public Character[] Characters { get; set; } = new Character[4];
///
/// 行动指令队列
///
public List ActionCommandQueue { get; set; } = [];
///
/// 当前回合数
///
public int RoundNum { get; set; } = 1;
///
/// 角色牌位置
///
public List CharacterCardRects { get; set; } = default!;
///
/// 手牌数量
///
public int CurrentCardCount { get; set; } = 0;
///
/// 骰子数量
///
public int CurrentDiceCount { get; set; } = 0;
private int _keqingECount = 0;
public async Task RunAsync(CancellationToken ct)
{
await Task.Run(() => { Run(ct); }, ct);
}
public void Run(CancellationToken ct)
{
try
{
AutoGeniusInvokationAssets.DestroyInstance();
LogScreenResolution();
NotificationHelper.SendTaskNotificationUsing(b => b.GeniusInvocation().Started().Build()); // TODO 需要移动
GeniusInvokationControl.GetInstance().Init(ct);
// 对局准备 选择初始手牌
GeniusInvokationControl.GetInstance().CommonDuelPrepare();
// 获取角色区域
try
{
CharacterCardRects = NewRetry.Do(() => GeniusInvokationControl.GetInstance().GetCharacterRects(), TimeSpan.FromSeconds(1.5), 3);
}
catch
{
// ignored
}
if (CharacterCardRects is not { Count: 3 })
{
CharacterCardRects = [];
var defaultCharacterCardRects = TaskContext.Instance().Config.AutoGeniusInvokationConfig.DefaultCharacterCardRects;
var assetScale = TaskContext.Instance().SystemInfo.AssetScale;
for (var i = 0; i < defaultCharacterCardRects.Count; i++)
{
CharacterCardRects.Add(defaultCharacterCardRects[i].Multiply(assetScale));
}
_logger.LogInformation("获取角色区域失败,使用默认区域");
}
for (var i = 1; i < 4; i++)
{
Characters[i].Area = CharacterCardRects[i - 1];
}
// 出战角色
CurrentCharacter = ActionCommandQueue[0].Character;
CurrentCharacter.ChooseFirst();
// 开始执行回合
while (true)
{
_logger.LogInformation("--------------第{RoundNum}回合--------------", RoundNum);
ClearCharacterStatus(); // 清空单回合的异常状态
if (RoundNum == 1)
{
CurrentCardCount = 5;
}
else
{
CurrentCardCount += 2;
}
CurrentDiceCount = 8;
// 预计算本回合内的所有可能的元素 // 并调整识别骰子素材的顺序
var elementSet = PredictionDiceType();
// 0 投骰子
GeniusInvokationControl.GetInstance().ReRollDice([.. elementSet]);
// 等待到我的回合 // 投骰子动画时间是不确定的 // 可能是对方先手
GeniusInvokationControl.GetInstance().WaitForMyTurn(this, 1000);
// 开始执行行动
while (true)
{
// 没骰子了就结束行动
_logger.LogInformation("行动开始,当前骰子数[{CurrentDiceCount}],当前手牌数[{CurrentCardCount}]", CurrentDiceCount, CurrentCardCount);
if (CurrentDiceCount <= 0)
{
_logger.LogInformation("骰子已经用完");
GeniusInvokationControl.GetInstance().Sleep(2000);
break;
}
// 每次行动前都要检查当前角色
CurrentCharacter = GeniusInvokationControl.GetInstance().WhichCharacterActiveWithRetry(this);
// 行动前重新确认骰子数量
var diceCountFromOcr = GeniusInvokationControl.GetInstance().GetDiceCountByOcr();
if (diceCountFromOcr != -10)
{
var diceDiff = Math.Abs(CurrentDiceCount - diceCountFromOcr);
if (diceDiff is > 0 and <= 4)
{
_logger.LogInformation("可能存在场地牌影响了骰子数[{CurrentDiceCount}] -> [{DiceCountFromOcr}]", CurrentDiceCount, diceCountFromOcr);
CurrentDiceCount = diceCountFromOcr;
}
else if (diceDiff > 4)
{
_logger.LogWarning(" OCR识别到的骰子数[{DiceCountFromOcr}]和计算得出的骰子数[{CurrentDiceCount}]差距较大,舍弃结果", diceCountFromOcr, CurrentDiceCount);
}
}
var alreadyExecutedActionIndex = new List();
var alreadyExecutedActionCommand = new List();
var i = 0;
for (i = 0; i < ActionCommandQueue.Count; i++)
{
var actionCommand = ActionCommandQueue[i];
// 指令中的角色未被打败、角色有异常状态 跳过指令
if (actionCommand.Character.IsDefeated || actionCommand.Character.StatusList?.Count > 0)
{
continue;
}
// 当前出战角色身上存在异常状态的情况下不执行本角色的指令
if (CurrentCharacter.StatusList?.Count > 0 &&
actionCommand.Character.Index == CurrentCharacter.Index)
{
continue;
}
// 1. 判断切人
if (CurrentCharacter.Index != actionCommand.Character.Index)
{
if (CurrentDiceCount >= 1)
{
actionCommand.SwitchLater();
CurrentDiceCount--;
alreadyExecutedActionIndex.Add(-actionCommand.Character.Index); // 标记为已执行
var switchAction = new ActionCommand
{
Character = CurrentCharacter,
Action = ActionEnum.SwitchLater,
TargetIndex = actionCommand.Character.Index
};
alreadyExecutedActionCommand.Add(switchAction);
_logger.LogInformation("→指令执行完成:{Action}", switchAction);
break;
}
else
{
_logger.LogInformation("骰子不足以进行下一步:切换角色 {CharacterIndex}", actionCommand.Character.Index);
break;
}
}
// 2. 判断使用技能
if (actionCommand.GetAllDiceUseCount() > CurrentDiceCount)
{
_logger.LogInformation("骰子不足以进行下一步:{Action}", actionCommand);
break;
}
else
{
bool useSkillRes = actionCommand.UseSkill(this);
if (useSkillRes)
{
CurrentDiceCount -= actionCommand.GetAllDiceUseCount();
alreadyExecutedActionIndex.Add(i);
alreadyExecutedActionCommand.Add(actionCommand);
_logger.LogInformation("→指令执行完成:{Action}", actionCommand);
// 刻晴的E加手牌
if (actionCommand.Character.Name == "刻晴" && actionCommand.TargetIndex == 2)
{
_keqingECount++;
if (_keqingECount % 2 == 0)
{
CurrentCardCount -= 1;
}
else
{
CurrentCardCount += 1;
}
}
}
else
{
_logger.LogWarning("→指令执行失败(可能是手牌不够):{Action}", actionCommand);
GeniusInvokationControl.GetInstance().Sleep(1000);
GeniusInvokationControl.GetInstance().ClickGameWindowCenter();
}
break;
}
}
if (alreadyExecutedActionIndex.Count != 0)
{
foreach (var index in alreadyExecutedActionIndex)
{
if (index >= 0)
{
ActionCommandQueue.RemoveAt(index);
}
}
alreadyExecutedActionIndex.Clear();
// 等待对方行动完成 (开大的时候等待时间久一点)
var sleepTime = ComputeWaitForMyTurnTime(alreadyExecutedActionCommand);
GeniusInvokationControl.GetInstance().WaitForMyTurn(this, sleepTime);
alreadyExecutedActionCommand.Clear();
}
else
{
// 如果没有任何指令可以执行 则跳出循环
// TODO 也有可能是角色死亡/所有角色被冻结导致没有指令可以执行
//if (i >= ActionCommandQueue.Count)
//{
// throw new DuelEndException("策略中所有指令已经执行完毕,结束自动打牌");
//}
GeniusInvokationControl.GetInstance().Sleep(2000);
break;
}
if (ActionCommandQueue.Count == 0)
{
throw new NormalEndException("策略中所有指令已经执行完毕,结束自动打牌");
}
}
// 回合结束
GeniusInvokationControl.GetInstance().Sleep(1000);
_logger.LogInformation("我方点击回合结束");
GeniusInvokationControl.GetInstance().RoundEnd();
// 等待对方行动+回合结算
GeniusInvokationControl.GetInstance().WaitOpponentAction(this);
VisionContext.Instance().DrawContent.ClearAll();
RoundNum++;
}
}
catch (TaskCanceledException ex)
{
NotificationHelper.SendTaskNotificationUsing(b => b.GeniusInvocation().Cancelled().Build());
throw;
}
catch (NormalEndException ex)
{
_logger.LogInformation("对局结束");
NotificationHelper.SendTaskNotificationUsing(b => b.GeniusInvocation().Success().Build());
throw;
}
catch (System.Exception ex)
{
if (TaskContext.Instance().Config.DetailedErrorLogs)
{
_logger.LogError(ex.StackTrace);
}
NotificationHelper.SendTaskNotificationUsing(b => b.GeniusInvocation().Failure().Build());
throw;
}
}
private HashSet PredictionDiceType()
{
var actionUseDiceSum = 0;
var elementSet = new HashSet
{
ElementalType.Omni
};
for (var i = 0; i < ActionCommandQueue.Count; i++)
{
var actionCommand = ActionCommandQueue[i];
// 角色未被打败的情况下才能执行
if (actionCommand.Character.IsDefeated)
{
continue;
}
// 通过骰子数量判断是否可以执行
// 1. 判断切人
if (i > 0 && actionCommand.Character.Index != ActionCommandQueue[i - 1].Character.Index)
{
actionUseDiceSum++;
if (actionUseDiceSum > CurrentDiceCount)
{
break;
}
else
{
// elementSet.Add(actionCommand.GetDiceUseElementType());
//executeActionIndex.Add(-actionCommand.Character.Index);
}
}
// 2. 判断使用技能
actionUseDiceSum += actionCommand.GetAllDiceUseCount();
if (actionUseDiceSum > CurrentDiceCount)
{
break;
}
else
{
elementSet.Add(actionCommand.GetDiceUseElementType());
//executeActionIndex.Add(i);
}
}
// 调整元素骰子识别素材顺序
GeniusInvokationControl.GetInstance().SortActionPhaseDiceMats(elementSet);
return elementSet;
}
public void ClearCharacterStatus()
{
foreach (var character in Characters)
{
character?.StatusList?.Clear();
}
}
///
/// 根据前面执行的命令计算等待时间
/// 大招等待15秒
/// 快速切换等待3秒
///
///
///
private int ComputeWaitForMyTurnTime(List alreadyExecutedActionCommand)
{
foreach (var command in alreadyExecutedActionCommand)
{
if (command.Action == ActionEnum.UseSkill && command.TargetIndex == 1)
{
return 15000;
}
// 莫娜切换等待3秒
if (command.Character.Name == "莫娜" && command.Action == ActionEnum.SwitchLater)
{
return 3000;
}
}
return 10000;
}
///
/// 获取角色切换顺序
///
///
public List GetCharacterSwitchOrder()
{
List orderList = [];
for (var i = 0; i < ActionCommandQueue.Count; i++)
{
if (!orderList.Contains(ActionCommandQueue[i].Character.Index))
{
orderList.Add(ActionCommandQueue[i].Character.Index);
}
}
return orderList;
}
/////
///// 获取角色存活数量
/////
/////
//public int GetCharacterAliveNum()
//{
// int num = 0;
// foreach (var character in Characters)
// {
// if (character != null && !character.IsDefeated)
// {
// num++;
// }
// }
// return num;
//}
private void LogScreenResolution()
{
var gameScreenSize = SystemControl.GetGameScreenRect(TaskContext.Instance().GameHandle);
if (gameScreenSize.Width != 1920 || gameScreenSize.Height != 1080)
{
_logger.LogWarning("游戏窗口分辨率不是 1920x1080 !当前分辨率为 {Width}x{Height} , 非 1920x1080 分辨率的游戏可能无法正常使用自动七圣召唤 !", gameScreenSize.Width, gameScreenSize.Height);
}
}
}