using CommonAI.Zone; using CommonAI.Zone.Helper; using CommonAI.Zone.Instance; using CommonLang; using CommonLang.Vector; using System; using static CommonAI.Zone.Instance.InstanceUnit; namespace XmdsCommonServer.Plugin.Units { public class XmdsInstanceUnitStateMachine { abstract public class XmdsStateFollow : InstanceUnit.State { public enum MoveState { Hold, Move, } // 被追目标 private IPositionObject target; // 如果被单位碰撞,则向左或右挪动// private MoveAI moveAI; // 检测切换走停的间隔时间 // readonly private State state = new State(MoveState.Move); readonly private bool begin_in_min_range; // 检测切换走停的间隔时间 // private TimeInterval start_move_hold_time; public abstract bool IsActive { get; } public bool IsNoWay { get { if (moveAI != null) { return moveAI.IsNoWay; } return false; } } public IPositionObject Target { get { return target; } } public MoveState FollowState { get { return state.Value; } } /// /// Hold到Move之间的检测间隔 /// public int StartMoveHoldTimeMS { get { return (start_move_hold_time != null) ? start_move_hold_time.IntervalTimeMS : 0; } set { if (value > 0 && value != StartMoveHoldTimeMS) { start_move_hold_time = new TimeInterval(value); } } } public virtual void MarkCannotArriveToAttack() { } public XmdsStateFollow(InstanceUnit unit, InstanceZoneObject target, bool beginInMinRange = true) : base(unit) { this.begin_in_min_range = beginInMinRange; this.target = target; this.moveAI = new MoveAI(unit); } public void ChangeMoveTarget(IPositionObject target) { if (moveAI.Target != target) { this.target = target; this.moveAI = new MoveAI(unit); } } public override bool onBlock(InstanceUnit.State new_state) { return true; } protected override void onStart() { if (cheekBeginInRange()) { state.ChangeState(MoveState.Hold); unit.SetActionStatus(UnitActionStatus.Idle); } else { state.ChangeState(MoveState.Move); unit.SetActionStatus(UnitActionStatus.Move); moveAI.FindPath(target); } } protected override void onUpdate() { if (!IsActive) { unit.doSomething(); } else { switch (FollowState) { case MoveState.Move: // 进入最小追踪距离 // if (CheckTargetInMinRange() == true) { changeToHold(); } else { if (!this.unit.IsCannotMove) { if (moveAI.Target == null) { moveAI.FindPath(target); } moveAI.Update(); } if (moveAI.IsNoWay) { if (CheckTargetInMaxRange() == true) { changeToHold(); } else { //System.Console.WriteLine("标记不可攻击:" + this.unit.Name); this.MarkCannotArriveToAttack(); unit.doSomething(); } } } break; case MoveState.Hold: // 超过最大追踪范围 // if ((start_move_hold_time == null || start_move_hold_time.Update(zone.UpdateIntervalMS)) && (CheckTargetInMaxRange() == false)) { changeToMove(); } else { onUpdateFollowed(target); } break; } } } protected override void onStop() { } private bool cheekBeginInRange() { if (begin_in_min_range) { return CheckTargetInMinRange(); } else { return CheckTargetInMaxRange(); } } private void changeToMove() { state.ChangeState(MoveState.Move); unit.SetActionStatus(UnitActionStatus.Move); moveAI.FindPath(target); onInRangeChanged(); onChangedToMove(target); } private void changeToHold() { state.ChangeState(MoveState.Hold); unit.SetActionStatus(UnitActionStatus.Idle); moveAI.Pause(); onInRangeChanged(); onChangedToHold(target); } /// /// 检查目标是否在跟随范围内。 /// 检测为True,停止移动。 /// protected abstract bool CheckTargetInMinRange(); /// /// 检查目标是否在跟随范围内。 /// 检测为False,开始移动。 /// protected abstract bool CheckTargetInMaxRange(); /// /// 行为改变 /// protected virtual void onInRangeChanged() { } /// /// 目标已经被追踪到 /// /// protected virtual void onUpdateFollowed(IPositionObject target) { } /// /// 开始移动 /// /// protected virtual void onChangedToMove(IPositionObject target) { } /// /// changeToHold /// /// protected virtual void onChangedToHold(IPositionObject target) { } } public class XmdsStateFollowAndAttack : XmdsStateFollow { /*readonly private*/ InstanceUnit targetUnit; private SkillTemplate.CastTarget expectTarget; private TimeInterval checkAdjustLaunchSkillTime; private InstanceUnit.SkillState launchSkill; private bool can_auto_focus_near_target; private bool can_random_skill = true; private bool can_attack = true; private float min_distance; private float max_distance; /// /// 是否追踪 /// /// override public bool IsActive { get { return can_attack && targetUnit.IsActive; } } public InstanceUnit.SkillState ExpectSkillState { get { return launchSkill; } set { launchSkill = value; expectTarget = value.Data.ExpectTarget; } } /** 标记不能达到目的,并攻击目标 */ public override void MarkCannotArriveToAttack() { this.can_attack = false; } public SkillTemplate ExpectSkill { get { return (launchSkill != null) ? launchSkill.Data : unit.DefaultSkill; } } public InstanceUnit TargetUnit { get { return targetUnit; } set { targetUnit = value; } } public SkillTemplate.CastTarget ExpectTarget { get { return expectTarget; } } public bool IsLaunchRandomSkill { get { return can_random_skill; } set { can_random_skill = value; } } public bool IsAutoFocusNearTarget { get { return can_auto_focus_near_target; } set { can_auto_focus_near_target = value; } } public XmdsStateFollowAndAttack( InstanceUnit unit, InstanceUnit target, SkillTemplate.CastTarget expectTarget = SkillTemplate.CastTarget.Enemy, bool autoFocusNearTarget = false, InstanceUnit.SkillState lanuchSkill = null) : base(unit, target, false) { this.targetUnit = target; this.expectTarget = expectTarget; this.can_auto_focus_near_target = autoFocusNearTarget; this.StartMoveHoldTimeMS = zone.Templates.CFG.AI_FOLLOW_AND_ATTACK_HOLD_TIME_MS; this.launchSkill = lanuchSkill; } protected override void onStart() { this.resetMaxMinRange(); this.checkAdjustLaunchSkillTime = new TimeInterval(zone.Templates.CFG.AI_FOLLOW_AND_ATTACK_HOLD_TIME_MS); this.launchSkill = unit.getRandomLaunchableExpectSkill(expectTarget); base.onStart(); } protected override void onUpdateFollowed(IPositionObject target) { this.can_attack = zone.IsAttackable(unit, targetUnit, expectTarget, AttackReason.Tracing, this.ExpectSkill); if (can_attack) { unit.faceTo(target.X, target.Y); if (TryLaunchSkill() == null) { SkillTemplate expect_skill = ExpectSkill; if (expect_skill != null && expect_skill.AttackKeepRange > 0) { if (checkAdjustLaunchSkillTime.IntervalTimeMS == 0 || checkAdjustLaunchSkillTime.Update(zone.UpdateIntervalMS)) { if (CUtils.RandomPercent(zone.RandomN, zone.Templates.CFG.AI_FOLLOW_AND_ATTACK_ADJUST_ESCAPE_PCT)) { unit.startAdjustLaunchSkill(expect_skill, targetUnit, this.OnEndAdjustKeepRange); } } } } } } protected override void onStop() { base.onStop(); } protected override void onUpdate() { if (IsActive) { resetMaxMinRange(); } base.onUpdate(); } private void resetMaxMinRange() { var expect_skill = ExpectSkillState; min_distance = unit.BodyBlockSize + targetUnit.BodyBlockSize; max_distance = min_distance;//Math.Max(min_distance, unit.BodyBlockSize + targetUnit.BodyHitSize); bool needCheck = true; if (expect_skill == null) { var new_skill = unit.getRandomLaunchableExpectSkill(targetUnit, expectTarget, AttackReason.Tracing, true); if (new_skill != null) { ExpectSkillState = new_skill; expect_skill = new_skill; needCheck = false; } } if (expect_skill != null) { if (can_random_skill) { if (needCheck == true && !expect_skill.checkTargetRange(targetUnit)) { var new_skill = unit.getRandomLaunchableExpectSkill(targetUnit, expectTarget, AttackReason.Tracing, true); if (new_skill != null) { ExpectSkillState = new_skill; expect_skill = new_skill; } } } float skill_distance = unit.GetSkillAttackRange(expect_skill.Data); float half = (skill_distance - min_distance) / 2; if (expect_skill.Data.AttackKeepRange > 0 && expect_skill.Data.AttackRange > expect_skill.Data.AttackKeepRange) { min_distance = Math.Max(min_distance, unit.GetSkillAttackRange(expect_skill.Data.AttackKeepRange) + targetUnit.BodyBlockSize); max_distance = Math.Max(max_distance, min_distance); } else if (half > 0) { min_distance = min_distance + half; max_distance = Math.Max(max_distance, min_distance); } else { min_distance = Math.Max(min_distance, skill_distance + targetUnit.BodyBlockSize); max_distance = Math.Max(max_distance, min_distance); } max_distance = Math.Max(max_distance, skill_distance + targetUnit.BodyHitSize); } } protected override bool CheckTargetInMaxRange() { return CMath.includeRoundPoint(unit.X, unit.Y, max_distance, targetUnit.X, targetUnit.Y); } protected override bool CheckTargetInMinRange() { return CMath.includeRoundPoint(unit.X, unit.Y, min_distance, targetUnit.X, targetUnit.Y); } protected virtual bool OnEndAdjustKeepRange(InstanceUnit.StateMove m) { unit.faceTo(Target.X, Target.Y); unit.changeState(this); return true; } protected virtual SkillTemplate TryLaunchSkill() { if (launchSkill == null) { this.launchSkill = unit.getRandomLaunchableExpectSkill(expectTarget); } InstanceUnit.StateSkill ret = null; Vector2 targetPos = targetUnit == null ? null : new Vector2(targetUnit.X, targetUnit.Y); if (launchSkill != null) { ret = unit.launchSkill(launchSkill.ID, new InstanceUnit.LaunchSkillParam(targetUnit.ID, targetPos, can_auto_focus_near_target, false, 0, true, true)); if (ret != null) { return ret.SkillData; } } if (can_random_skill) { ret = unit.launchRandomSkill(expectTarget, new InstanceUnit.LaunchSkillParam(targetUnit.ID, targetPos, can_auto_focus_near_target, false, 0, true, true)); if (ret != null) { return ret.SkillData; } } return null; } public void ChangeAttackTarget(InstanceUnit.SkillState expectSkill, InstanceUnit target) { TargetUnit = target; ExpectSkillState = expectSkill; ChangeMoveTarget(target); } } /// /// 嘲讽状态 /// public class XmdsInstanceUnitStateMock : XmdsStateFollow, IStateNoneControllable { private InstanceUnit targetUnit; // 是否能攻击 private bool can_attack = true; //标记失效 public bool mMarkRemove = false; private float max_distance; public override bool IsActive { get { return targetUnit.IsActive && !mMarkRemove; } } public XmdsInstanceUnitStateMock(InstanceUnit unit, InstanceUnit targetUnit) : base(unit, targetUnit, false) { this.targetUnit = targetUnit; this.StartMoveHoldTimeMS = zone.Templates.CFG.AI_FOLLOW_AND_ATTACK_HOLD_TIME_MS; this.max_distance = unit.DefaultSkill.AttackRange - targetUnit.BodyBlockSize; } public InstanceUnit GetTargetUnit() { return this.targetUnit; } protected override void onStart() { unit.SetActionStatus(UnitActionStatus.ServerSyncMove); //unit.SendForceSync(); base.onStart(); } protected override void onStop() { unit.doSomething(); base.onStop(); } public override bool onBlock(InstanceUnit.State new_state) { if (!IsActive || (unit.IsDead() && new_state is StateDead)) { return true; } if (new_state is XmdsInstanceUnitStateMock) { return true; } else if (new_state is StateSkill) { StateSkill ss = new_state as StateSkill; // 反击或者状态解除// if (ss.Skill.GetSkillType() == XmdsSkillType.normalAtk) { return true; } } return false; } protected override void onUpdateFollowed(IPositionObject target) { this.can_attack = zone.IsAttackable(unit, targetUnit, SkillTemplate.CastTarget.Enemy, AttackReason.Tracing, this.unit.DefaultSkill); if (can_attack) { unit.faceTo(targetUnit.X, targetUnit.Y); unit.launchSkill(this.unit.DefaultSkillStatus(), new InstanceUnit.LaunchSkillParam(targetUnit.ID)); } } protected override bool CheckTargetInMinRange() { return CMath.includeRoundPoint(unit.X, unit.Y, max_distance, targetUnit.X, targetUnit.Y); } protected override bool CheckTargetInMaxRange() { return CMath.includeRoundPoint(unit.X, unit.Y, max_distance, targetUnit.X, targetUnit.Y); } public State AsState() { return this; } } } }