using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using RichCreator.Utility;
using RichCreator.Utility.Captures;
using RichCreator.Utility.CV;
using RichCreator.Utility.InputControl;
using RichCreator.Utility.Structs;
using Emgu.CV;
using Emgu.CV.Structure;
using RichCreator.Maps;
using RichCreator.Maps.Lindong;
using RichCreator.StateMachines;
using RichCreator.Utilitys;
using static RichCreator.Utilitys.AttackRectangle;
using Utils= RichCreator.Utilitys.Utils;
using ZTImage.Collections;
namespace RichCreator.Jobs.StateMachines
{
///
/// 状态动作
///
///
///
///
///
public delegate KillMonsterStateResult StateAction(Image image, Image hsvImage);
///
/// 杀怪状态机
///
public class KillMonsterStateMachine
{
private const Int32 NoMoveMaxMillSecond = 2000;//最大未移动容忍毫秒数
private StateAction[] states;//状态动作列表
private MapHouse house;//当前房间
private ZTPoint miniMapStart;//小地图区域
private ZTRectangle gameRect;//游戏区域
private Int32 preHouseIndex = 0;//上一房间编号
private MoveState moveState;//移动状态
private OutOfBounds outOfBounds;//是否在禁区
private bool exitResult;//退出结果
private ZTPoint roleLastPosition=ZTPoint.Empty;//角色最后位置
private Int32 runningStep = RunningStep.None;//是否恢复的状态
#region Find Door Info
//门坐标
private ZTPoint stateDoorPosition;
//角色位置
private ZTPoint stateRolePosition;
//离开的门朝向
private Direction stateDoorLevelDirect = Direction.None;
//定位点方框
private MultiList stateLocationRectangle = new MultiList();
//怪
private ZTPoint[] stateMonsters;
#endregion
public KillMonsterStateMachine(MapHouse house, ZTPoint miniMapStart, ZTRectangle gameRect,Int32 preHouseIndex,Int32 runningStep)
{
this.runningStep = runningStep;
this.house = house;
this.miniMapStart = miniMapStart;
this.gameRect = gameRect;
this.preHouseIndex = preHouseIndex;
moveState = new MoveState(this.gameRect, this.house);
outOfBounds = new OutOfBounds(this.gameRect,moveState);
InitStates();
}
///
/// 状态机开始
///
///
///
///
///
public Int32 Start(CancellationToken cancellationToken, Int32 timeoutMillSecond)
{
DateTime expireTime = DateTime.Now.AddMilliseconds(timeoutMillSecond);
KillMonsterStateResult result = new KillMonsterStateResult(STATE_Start, false);
if (this.runningStep > RunningStep.None)
{
result = new KillMonsterStateResult(STATE_FindMonster, true);
}
System.Drawing.Bitmap bitmap;
//原图
Image image = null;
//色彩hsv
Image hsvImage = new Image(gameRect.End.X - gameRect.Start.X + 1, gameRect.End.Y - gameRect.Start.Y + 1);
while (true)
{
byte currentState = result.NextState;
if (cancellationToken.IsCancellationRequested)
{
G.Instance.DebugWriter("取消刷图");
this.moveState.StopMove();
return JobResult.Cancel;
}
if (DateTime.Now > expireTime)
{
G.Instance.DebugWriter("刷图超时");
this.moveState.StopMove();
return JobResult.Timeout;
}
if (result.NextState == STATE_Exit)
{
G.Instance.DebugWriter("退出状态");
this.moveState.StopMove();
if (this.exitResult)
{
return JobResult.Success;
}
return JobResult.Failed;
}
bool refreshScreen = result.RefreshScreen;
if (refreshScreen)
{
using (bitmap = ScreenCapture.Instance.CaptureScreen())
{
image = new Image(bitmap);
image = image.GetSubRect(new System.Drawing.Rectangle(gameRect.Start.X, gameRect.Start.Y, gameRect.End.X - gameRect.Start.X + 1, gameRect.End.Y - gameRect.Start.Y + 1));
CvInvoke.CvtColor(image, hsvImage, Emgu.CV.CvEnum.ColorConversion.Rgb2Hsv);
}
if (this.house.Index != 15)
{
//计算是否进入其它房间
Int32 houseIndex;
if (!LindongCVHelper.GetCurrentHouseIndex(out houseIndex, image, this.miniMapStart))
{
//得不到房间
if (!FuncUtils.TimeoutCancelableWrap(30 * 1000, cancellationToken, () => {
using (bitmap = ScreenCapture.Instance.CaptureScreen())
{
image = new Image(bitmap);
image = image.GetSubRect(new System.Drawing.Rectangle(gameRect.Start.X, gameRect.Start.Y, gameRect.End.X - gameRect.Start.X + 1, gameRect.End.Y - gameRect.Start.Y + 1));
return LindongCVHelper.GetCurrentHouseIndex(out houseIndex, image, this.miniMapStart);
}
}, 50))
{
return JobResult.Failed;
}
}
if (houseIndex != this.house.Index)
{
this.moveState.StopMove();
return JobResult.Success;
}
}
}
StateAction action = states[currentState];
result =action(image, hsvImage);
if (refreshScreen)
{
image.Dispose();
}
G.Instance.DebugWriter("next state:" + result.NextState.ToString());
}
}
#region States
private const byte STATE_FindMonster = 0;//找怪
private const byte STATE_PrepareReleaseSkill = 1;//释放技能预处理,计算位置
private const byte STATE_ReleaseSkill = 2;//释放技能
private const byte STATE_DetectDoorIsOpen = 3;//查找进入下一房间的门是否开了
private const byte STATE_MoveToDoor = 4;//移动进门
private const byte STATE_Start = 8;//开始状态
private const byte STATE_Exit = 9;//完成状态
///
/// 初始化状态
///
private void InitStates()
{
//状态列表
states = new StateAction[STATE_Exit+1];
states[STATE_FindMonster] = FindMonster;
states[STATE_PrepareReleaseSkill] = PrepareReleaseSkill;
states[STATE_ReleaseSkill] = ReleaseSkill;
states[STATE_DetectDoorIsOpen] = DetectDoorIsOpen;
states[STATE_MoveToDoor] = MoveToDoor;
states[STATE_Start] = StartState;
}
private KillMonsterStateResult StartState(Image image, Image hsvImage)
{
SingleEntryHouseSkill.ReleaseSkill(this.house.Index, this.preHouseIndex,this.moveState);
return new KillMonsterStateResult(STATE_FindMonster, true);
}
///
/// 0.找怪
///
///
///
///
private KillMonsterStateResult FindMonster(Image image, Image hsvImage)
{
#region coll
//while (true)
//{
// image = ScreenCapture.Instance.CaptureScreenReturnImage();
// image = image.GetSubRect(new System.Drawing.Rectangle(gameRect.Start.X, gameRect.Start.Y, gameRect.End.X - gameRect.Start.X + 1, gameRect.End.Y - gameRect.Start.Y + 1));
// CvInvoke.CvtColor(image, hsvImage, Emgu.CV.CvEnum.ColorConversion.Rgb2Hsv);
// ZTPoint start=DnfCVHelper.FindRole(hsvImage, this.gameRect);
// Int32 dur = RandomUtils.KeyPressDuration;
// G.Instance.InputControl.PutDown(false, false, false, false, false, false, false, false,HIDCode.LeftArrow);
// Thread.Sleep(dur);
// G.Instance.InputControl.PutDown(false, false, false, false, false, false, false, false);
// Thread.Sleep(dur);
// G.Instance.InputControl.PutDown(false, false, false, false, false, false, false, false, HIDCode.LeftArrow);
// Thread.Sleep(dur);
// G.Instance.InputControl.PutDown(false, false, false, false, false, false, false, false);
// image = ScreenCapture.Instance.CaptureScreenReturnImage();
// image = image.GetSubRect(new System.Drawing.Rectangle(gameRect.Start.X, gameRect.Start.Y, gameRect.End.X - gameRect.Start.X + 1, gameRect.End.Y - gameRect.Start.Y + 1));
// CvInvoke.CvtColor(image, hsvImage, Emgu.CV.CvEnum.ColorConversion.Rgb2Hsv);
// ZTPoint end = DnfCVHelper.FindRole(hsvImage, this.gameRect);
// G.Instance.InfoWriter($"dur:{dur},start:{start},end{end},distance:{end.X - start.X}");
// Thread.Sleep(2000);
//}
#endregion
//怪
this.stateMonsters = LindongCVHelper.FindMonster(hsvImage,this.gameRect);
//主角
this.stateRolePosition = DnfCVHelper.FindRole(hsvImage, this.gameRect);
//定位点
DnfCVHelper.GetLocationPoint(out stateLocationRectangle, hsvImage, this.gameRect);
if (this.stateMonsters.Length > 0)
{
G.Instance.DebugWriter(string.Format("已找到{0}个怪", this.stateMonsters.Length));
//未找到主角
if (stateRolePosition == ZTPoint.Empty)
{
this.moveState.StopMove();
G.Instance.DebugWriter("找不到角色,Send X");
G.Instance.InputControl.PressKey(1000, HIDCode.X);
return new KillMonsterStateResult(STATE_FindMonster, true);
}
return new KillMonsterStateResult(STATE_PrepareReleaseSkill, false);
}
else
{
//未查找到主角
if (stateRolePosition == ZTPoint.Empty)
{
if (this.moveState.IsMoving && !this.moveState.IsFindRoleMoving)
{
this.moveState.StopMove();
}
//是否刷完
if (IsComplete(image))
{
return new KillMonsterStateResult(STATE_Exit, false);
}
this.moveState.FindRoleMove();
return new KillMonsterStateResult(STATE_FindMonster, true);
}
return new KillMonsterStateResult(STATE_DetectDoorIsOpen, true);
}
}
private DateTime FindRoleLastNoMoveTime = DateTime.MaxValue;//最后没移动时间
private bool FindRoleLastNoMoveStart = false;//没移动是否开始计时
///
/// 1.释放技能预处理,计算位置
///
///
///
///
private KillMonsterStateResult PrepareReleaseSkill(Image image, Image hsvImage)
{
if (this.moveState.IsMoving&&!this.moveState.IsAttackMoving)
{
this.moveState.StopMove();
}
SkillInfo skill = this.house.Skills.SyncPeek();
//计算攻击移动距离
HIDCode dir = HIDCode.RightArrow;
bool needMove = false;
ZTSize moveDistance = AttackRectangle.GetMoveDistance(this.gameRect, stateRolePosition, stateMonsters, skill, out dir, out needMove);
if (!needMove)
{
this.roleLastPosition = ZTPoint.Empty;
//不需要移动
if (dir != HIDCode.NoEvent)
{
G.Instance.InputControl.PressKey(100, dir);
}
this.moveState.StopMove();
G.Instance.DebugWriter(string.Format("不需移动直接发技能,距离:{0}", moveDistance.ToString()));
return new KillMonsterStateResult(STATE_ReleaseSkill, false);
}
//没有移动,可能有障碍物
if (stateRolePosition == this.roleLastPosition)
{
if (FindRoleLastNoMoveStart && (DateTime.Now - FindRoleLastNoMoveTime).TotalMilliseconds > NoMoveMaxMillSecond)
{
//开始计时 并且 超过最大未移动容忍时间
G.Instance.DebugWriter("find role no mvoe:" + stateRolePosition.ToString());
this.moveState.StopMove();
G.Instance.InputControl.PressKey(RandomUtils.KeyPressDuration, HIDCode.X);
FindRoleLastNoMoveTime = DateTime.Now;
}
else if (!FindRoleLastNoMoveStart)
{
//没开始计时 则 开始计时
FindRoleLastNoMoveStart = true;
FindRoleLastNoMoveTime = DateTime.Now;
}
}
else
{
//移动了 取消计时
this.roleLastPosition = stateRolePosition;
FindRoleLastNoMoveStart = false;
}
G.Instance.DebugWriter("attack move :"+moveDistance.ToString()+",role:"+stateRolePosition.ToString()+",monster1:"+stateMonsters[0].ToString()+",skill:"+skill.Key.ToString());
this.moveState.AttackMove(moveDistance);
return new KillMonsterStateResult(STATE_FindMonster, true);
}
///
/// 2.释放技能
///
///
///
///
private KillMonsterStateResult ReleaseSkill(Image image, Image hsvImage)
{
SkillInfo skill = this.house.Skills.SyncDeQueue();
if (skill.Key == HIDCode.X)
{
for (int i = 0; i < 5; i++)
{
G.Instance.InputControl.PressKey(RandomUtils.KeyPressDuration, skill.Key);
Thread.Sleep(200);
}
}
else
{
G.Instance.InputControl.PressKey(RandomUtils.KeyPressDuration, skill.Key);
Thread.Sleep((Int32)skill.ReleaseTime);
}
G.Instance.DebugWriter(string.Format("发完技能,技能按键:{0},技能名称:{1}", skill.Key, skill.SkillName));
return new KillMonsterStateResult(STATE_FindMonster, true);
}
///
/// 3.检测进入下一房间的门是否开了
///
///
private KillMonsterStateResult DetectDoorIsOpen(Image image, Image hsvImage)
{
if (this.moveState.IsAttackMoving|| this.moveState.IsFindRoleMoving)
{
this.moveState.StopMove();
}
////查找当前房间编号,看是否进入其它房间
//Int32 houseIndex;
//if (!LindongCVHelper.GetCurrentHouseIndex(out houseIndex, image, this.miniMapStart))
//{
if (IsComplete(image))
{
return new KillMonsterStateResult(STATE_Exit, false);
}
// G.Instance.DebugWriter("检测是否进入下一房间时,未发现小地图中有人物标记,id"+this.house.Index+",isend:"+this.house.IsEnd.ToString());
// return new KillMonsterStateResult(STATE_FindMonster, true);
//}
//if (houseIndex != this.house.Index)
//{
// //已进入其它房间
// this.moveState.StopMove();
// this.exitResult = true;
// return new KillMonsterStateResult(STATE_Exit, false);
//}
////拾取物品,获取最近一个物品位置并步行过去
//ZTPoint thingItemPosition = GetNearlyThingItem(image, rolePosition);
//if (thingItemPosition != ZTPoint.Empty)
//{
// if (this.moveState.IsMoving && !this.moveState.IsPickupMoving)
// {
// this.moveState.StopMove();
// }
// this.moveState.PickupMove(rolePosition, thingItemPosition);
// return new KillMonsterStateResult(STATE_FindMonster, true);
//}
//if (this.moveState.IsPickupMoving)
//{
// this.moveState.StopMove();
//}
//查找真实的门
ZTPoint doorPosition = LindongCVHelper.FindDoor(out stateDoorLevelDirect,hsvImage, this.house.DoorDirection,this.gameRect);
if (doorPosition != ZTPoint.Empty)
{
G.Instance.InfoWriter("door find:"+doorPosition.ToString());
this.moveState.StopMove();
this.stateDoorPosition = doorPosition;
//找到门,向门移动
return new KillMonsterStateResult(STATE_MoveToDoor, false);
}
else
{
//未找到门,循环移动
Int32 areaID = 0;
if (this.outOfBounds.InOutOfBound(this.house.Index, stateRolePosition, out areaID))
{
this.outOfBounds.MoveToCommonBound(areaID);
}
else
{
this.moveState.FindDoorMove(stateRolePosition);
}
}
return new KillMonsterStateResult(STATE_FindMonster, true);
}
private const Int32 XDoorLevelOffset = 200;
private const Int32 YDoorLevelOffset = 100;
///
/// 4.找门
///
///
///
///
private KillMonsterStateResult MoveToDoor(Image image, Image hsvImage)
{
Int32 limitLine = 0;
Int32 diff = 0;
//传过来人物坐标和门的坐标,根据门的朝向计算人物走向
switch (stateDoorLevelDirect)
{
case Direction.Up:
//门在上方
limitLine = stateDoorPosition.Y + YDoorLevelOffset;
if (stateRolePosition.Y < limitLine)
{
//下移
this.moveState.SyncMove(new ZTPoint(0, limitLine - stateRolePosition.Y));
}
//垂直对齐
diff = stateDoorPosition.X - stateRolePosition.X;
this.moveState.SyncMove(new ZTPoint(diff, 0));
//移动y,进入
this.moveState.SyncMove(new ZTPoint(0, this.gameRect.Start.Y - stateRolePosition.Y));
break;
case Direction.Right:
//门在右侧
limitLine = stateDoorPosition.X - XDoorLevelOffset;
if (stateRolePosition.X > limitLine)
{
//如果角色位于门右侧,先向左移
this.moveState.SyncMove(new ZTPoint(limitLine - stateRolePosition.X, 0));
}
//水平对齐
diff = stateDoorPosition.Y - stateRolePosition.Y;
this.moveState.SyncMove(new ZTPoint(0, diff));
//移动x,进入
this.moveState.SyncMove(new ZTPoint(this.gameRect.End.X - stateRolePosition.X, 0));
break;
case Direction.Bottom:
//门在下方
limitLine = stateDoorPosition.Y - YDoorLevelOffset;
if (stateRolePosition.Y > limitLine)
{
//如果角色在门下方,先向上移
this.moveState.SyncMove(new ZTPoint(0,limitLine- stateRolePosition.Y));
}
//垂直对齐
diff = stateDoorPosition.X - stateRolePosition.X;
this.moveState.SyncMove(new ZTPoint(diff, 0));
//移动y,进入
this.moveState.SyncMove(new ZTPoint(0, this.gameRect.End.Y - stateRolePosition.Y));
break;
case Direction.Left:
//门在左侧
limitLine = stateDoorPosition.X + XDoorLevelOffset;
if (stateRolePosition.X < limitLine)
{
//如果角色位于门左侧,先向右移
this.moveState.SyncMove(new ZTPoint(limitLine - stateRolePosition.X, 0));
}
//水平对齐
diff = stateDoorPosition.Y - stateRolePosition.Y;
this.moveState.SyncMove(new ZTPoint(0, diff));
//移动x,进入
this.moveState.SyncMove(new ZTPoint(this.gameRect.Start.X- stateRolePosition.X, 0));
break;
}
return new KillMonsterStateResult(STATE_DetectDoorIsOpen, true);
}
#endregion
///
/// 得到最近的物品
///
///
///
public ZTPoint GetNearlyThingItem(Image image,ZTPoint rolePosition)
{
List points=DnfCVHelper.GetThingItemPoints(image, this.gameRect);
if (points.Count <= 0)
{
return ZTPoint.Empty;
}
if (points.Count == 1)
{
return points[0];
}
double distance = 0;
ZTPoint result = ZTPoint.Empty;
for (int i = 0; i < points.Count; i++)
{
double temp = ZTImage.Utils.GetDistance(rolePosition.X, rolePosition.Y, points[i].X, points[i].Y);
if (i == 0)
{
distance = temp;
result = points[0];
continue;
}
if (temp < distance)
{
distance = temp;
result = points[i];
}
}
return result;
}
///
/// 是否完成
///
///
///
public bool IsComplete(Image image)
{
if (this.house.IsEnd)
{
if (DnfCVHelper.IsJiangli(image, this.gameRect))
{
//翻牌
this.moveState.StopMove();
Fanpai();
Thread.Sleep(5000);
this.exitResult = true;
return true;
}
if (DnfCVHelper.IsCompleteRoom(image, this.gameRect))
{
//已经刷完
this.moveState.StopMove();
this.exitResult = true;
return true;
}
}
return false;
}
#region 翻牌
///
/// 牌的位置
///
private static readonly ZTPoint[] CardList = new ZTPoint[] {
new ZTPoint(364,231),
new ZTPoint(561,231),
new ZTPoint(744,231),
new ZTPoint(924,231),
new ZTPoint(364,570),
new ZTPoint(561,570),
new ZTPoint(744,570),
new ZTPoint(924,570),
};
///
/// 翻牌
/// 1-4
/// 5-8
///
///
private void Fanpai()
{
Thread.Sleep(2000);
Int32 number = RandomUtils.G(1, 4);
ZTPoint willPosition = this.gameRect.Start.Add(CardList[number - 1]);
G.Instance.InputControl.MoveToAndClick(RandomUtils.PointRange(willPosition,10));
//判断黄金版是否可以翻
if (DnfCVHelper.HasMowangqiyueCard(this.gameRect))
{
number = RandomUtils.G(5, 8);
willPosition = this.gameRect.Start.Add(CardList[number - 1]);
G.Instance.InputControl.MoveToAndClick(RandomUtils.PointRange(willPosition, 10));
}
}
#endregion
}
}