using Emgu.CV;
|
using Emgu.CV.Structure;
|
using RichCreator.Dnf;
|
using RichCreator.Maps.Kalete;
|
using RichCreator.Models;
|
using RichCreator.StateMachines;
|
using RichCreator.Utility;
|
using RichCreator.Utility.Captures;
|
using RichCreator.Utility.CV;
|
using RichCreator.Utility.Dnf;
|
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
|
{
|
/// <summary>
|
/// 杀怪状态机
|
/// </summary>
|
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<Rgb, byte> image = null;//原图
|
private Image<Hsv, byte> 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)
|
{
|
if (house.HousePathInfo.LoopPoint.Equals(ZTPoint.Empty))
|
{
|
throw new ArgumentOutOfRangeException("寻路中不存在循环点");
|
}
|
|
this.map = map;
|
this.house = house;
|
this.role = role;
|
|
|
hsvImage = new Image<Hsv, byte>(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;
|
}
|
|
/// <summary>
|
/// 状态机开始
|
/// </summary>
|
/// <param name="gameRect"></param>
|
/// <param name="cancellationToken"></param>
|
/// <param name="timeoutMillSecond"></param>
|
/// <returns></returns>
|
public ZTResult Work(Int32 timeoutMillSecond,Int32 fromHouseIndex,Int32 runningStep)
|
{
|
bool imageIsChange = false;//图像是否改变
|
DateTime expireTime = DateTime.Now.AddMilliseconds(timeoutMillSecond);
|
|
while (true)
|
{
|
if (map.CancelToken.IsCancellationRequested)
|
{
|
G.Instance.DebugWriter("取消刷图");
|
this.role.StopMove("cancel");
|
return ZTResult.Cancel;
|
}
|
|
if (DateTime.Now > expireTime)
|
{
|
G.Instance.DebugWriter("刷图超时");
|
this.role.StopMove("timeout");
|
return ZTResult.Timeout;
|
}
|
|
if (needCaptureScreen)
|
{
|
using (System.Drawing.Bitmap bitmap = ScreenCapture.Instance.CaptureScreen())
|
{
|
if (image != null)
|
{
|
image.Dispose();
|
}
|
image = new Image<Rgb, byte>(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:
|
//开始
|
if (this.house.FromHouseIndex == fromHouseIndex)
|
{
|
this.map.EntryHousePrework(this.house.Index, fromHouseIndex);
|
}
|
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))
|
{
|
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))
|
{
|
//未找到角色
|
if (!this.role.IsMoving)
|
{
|
SetState(KillMonsterStates.FindRoleMove, false);
|
}
|
else
|
{
|
SetState(KillMonsterStates.IsLastHouse, true);
|
}
|
}
|
else
|
{
|
if (this.role.IsMoveIntent(MoveIntent.FindRoleMove))
|
{
|
this.role.StopMove("update cb position");
|
}
|
role.UpdatePosition(roleCBPosition);
|
SetState(KillMonsterStates.FindMonster, false);
|
}
|
break;
|
case KillMonsterStates.FindRoleMove:
|
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
|
{
|
if (this.role.IsMoveIntent(MoveIntent.AttackMove))
|
{
|
this.role.StopMove("find monster to pickup thing");
|
}
|
SetState(KillMonsterStates.PickupThing, false);
|
}
|
break;
|
case KillMonsterStates.PickupThing:
|
//拾取物品
|
PickupThing();
|
break;
|
case KillMonsterStates.FindDoor:
|
//查找门
|
if (this.house.IsEnd)
|
{
|
SetState(KillMonsterStates.IsLastHouse, true);
|
break;
|
}
|
FindDoor();
|
break;
|
case KillMonsterStates.EntryDoor:
|
//向门移动, 进门
|
EntryDoor();
|
break;
|
case KillMonsterStates.ToLoop:
|
//巡逻
|
ToLoop();
|
break;
|
case KillMonsterStates.CalcAttackPoint:
|
//判断使用何技能,计算攻击移动距离, 是否需要移动
|
CalcAttackDistance();
|
break;
|
case KillMonsterStates.ReleaseSkill:
|
//调整朝向, 释放技能,
|
ReleaseSkill();
|
break;
|
case KillMonsterStates.AttackMove:
|
//攻击移动
|
AttackMove();
|
break;
|
case KillMonsterStates.Exit:
|
//结束
|
this.role.StopMove("exit");
|
if (this.isSuccess)
|
{
|
return ZTResult.Success;
|
}
|
return ZTResult.Failed;
|
}
|
|
G.Instance.DebugWriter($"next state:{currentState},capture screen:{needCaptureScreen}");
|
}
|
}
|
|
#region States
|
/// <summary>
|
/// 让主角移动(原:有怪攻击一下,无怪移动一下)
|
/// </summary>
|
private void FindRoleMove()
|
{
|
if (this.role.IsMoving)
|
{
|
if (this.role.IsMoveIntent(MoveIntent.FindRoleMove))
|
{
|
//进行中,则返回
|
SetState(KillMonsterStates.IsLastHouse, true);
|
return;
|
}
|
else
|
{
|
//不是则停止
|
this.role.StopMove("find role other");
|
}
|
}
|
this.role.FindRoleMove();
|
SetState(KillMonsterStates.IsLastHouse, true);
|
}
|
|
|
DateTime pickupTime = DateTime.MinValue;
|
/// <summary>
|
/// 拾取物品
|
/// </summary>
|
private void PickupThing()
|
{
|
//拾取物品中,返回
|
if (this.role.IsMoveIntent(MoveIntent.PickupMove))
|
{
|
SetState(KillMonsterStates.IsLastHouse, true);
|
return;
|
}
|
|
ZTPoint screenThingPosition = GetNearlyThingItem(hsvImage, role.Position);
|
//是否有物品
|
if (!screenThingPosition.Equals(ZTPoint.Empty))
|
{
|
//有其它移动,则停止
|
if (this.role.IsMoving&&!this.role.IsMoveIntent(MoveIntent.PickupMove))
|
{
|
this.role.StopMove("pickup moveing");
|
}
|
|
//第一次发现有物品后,等一秒再去拾取,防物品刚出来时的去错误的位置拾取
|
if (pickupTime == DateTime.MinValue)
|
{
|
pickupTime = DateTime.Now.AddSeconds(1);
|
}
|
|
if (pickupTime > DateTime.Now)
|
{
|
SetState(KillMonsterStates.IsLastHouse, true);
|
return;
|
}
|
////todo:remove
|
//hsvImage.Save(DateTime.Now.ToString("HH_mm_ss_fff")+"_" + Guid.NewGuid().ToString()+".png");
|
|
this.role.PickupMove(screenThingPosition,this.stateScreenLocation);
|
SetState(KillMonsterStates.IsLastHouse, true);
|
}
|
else
|
{
|
SetState(KillMonsterStates.FindDoor, false);
|
}
|
}
|
|
/// <summary>
|
/// 查找门, 是否找到门
|
/// </summary>
|
private void FindDoor()
|
{
|
//查找真实的门
|
Dictionary<ZTPoint,Direction> 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);
|
|
if (GeoHelper.IsInRect(mapDoorPoint, doorRect))
|
{
|
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.ToLoop, false);
|
}
|
}
|
|
/// <summary>
|
/// 向门移动, 进门
|
/// </summary>
|
private void EntryDoor()
|
{
|
//已经开始进门
|
if (this.role.IsMoving)
|
{
|
this.role.StopMove("entry door other moving");
|
}
|
|
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.Sub(DnfRole.RoleHalfOffset), 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
|
{
|
this.role.EntryDoorMove(this.state_DoorPosition, this.stateScreenLocation, this.state_DoorDirect);
|
tryEntryDoor = true;
|
}
|
|
SetState(KillMonsterStates.IsLastHouse, true);
|
return;
|
}
|
|
/// <summary>
|
/// 判断使用何技能,计算攻击移动距离, 是否需要移动
|
/// </summary>
|
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)
|
{
|
if (this.role.IsMoving)
|
{
|
this.role.StopMove("calc attack");
|
}
|
G.Instance.DebugWriter(string.Format("不需移动直接发技能,距离:{0}", skillReleasePoint.ToString()));
|
SetState(KillMonsterStates.ReleaseSkill, false);
|
}
|
else
|
{
|
SetState(KillMonsterStates.AttackMove, false);
|
}
|
}
|
|
/// <summary>
|
/// 攻击移动
|
/// </summary>
|
private void AttackMove()
|
{
|
if (this.role.IsMoving)
|
{
|
if (this.role.IsMoveIntent(MoveIntent.AttackMove))
|
{
|
//todo:添加如果是攻击移动中,如果怪物偏差太大则重新计算
|
//进行中,则返回
|
SetState(KillMonsterStates.IsLastHouse, true);
|
return;
|
}
|
else
|
{
|
//不是则停止
|
this.role.StopMove("attack move other");
|
}
|
}
|
|
G.Instance.DebugWriter("attack move :" + skillReleasePoint.ToString() + ",role:" + role.Position.ToString() + ",monster1:" + stateMonsters[0].ToString() + ",skill:" + this.house.Skills.SyncPeek().Key.ToString());
|
this.role.AttackMove(skillReleasePoint, this.stateScreenLocation);
|
SetState(KillMonsterStates.FindRole, true);
|
}
|
|
/// <summary>
|
/// 调整朝向, 释放技能
|
/// </summary>
|
private void ReleaseSkill()
|
{
|
SkillInfo skill = this.house.Skills.SyncDeQueue();
|
StateProvider.Instance.AttackKey = skill.Key;
|
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);
|
}
|
|
/// <summary>
|
/// 巡逻
|
/// </summary>
|
private void ToLoop()
|
{
|
//巡逻
|
if (this.role.IsMoving)
|
{
|
if (this.role.IsMoveIntent(MoveIntent.ToLoop))
|
{
|
//进行中,则返回
|
SetState(KillMonsterStates.IsLastHouse, true);
|
return;
|
}
|
else
|
{
|
//不是则停止
|
this.role.StopMove("to loop other");
|
}
|
}
|
|
this.role.LoopMove(this.stateScreenLocation);
|
SetState(KillMonsterStates.IsLastHouse, true);
|
}
|
#endregion
|
|
/// <summary>
|
/// 得到最近的物品
|
/// </summary>
|
/// <param name="image"></param>
|
/// <returns></returns>
|
public ZTPoint GetNearlyThingItem(Image<Hsv, byte> image, ZTPoint rolePosition)
|
{
|
ZTPoint[] points = DnfCVHelper.FindThings(image, map.GameRect);
|
if (points.Length <= 0)
|
{
|
return ZTPoint.Empty;
|
}
|
|
if (points.Length == 1)
|
{
|
return points[0];
|
}
|
|
double distance = 0;
|
ZTPoint result = ZTPoint.Empty;
|
for (int i = 0; i < points.Length; 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;
|
}
|
|
/// <summary>
|
/// 牌的位置
|
/// </summary>
|
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),
|
|
};
|
|
/// <summary>
|
/// 翻牌
|
/// 1-4
|
/// 5-8
|
/// </summary>
|
/// <param name="number"></param>
|
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));
|
}
|
|
}
|
|
/// <summary>
|
/// 是否改变房间
|
/// </summary>
|
/// <param name="cancellationToken"></param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 杀怪状态
|
/// </summary>
|
public enum KillMonsterStates : Int32
|
{
|
Start,//开始状态
|
Exit,//完成状态
|
IsLastHouse,//是否最后一个房间
|
HasRewardWindow,//是否有奖励界面
|
TurnAroundCard,//翻牌
|
IsCompletePage,//是否刷完界面
|
IsOtherHouse,//是否进入其它房间
|
FindRole,//找主角
|
FindRoleMove,//让主角移动(原:有怪攻击一下,无怪移动一下)
|
FindMonster,//找怪
|
PickupThing,//拾取物品
|
FindDoor,//查找门, 是否找到门
|
EntryDoor,//向门移动, 进门
|
ToLoop,//巡逻
|
|
|
|
CalcAttackPoint,//判断使用何技能,计算攻击移动距离, 是否需要移动
|
ReleaseSkill,//调整朝向, 释放技能,
|
AttackMove,//攻击移动
|
}
|
}
|
}
|