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
|
{
|
/// <summary>
|
/// 杀怪状态机
|
/// </summary>
|
public class KillMonsterStateMachine_old:StateMachineBase
|
{
|
private const Int32 NoMoveMaxMillSecond = 2000;//最大未移动容忍毫秒数
|
|
private HouseInfo house;//当前房间
|
private ZTPoint miniMapStart;//小地图区域
|
private ZTRectangle gameRect;//游戏区域
|
private Int32 preHouseIndex = 0;//上一房间编号
|
private MoveState moveState;//移动状态
|
private OutOfBounds outOfBounds;//禁区
|
private bool isSuccess;//退出结果
|
|
|
private ZTPoint roleLastPosition=ZTPoint.Empty;//角色最后位置
|
private Int32 runningStep = RunningStep.None;//是否恢复的状态
|
|
//当前状态
|
private KillMonsterStates currentState = KillMonsterStates.Start;
|
//是否截图屏幕
|
private bool captureScreen = false;
|
|
|
#region Find Door Info
|
//门坐标
|
private ZTPoint stateDoorPosition;
|
//角色位置
|
private ZTPoint stateRolePosition;
|
//离开的门朝向
|
private Direction stateDoorLevelDirect = Direction.None;
|
//定位点方框
|
private MultiList<ZTRectangle, Int32> stateLocationRectangle = new MultiList<ZTRectangle, int>();
|
//怪
|
private ZTPoint[] stateMonsters;
|
|
private System.Drawing.Bitmap bitmap = null;
|
//原图
|
private Image<Rgb, byte> image = null;
|
//色彩hsv
|
private Image<Hsv, byte> hsvImage = null;
|
#endregion
|
|
|
public KillMonsterStateMachine(HouseInfo 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);
|
hsvImage = new Image<Hsv, byte>(gameRect.End.X - gameRect.Start.X + 1, gameRect.End.Y - gameRect.Start.Y + 1);
|
}
|
|
|
|
private void SetState(KillMonsterStates current, bool capture)
|
{
|
currentState = current;
|
captureScreen = capture;
|
}
|
|
/// <summary>
|
/// 状态机开始
|
/// </summary>
|
/// <param name="gameRect"></param>
|
/// <param name="cancellationToken"></param>
|
/// <param name="timeoutMillSecond"></param>
|
/// <returns></returns>
|
public ZTResult Work(CancellationToken cancellationToken, Int32 timeoutMillSecond)
|
{
|
DateTime expireTime = DateTime.Now.AddMilliseconds(timeoutMillSecond);
|
if (this.runningStep > RunningStep.None)
|
{
|
SetState(KillMonsterStates.FindMonster, true);
|
}
|
|
|
while (true)
|
{
|
if (cancellationToken.IsCancellationRequested)
|
{
|
G.Instance.DebugWriter("取消刷图");
|
this.moveState.StopMove();
|
return ZTResult.Cancel;
|
}
|
|
if (DateTime.Now > expireTime)
|
{
|
G.Instance.DebugWriter("刷图超时");
|
this.moveState.StopMove();
|
return ZTResult.Timeout;
|
}
|
|
if (captureScreen)
|
{
|
using (bitmap = ScreenCapture.Instance.CaptureScreen())
|
{
|
image = new Image<Rgb, byte>(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));
|
}
|
|
if (this.house.Index != 15)
|
{
|
//计算是否进入其它房间
|
if (HouseIsChange(cancellationToken))
|
{
|
return ZTResult.Success;
|
}
|
}
|
}
|
|
bool lastCaptureScreen = this.captureScreen;
|
switch (currentState)
|
{
|
case KillMonsterStates.Start:
|
StartState();
|
//开始
|
break;
|
case KillMonsterStates.FindMonster:
|
//找怪
|
CvInvoke.CvtColor(image, hsvImage, Emgu.CV.CvEnum.ColorConversion.Rgb2Hsv);
|
FindMonster(image, hsvImage);
|
break;
|
case KillMonsterStates.DetectDoorIsOpen:
|
//查找进入下一房间的门是否开了
|
CvInvoke.CvtColor(image, hsvImage, Emgu.CV.CvEnum.ColorConversion.Rgb2Hsv);
|
DetectDoorIsOpen(image, hsvImage);
|
break;
|
case KillMonsterStates.MoveToDoor:
|
//移动进门
|
MoveToDoor();
|
break;
|
case KillMonsterStates.PrepareReleaseSkill:
|
//释放技能预处理,计算位置
|
PrepareReleaseSkill();
|
break;
|
case KillMonsterStates.ReleaseSkill:
|
//释放技能
|
ReleaseSkill();
|
break;
|
case KillMonsterStates.Exit:
|
//结束
|
G.Instance.DebugWriter("退出状态");
|
this.moveState.StopMove();
|
if (this.isSuccess)
|
{
|
return ZTResult.Success;
|
}
|
return ZTResult.Failed;
|
}
|
|
if (lastCaptureScreen)
|
{
|
image.Dispose();
|
}
|
G.Instance.DebugWriter("next state:" + currentState.ToString());
|
}
|
}
|
|
#region States
|
private void StartState()
|
{
|
SingleEntryHouseSkill.ReleaseSkill(this.house.Index, this.preHouseIndex,this.moveState);
|
SetState(KillMonsterStates.FindMonster, true);
|
}
|
|
/// <summary>
|
/// 0.找怪
|
/// </summary>
|
/// <param name="image"></param>
|
/// <param name="hsvImage"></param>
|
/// <returns></returns>
|
private void FindMonster(Image<Rgb, byte> image, Image<Hsv, byte> hsvImage)
|
{
|
//怪
|
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);
|
SetState(KillMonsterStates.FindMonster, true);
|
return;
|
}
|
|
SetState(KillMonsterStates.PrepareReleaseSkill, false);
|
return;
|
}
|
else
|
{
|
//未查找到主角
|
|
if (stateRolePosition == ZTPoint.Empty)
|
{
|
if (this.moveState.IsMoving && !this.moveState.IsFindRoleMoving)
|
{
|
this.moveState.StopMove();
|
}
|
|
|
//是否刷完
|
if (IsComplete(image))
|
{
|
SetState(KillMonsterStates.Exit, false);
|
return;
|
}
|
|
this.moveState.FindRoleMove();
|
SetState(KillMonsterStates.FindMonster, true);
|
return;
|
}
|
SetState(KillMonsterStates.DetectDoorIsOpen, true);
|
return;
|
}
|
}
|
|
private DateTime FindRoleLastNoMoveTime = DateTime.MaxValue;//最后没移动时间
|
private bool FindRoleLastNoMoveStart = false;//没移动是否开始计时
|
/// <summary>
|
/// 1.释放技能预处理,计算位置
|
/// </summary>
|
/// <returns></returns>
|
private void PrepareReleaseSkill()
|
{
|
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()));
|
SetState(KillMonsterStates.ReleaseSkill, false);
|
return;
|
}
|
|
//没有移动,可能有障碍物
|
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);
|
SetState(KillMonsterStates.FindMonster, true);
|
return;
|
}
|
|
/// <summary>
|
/// 2.释放技能
|
/// </summary>
|
/// <returns></returns>
|
private void ReleaseSkill()
|
{
|
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));
|
SetState(KillMonsterStates.FindMonster, true);
|
|
return;
|
}
|
|
|
/// <summary>
|
/// 3.检测进入下一房间的门是否开了
|
/// </summary>
|
/// <returns></returns>
|
private void DetectDoorIsOpen(Image<Rgb, byte> image, Image<Hsv, byte> hsvImage)
|
{
|
if (this.moveState.IsAttackMoving|| this.moveState.IsFindRoleMoving)
|
{
|
this.moveState.StopMove();
|
}
|
|
////查找当前房间编号,看是否进入其它房间
|
//Int32 houseIndex;
|
//if (!LindongCVHelper.GetCurrentHouseIndex(out houseIndex, image, this.miniMapStart))
|
//{
|
if (IsComplete(image))
|
{
|
SetState(KillMonsterStates.Exit, false);
|
return;
|
}
|
|
// 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;
|
|
//找到门,向门移动
|
SetState(KillMonsterStates.MoveToDoor, false);
|
return;
|
}
|
else
|
{
|
//未找到门,循环移动
|
Int32 areaID = 0;
|
if (this.outOfBounds.InOutOfBound(this.house.Index, stateRolePosition, out areaID))
|
{
|
this.outOfBounds.MoveToCommonBound(areaID);
|
}
|
else
|
{
|
this.moveState.FindDoorMove(stateRolePosition);
|
}
|
}
|
SetState(KillMonsterStates.FindMonster, true);
|
return;
|
}
|
|
/// <summary>
|
/// 4.找门
|
/// </summary>
|
/// <returns></returns>
|
private void MoveToDoor()
|
{
|
const Int32 XLevelOffset = 200;
|
const Int32 YLevelOffset = 100;
|
Int32 limitLine = 0;
|
Int32 diff = 0;
|
//传过来人物坐标和门的坐标,根据门的朝向计算人物走向
|
switch (stateDoorLevelDirect)
|
{
|
case Direction.Up:
|
//门在上方
|
limitLine = stateDoorPosition.Y + YLevelOffset;
|
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 - XLevelOffset;
|
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 - YLevelOffset;
|
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 + XLevelOffset;
|
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;
|
}
|
SetState(KillMonsterStates.DetectDoorIsOpen, true);
|
return;
|
}
|
#endregion
|
|
/// <summary>
|
/// 得到最近的物品
|
/// </summary>
|
/// <param name="image"></param>
|
/// <returns></returns>
|
public ZTPoint GetNearlyThingItem(Image<Rgb, byte> image,ZTPoint rolePosition)
|
{
|
List<ZTPoint> 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;
|
}
|
|
/// <summary>
|
/// 是否完成
|
/// </summary>
|
/// <param name="image"></param>
|
/// <returns></returns>
|
public bool IsComplete(Image<Rgb,byte> image)
|
{
|
if (this.house.IsEnd)
|
{
|
|
if (DnfCVHelper.IsJiangli(image, this.gameRect))
|
{
|
//翻牌
|
this.moveState.StopMove();
|
Fanpai();
|
Thread.Sleep(5000);
|
this.isSuccess = true;
|
return true;
|
}
|
|
if (DnfCVHelper.IsCompleteRoom(image, this.gameRect))
|
{
|
//已经刷完
|
this.moveState.StopMove();
|
this.isSuccess = true;
|
return true;
|
}
|
}
|
return false;
|
}
|
|
#region 翻牌
|
/// <summary>
|
/// 牌的位置
|
/// </summary>
|
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),
|
|
};
|
/// <summary>
|
/// 翻牌
|
/// 1-4
|
/// 5-8
|
/// </summary>
|
/// <param name="number"></param>
|
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
|
|
/// <summary>
|
/// 是否改变房间
|
/// </summary>
|
/// <param name="cancellationToken"></param>
|
/// <returns></returns>
|
private bool HouseIsChange(CancellationToken cancellationToken)
|
{
|
Int32 houseIndex;
|
if (!LindongCVHelper.GetCurrentHouseIndex(out houseIndex, image, this.miniMapStart))
|
{
|
//得不到房间
|
if (!FuncUtils.TimeoutCancelableWrap(30 * 1000, cancellationToken, () => {
|
using (bitmap = ScreenCapture.Instance.CaptureScreen())
|
{
|
image = new Image<Rgb, byte>(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))
|
{
|
throw new Exception("找不到默认房间");
|
}
|
}
|
if (houseIndex != this.house.Index)
|
{
|
this.moveState.StopMove();
|
return true;
|
}
|
return false;
|
}
|
|
|
/// <summary>
|
/// 杀怪状态
|
/// </summary>
|
public enum KillMonsterStates:Int32
|
{
|
FindMonster = 0,//找怪
|
PrepareReleaseSkill = 1,//释放技能预处理,计算位置
|
ReleaseSkill = 2,//释放技能
|
DetectDoorIsOpen = 3,//查找进入下一房间的门是否开了
|
MoveToDoor = 4,//移动进门
|
|
Start = 8,//开始状态
|
Exit = 9,//完成状态
|
}
|
}
|
}
|