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,//攻击移动
}
}
}