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 } }