using Emgu.CV; using Emgu.CV.Structure; using RichCreator.Dnf; using RichCreator.Maps.Kalete; using RichCreator.StateMachines; using RichCreator.Utility; using RichCreator.Utility.Captures; using RichCreator.Utility.CV; using RichCreator.Utility.InputControl; using RichCreator.Utility.Maps; using RichCreator.Utility.Skills; using RichCreator.Utility.Structs; using RichCreator.Utility.Utilitys; using RichCreator.Utilitys; using System; using System.Collections.Generic; using System.Threading; using ZTImage.Collections; namespace RichCreator.Jobs.StateMachines { /// /// 杀怪状态机 /// public class KillMonsterStateMachine : StateMachineBase { private const Int32 NoMoveMaxMillSecond = 2000;//最大未移动容忍毫秒数 private MapInfo map;//地图 private HouseInfo house;//当前房间 private DnfRole role;//当前角色控制 private bool isSuccess;//退出结果 private KillMonsterStates currentState = KillMonsterStates.Start;//当前状态 private bool needCaptureScreen = false;//是否截图屏幕 private ZTPoint[] stateMonsters;//怪 private Image image = null;//原图 private Image hsvImage = null;//色彩hsv private ZTPoint state_DoorPosition;//门坐标 private Direction state_DoorDirect = Direction.None;//门朝向 private ParametersPoint stateScreenLocation = new ParametersPoint();//屏幕定位点 private ZTPoint skillReleasePoint = ZTPoint.Empty; private HIDCode roleDirection = HIDCode.NoEvent; private bool tryEntryDoor = false;//是否尝试进门 public KillMonsterStateMachine(MapInfo map,HouseInfo house, DnfRole role) { this.map = map; this.house = house; this.role = role; hsvImage = new Image(map.GameRect.End.X - map.GameRect.Start.X + 1, map.GameRect.End.Y - map.GameRect.Start.Y + 1); } private void SetState(KillMonsterStates current, bool capture) { currentState = current; needCaptureScreen = capture; } /// /// 状态机开始 /// /// /// /// /// public ZTResult Work(Int32 timeoutMillSecond,Int32 preHouseIndex,Int32 runningStep) { bool imageIsChange = false;//图像是否改变 Int32 nextGate = 0; DateTime expireTime = DateTime.Now.AddMilliseconds(timeoutMillSecond); while (true) { if (map.CancelToken.IsCancellationRequested) { G.Instance.DebugWriter("取消刷图"); this.role.StopMove(); return ZTResult.Cancel; } if (DateTime.Now > expireTime) { G.Instance.DebugWriter("刷图超时"); this.role.StopMove(); return ZTResult.Timeout; } if (needCaptureScreen) { using (System.Drawing.Bitmap bitmap = ScreenCapture.Instance.CaptureScreen()) { if (image != null) { image.Dispose(); } image = new Image(bitmap); image = image.GetSubRect(new System.Drawing.Rectangle(map.GameRect.Start.X, map.GameRect.Start.Y, map.GameRect.End.X - map.GameRect.Start.X + 1, map.GameRect.End.Y - map.GameRect.Start.Y + 1)); imageIsChange = true; } } switch (currentState) { case KillMonsterStates.Start: //开始 this.map.EntryHousePrework(this.house.Index, preHouseIndex); SetState(KillMonsterStates.IsLastHouse, true); break; case KillMonsterStates.IsLastHouse: //是否最后一个房间 if (!imageIsChange) { //图片没变,不用判断 SetState(KillMonsterStates.FindRole, false); break; } if (this.house.IsEnd) { SetState(KillMonsterStates.HasRewardWindow, false); } else { SetState(KillMonsterStates.IsOtherHouse, false); } break; case KillMonsterStates.HasRewardWindow: //是否有奖励界面 if (DnfCVHelper.IsJiangli(image, map.GameRect)) { G.Instance.InfoWriter("has jiangli"); SetState(KillMonsterStates.TurnAroundCard, false); } else { SetState(KillMonsterStates.IsCompletePage, false); } break; case KillMonsterStates.TurnAroundCard: //翻牌 Fanpai(); Thread.Sleep(5000); this.isSuccess = true; SetState(KillMonsterStates.Exit, false); break; case KillMonsterStates.IsCompletePage: //是否刷完界面 if (DnfCVHelper.IsCompleteRoom(image, map.GameRect)) { this.isSuccess = true; SetState(KillMonsterStates.Exit, false); } else { SetState(KillMonsterStates.FindRole, false); } break; case KillMonsterStates.IsOtherHouse: //是否进入其它房间 if (HouseIsChange()) { this.isSuccess = true; SetState(KillMonsterStates.Exit, false); } else { //是否需要截图 SetState(KillMonsterStates.FindRole, false); } break; case KillMonsterStates.FindRole: //主角 //定位点 if (this.house.WithoutNumber.Count <= 0) { this.stateScreenLocation = DnfCVHelper.GetLocationPoint(image, map.GameRect); } else { this.stateScreenLocation = DnfCVHelper.GetLocationPoint(image, map.GameRect,this.house.WithoutNumber); } if (this.stateScreenLocation.Equals(ParametersPoint.Empty)) { //找不到定位点 SetState(KillMonsterStates.IsLastHouse, true); break; } CvInvoke.CvtColor(image, hsvImage, Emgu.CV.CvEnum.ColorConversion.Rgb2Hsv); ZTPoint roleCBPosition = DnfCVHelper.FindRole(hsvImage, map.GameRect); if (roleCBPosition.Equals(ZTPoint.Empty)) { SetState(KillMonsterStates.FindRoleMove, false); } else { if (this.role.IsMoveIntent(Utility.Dnf.MoveIntent.FindRoleMove)) { this.role.StopMove(); } role.UpdatePosition(roleCBPosition); SetState(KillMonsterStates.FindMonster, false); } break; case KillMonsterStates.FindRoleMove: //todo:让主角移动(原:有怪攻击一下,无怪移动一下) FindRoleMove(); break; case KillMonsterStates.FindMonster: //找怪 this.stateMonsters = DnfCVHelper.FindMonster(hsvImage, map.GameRect); if (this.stateMonsters.Length > 0) { G.Instance.DebugWriter(string.Format("找到{0}个怪", this.stateMonsters.Length)); SetState(KillMonsterStates.CalcAttackPoint, false); } else { SetState(KillMonsterStates.PickupThing, false); } break; case KillMonsterStates.PickupThing: //拾取物品 //todo: PickupThing(); SetState(KillMonsterStates.FindDoor, false); break; case KillMonsterStates.FindDoor: //查找门 if (this.house.IsEnd) { SetState(KillMonsterStates.IsLastHouse, true); break; } FindDoor(); break; case KillMonsterStates.EntryDoor: //向门移动, 进门 EntryDoor(); break; case KillMonsterStates.InNextGatePoint: //是否在进门点 ParametersPoint nextGatePoint = ParametersPoint.Empty; ZTPoint rmhp=this.house.ScreenToMapCoordinate(this.role.HalfPosition, this.stateScreenLocation); if (this.house.IsInNextGatePoint(out nextGatePoint,rmhp)) { SetState(KillMonsterStates.ToLoopPoint, false); } else { //不在进门点 SetState(KillMonsterStates.ToNextGatePoint, false); } break; case KillMonsterStates.ToNextGatePoint: //移到进门点 ZTPoint rmp = this.house.ScreenToMapCoordinate(this.role.Position, this.stateScreenLocation); ZTPoint ngp = DnfRole.HalfToFootPosition(this.house.HousePathInfo.NextGates[nextGate].Point); this.role.MoveToMapPoint(rmp, ngp); nextGate++; nextGate = nextGate % this.house.HousePathInfo.NextGates.Count; SetState(KillMonsterStates.IsLastHouse, true); break; case KillMonsterStates.ToLoopPoint: //移动到循环点 if (!this.house.HousePathInfo.LoopPoint.Equals(ZTPoint.Empty)) { //开始移动 ZTPoint roleMapPoint = this.house.ScreenToMapCoordinate(this.role.Position, this.stateScreenLocation); this.role.MoveToMapPoint(roleMapPoint, this.house.HousePathInfo.LoopPoint); } SetState(KillMonsterStates.IsLastHouse, true); break; case KillMonsterStates.CalcAttackPoint: //判断使用何技能,计算攻击移动距离, 是否需要移动 CalcAttackDistance(); break; case KillMonsterStates.ReleaseSkill: //调整朝向, 释放技能, ReleaseSkill(); break; case KillMonsterStates.AttackMove: //todo:攻击移动 AttackMove(); break; case KillMonsterStates.Exit: //结束 G.Instance.DebugWriter("退出!~"); this.role.StopMove(); if (this.isSuccess) { return ZTResult.Success; } return ZTResult.Failed; } G.Instance.DebugWriter("next state:" + currentState.ToString()); } } #region States /// /// 让主角移动(原:有怪攻击一下,无怪移动一下) /// private void FindRoleMove() { this.role.FindRoleMove(); SetState(KillMonsterStates.FindRole, true); } /// /// 拾取物品 /// private void PickupThing() { ////拾取物品,获取最近一个物品位置并步行过去 //ZTPoint thingItemPosition = GetNearlyThingItem(image, role.Position); //if (!thingItemPosition .Equals( ZTPoint.Empty)) //{ // if (this.role.IsMoving && !this.role.IsMoveIntent(Utility.Dnf.MoveIntent.PickupMove)) // { // this.role.StopMove(); // } // this.role.PickupMove(this.role.Position, thingItemPosition); // //return new KillMonsterStateResult(STATE_FindMonster, true); //} //if (this.role.IsMoveIntent(Utility.Dnf.MoveIntent.PickupMove)) //{ // this.role.StopMove(); //} } /// /// 查找门, 是否找到门 /// private void FindDoor() { //查找真实的门 Dictionary doors=ShikongzhimenCVHelper.FindDoor(hsvImage, this.house.DoorDirection, map.GameRect); this.state_DoorDirect = Direction.None; this.state_DoorPosition = ZTPoint.Empty; if (doors.Count > 0) { foreach (var door in doors) { //门地图坐标 ZTPoint mapDoorPoint = this.house.ScreenToMapCoordinate(door.Key, this.stateScreenLocation); //要进入门的允许区域 ZTPoint nextGatePoint = this.house.GetNextGatePoint(door.Value); ZTRectangle doorRect = new ZTRectangle(nextGatePoint.X - 70, nextGatePoint.Y - 70, nextGatePoint.X + 70, nextGatePoint.Y + 70); G.Instance.InfoWriter("has door:" + mapDoorPoint.ToString()+",rect:"+doorRect.ToString()); if (GeoHelper.IsInRect(mapDoorPoint, doorRect)) { G.Instance.InfoWriter("in door"); this.state_DoorDirect = door.Value; this.state_DoorPosition = door.Key; break; } } } if (!this.state_DoorPosition.Equals(ZTPoint.Empty)) { //找到门,向门移动 SetState(KillMonsterStates.EntryDoor, false); } else { //未找到门,是否存进门点 SetState(KillMonsterStates.InNextGatePoint, false); } } /// /// 向门移动, 进门 /// private void EntryDoor() { const Int32 offsetX = 200; const Int32 offsetY = 100; ZTRectangle doorRect = new ZTRectangle(this.state_DoorPosition.X - offsetX, this.state_DoorPosition.Y - offsetY, this.state_DoorPosition.X + offsetX, this.state_DoorPosition.Y + offsetY); if (tryEntryDoor && GeoHelper.IsInRect(this.role.RoleCBPosition, doorRect)) { //离门太近,先向外走,再向门走 Int32 limitLine = 0; Int32 diff = 0; //传过来人物坐标和门的坐标,根据门的朝向计算人物走向 switch (state_DoorDirect) { case Direction.Up: //门在上方 limitLine = state_DoorPosition.Y + offsetY; //下移 this.role.SyncMove(new ZTPoint(0, limitLine - role.Position.Y)); //垂直对齐 diff = state_DoorPosition.X - role.Position.X; this.role.SyncMove(new ZTPoint(diff, 0)); //移动y,进入 this.role.SyncMove(new ZTPoint(0, map.GameRect.Start.Y - role.Position.Y)); break; case Direction.Right: //门在右侧 limitLine = state_DoorPosition.X - offsetX; //如果角色位于门右侧,先向左移 this.role.SyncMove(new ZTPoint(limitLine - role.Position.X, 0)); //水平对齐 diff = state_DoorPosition.Y - role.Position.Y; this.role.SyncMove(new ZTPoint(0, diff)); //移动x,进入 this.role.SyncMove(new ZTPoint(map.GameRect.End.X - role.Position.X, 0)); break; case Direction.Bottom: //门在下方 limitLine = state_DoorPosition.Y - offsetY; //如果角色在门下方,先向上移 this.role.SyncMove(new ZTPoint(0, limitLine - role.Position.Y)); //垂直对齐 diff = state_DoorPosition.X - role.Position.X; this.role.SyncMove(new ZTPoint(diff, 0)); //移动y,进入 this.role.SyncMove(new ZTPoint(0, map.GameRect.End.Y - role.Position.Y)); break; case Direction.Left: //门在左侧 limitLine = state_DoorPosition.X + offsetX; //如果角色位于门左侧,先向右移 this.role.SyncMove(new ZTPoint(limitLine - role.Position.X, 0)); //水平对齐 diff = state_DoorPosition.Y - role.Position.Y; this.role.SyncMove(new ZTPoint(0, diff)); //移动x,进入 this.role.SyncMove(new ZTPoint(map.GameRect.Start.X - role.Position.X, 0)); break; } tryEntryDoor = false; } else { //先到进门点 ZTPoint roleMapPoint = this.house.ScreenToMapCoordinate(this.role.Position, this.stateScreenLocation); ZTPoint nextGatePoint = this.house.GetNextGatePoint(this.state_DoorDirect); List paths = this.house.FindPath(roleMapPoint, DnfRole.HalfToFootPosition(nextGatePoint)); //再到门后50像素 ZTPoint dp = this.house.ScreenToMapCoordinate(this.state_DoorPosition, this.stateScreenLocation); ZTPoint doorPoint = DnfRole.HalfToFootPosition(dp); switch (this.state_DoorDirect) { case Direction.Up: doorPoint = doorPoint.Add(0, -50); break; case Direction.Right: doorPoint = doorPoint.Add(50, 0); break; case Direction.Bottom: doorPoint = doorPoint.Add(0, 50); break; case Direction.Left: doorPoint = doorPoint.Add(-50, 0); break; } paths.Add(doorPoint); this.role.MovePaths(roleMapPoint, paths); tryEntryDoor = true; } SetState(KillMonsterStates.IsLastHouse, true); return; } /// /// 判断使用何技能,计算攻击移动距离, 是否需要移动 /// private void CalcAttackDistance() { SkillInfo attackSkill = this.house.Skills.SyncPeek(); //计算攻击移动距离 bool needMove = false; skillReleasePoint = AttackRectangle.GetAttackPoint(map.GameRect, role.HalfPosition, stateMonsters, attackSkill, out roleDirection, out needMove); if (!needMove) { G.Instance.DebugWriter(string.Format("不需移动直接发技能,距离:{0}", skillReleasePoint.ToString())); SetState(KillMonsterStates.ReleaseSkill, false); } else { SetState(KillMonsterStates.AttackMove, false); } } /// /// 攻击移动 /// private void AttackMove() { G.Instance.DebugWriter("attack move :" + skillReleasePoint.ToString() + ",role:" + role.Position.ToString() + ",monster1:" + stateMonsters[0].ToString() + ",skill:" + this.house.Skills.SyncPeek().Key.ToString()); this.role.AttackMoveTo(skillReleasePoint,this.stateScreenLocation); SetState(KillMonsterStates.FindRole, true); } /// /// 调整朝向, 释放技能 /// private void ReleaseSkill() { this.role.StopMove(); SkillInfo skill = this.house.Skills.SyncDeQueue(); if (roleDirection != HIDCode.NoEvent) { G.Instance.InputControl.PressKey(100, roleDirection); } 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.ReleaseWaitTime); } G.Instance.DebugWriter(string.Format("发完技能,技能按键:{0},技能名称:{1}", skill.Key, skill.SkillName)); SetState(KillMonsterStates.IsLastHouse, true); } #endregion /// /// 得到最近的物品 /// /// /// public ZTPoint GetNearlyThingItem(Image image, ZTPoint rolePosition) { List points = DnfCVHelper.GetThingItemPoints(image, map.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; } /// /// 牌的位置 /// private static readonly ZTPoint[] CardList = new ZTPoint[] { new ZTPoint(164,195), new ZTPoint(315,195), new ZTPoint(474,195), new ZTPoint(635,195), new ZTPoint(164,480), new ZTPoint(315,480), new ZTPoint(474,480), new ZTPoint(635,480), }; /// /// 翻牌 /// 1-4 /// 5-8 /// /// private void Fanpai() { Thread.Sleep(2000); Int32 number = RandomUtils.G(1, 4); ZTPoint willPosition = map.GameRect.Start.Add(CardList[number - 1]); G.Instance.InputControl.MoveToAndClick(RandomUtils.PointRange(willPosition, 10)); //判断黄金版是否可以翻 if (DnfCVHelper.HasGoldCard(map.GameRect)) { number = RandomUtils.G(5, 8); willPosition = map.GameRect.Start.Add(CardList[number - 1]); G.Instance.InputControl.MoveToAndClick(RandomUtils.PointRange(willPosition, 10)); } } /// /// 是否改变房间 /// /// /// private bool HouseIsChange() { Int32 houseIndex = 0; if (!map.MiniMap.GetCurrentHouseIndexWaitTimeout(out houseIndex, image, map.CancelToken, 30 * 1000)) { throw new Exception("找不到默认房间"); } if (houseIndex != this.house.Index) { return true; } return false; } /// /// 杀怪状态 /// public enum KillMonsterStates : Int32 { Start,//开始状态 Exit,//完成状态 IsLastHouse,//是否最后一个房间 HasRewardWindow,//是否有奖励界面 TurnAroundCard,//翻牌 IsCompletePage,//是否刷完界面 IsOtherHouse,//是否进入其它房间 FindRole,//找主角 FindRoleMove,//让主角移动(原:有怪攻击一下,无怪移动一下) FindMonster,//找怪 PickupThing,//拾取物品 FindDoor,//查找门, 是否找到门 EntryDoor,//向门移动, 进门 InNextGatePoint,//是否在进门点 ToNextGatePoint,//移至进门点 ToLoopPoint,//移至巡逻点 CalcAttackPoint,//判断使用何技能,计算攻击移动距离, 是否需要移动 ReleaseSkill,//调整朝向, 释放技能, AttackMove,//攻击移动 } } }