using System; using System.Collections.Generic; using System.Text; using CommonAI.RTS.Manhattan; using CommonAI.RTS; using CommonLang.Vector; using CommonLang; using CommonAI.ZoneClient; using CommonAI.Zone.Helper; using CommonAI.Zone.Formula; using CommonAI.Zone.Attributes; namespace CommonAI.Zone.Instance { //-------------------------------------------------------------------------------------------------------- /// /// 所有自动电脑AI /// public class InstanceGuard : InstanceUnit, IGuardUnit, ViewTrigger.ViewTriggerListener { protected ViewTrigger mViewTrigger; protected HateSystem mHateSystem; protected StateAttackTo mRunningPath; protected StateFollowAndAttack mTracingTarget; protected StateFollowAndGuard mGuardTarget; protected StateBackToPosition mBackToPosition; protected Vector2 mOrginPosition; //protected TimeInterval mCheckInGuardLimit; protected SimpleTrigger mNextCheckGuardLimit; public InstanceUnit TracingTarget { get { if (mTracingTarget != null) return mTracingTarget.TargetUnit; return null; } } public InstanceGuard(InstanceZone zone, UnitInfo info, string name, int force, int level) : base(zone, info, name, force, level) { //this.mCheckInGuardLimit = new TimeInterval(Templates.CFG.AI_NPC_CHECK_IN_GUARD_LIMIT_TIME_MS); this.mNextCheckGuardLimit = new SimpleTrigger(Templates.CFG.AI_NPC_CHECK_IN_GUARD_LIMIT_TIME_MS); this.OnActivated += this.onUnitActivated; } protected override void onAdded(bool pointLv) { base.onAdded(pointLv); this.mHateSystem = TemplateManager.Factory.CreateHateSystem(this); this.mOrginPosition = new Vector2(X, Y); } protected override void Disposing() { base.Disposing(); if (mViewTrigger != null) mViewTrigger.Dispose(); mHateSystem.Clear(); mRunningPath = null; mTracingTarget = null; mGuardTarget = null; mBackToPosition = null; mOrginPosition = null; this.mNextCheckGuardLimit = null; this.OnActivated -= this.onUnitActivated; } override protected void onResetAI() { mTracingTarget = null; mHateSystem.Clear(); doSomething(); } protected override void onDead(InstanceUnit killer) { base.onDead(killer); SetEnableView(false); } //-------------------------------------------------------------------------------------------------------- #region View public void SetEnableView(bool view) { if (mViewTrigger != null) { this.mViewTrigger.Enable = view && !IsNature && !IsNoneSkill; } } private void initViewTrigger() { this.mViewTrigger = CreateViewTrigger(Parent); if (mViewTrigger != null) { this.mViewTrigger.setListener(this); } } protected virtual ViewTrigger CreateViewTrigger(InstanceZone zone) { if (Info.GuardRange > 0) { return new ViewTriggerRoundBody(zone, Info.GuardRange); } else { return new ViewTriggerBlind(zone); } } void ViewTrigger.ViewTriggerListener.onObjectEnterView(ViewTrigger src, InstanceZoneObject obj) { onAddEnemy(obj as InstanceUnit, true, AttackReason.Look); } void ViewTrigger.ViewTriggerListener.onObjectLeaveView(ViewTrigger src, InstanceZoneObject obj) { } bool ViewTrigger.ViewTriggerListener.select(ViewTrigger src, InstanceZoneObject obj) { if (obj == this) { return false; } else if ((obj is InstanceUnit)) { InstanceUnit u = obj as InstanceUnit; if (!u.IsNature && !this.IsNature && Parent.IsAttackable(this, u, SkillTemplate.CastTarget.Enemy, AttackReason.Look, Info)) { return true; } } return false; } #endregion //-------------------------------------------------------------------------------------------------------- #region Action void IGuardUnit.AttackTo(ZoneWayPoint start, int peaceTime) { this.attackTo(start, peaceTime); } void IGuardUnit.SetOrginPosition(float x, float y) { this.setOrginPosition(x, y); } void IGuardUnit.PatrolWith(ZoneWayPoint start, int holdMinTimeMS, int holdMaxTimeMS, int peaceTime) { this.patrolWith(start, holdMinTimeMS, holdMaxTimeMS, peaceTime); } bool IGuardUnit.FollowAndAttack(InstanceUnit target, AttackReason reason) { return this.followAndAttack(target, reason); } void IGuardUnit.GuardUnit(InstanceUnit vip) { this.guardUnit(vip); } public override void doSomething() { if (CurrentState is StateSkill) { var target = TracingTarget; if (target != null) { if (tryMoveScatterTarget(target)) { return; } } } guard(); } /// /// 设置警戒地点,如果在寻路或保护单位,则无效 /// /// /// public virtual void setOrginPosition(float x, float y) { this.mOrginPosition.SetX(x); this.mOrginPosition.SetY(y); } public virtual Vector2 GetOrginPosition() { return mOrginPosition; } /// /// 寻路攻击目标 /// /// /// public virtual bool followAndAttack(InstanceUnit src, AttackReason reason) { if (IsNoneSkill) return false; if ((src != null)) { if (Parent.IsAttackable(this, src, SkillTemplate.CastTarget.Enemy, reason, Info)) { this.SetEnableView(false); mHateSystem.Add(src); if (TracingTarget != src) { //Console.WriteLine(" - - BBBBBB - - " + ((TracingTarget == null) ? "null" : TracingTarget.Name) + ", " + src.Name); mTracingTarget = new StateFollowAndAttack(this, src); } changeState(mTracingTarget); return true; } else { mHateSystem.Remove(src); } } return false; } /// /// 寻路并一路警戒 /// /// public virtual void attackTo(ZoneWayPoint path, int peaceTime = 0) { this.mRunningPath = new StateAttackToZoneWayPoint(this, path, peaceTime); changeState(this.mRunningPath); } /// /// 在路点范围内警戒 /// /// /// /// public virtual void patrolWith(ZoneWayPoint wp, int holdMinTimeMS, int holdMaxTimeMS, int peaceTime = 0) { this.mRunningPath = new StatePatrolWayPoint(this, wp, holdMinTimeMS, holdMaxTimeMS, peaceTime); changeState(this.mRunningPath); } /// /// 保护单位 /// /// public virtual void guardUnit(InstanceUnit vip) { if (mGuardTarget == null || !mGuardTarget.IsActive || mGuardTarget.TargetUnit != vip) { mGuardTarget = new StateFollowAndGuard(this, vip, this.BodyBlockSize * 2 + vip.BodyBlockSize, Info.GuardRange); } changeState(mGuardTarget); } /// /// 待机 /// public virtual void guard() { if(mHateSystem != null && mHateSystem.Count > 0) { if (followAndAttack(mHateSystem.GetHated(), AttackReason.Tracing)) { return; } } mTracingTarget = null; SetEnableView(true); if (mGuardTarget != null && mGuardTarget.IsActive) { changeState(this.mGuardTarget); return; } if (mRunningPath != null && !mRunningPath.IsDone) { changeState(this.mRunningPath); return; } if (Moveable && MoveSpeedSEC > 0) { guardInPosition(mOrginPosition); return; } base.startIdle(); } public virtual void guardInPosition(Vector2 pos) { changeState(new StateGuardInPosition(this, pos)); } /// /// 立刻开始返回原点 /// public virtual void backToOrgin() { mHateSystem.Clear(); mTracingTarget = null; if (mGuardTarget != null && mGuardTarget.IsActive) { changeState(this.mGuardTarget); } else { mBackToPosition = new StateBackToPosition(this, mOrginPosition); changeState(mBackToPosition); } OnBackToOrgin(); } /// /// 在一定范围内浪 /// /// 浪多久 /// 浪多远 public virtual void idleMove(int timeMS, int range) { changeState(new StateIdleMove(this, mOrginPosition, timeMS, range)); } #endregion //-------------------------------------------------------------------------------------------------------- #region Update protected override void onUpdateRecover() { if (mTracingTarget == null) { base.onUpdateRecover(); } } override protected void onUpdate(bool slowRefresh) { base.onUpdate(slowRefresh); updateTracingTarget(); updateRunningPath(); updateGuardTarget(); updateBackToOrgin(); //updateHate(); updateView(); } protected virtual void updateTracingTarget() { if (mTracingTarget != null) { if (Parent.IsAttackable(this, mTracingTarget.TargetUnit, SkillTemplate.CastTarget.Enemy, AttackReason.Tracing, Info)) { //有攻击目标// if ((CurrentState is StateSkill)) { tryLaunchRandomSkillAndCancelCurrentSkill(TracingTarget, false); } else if (CurrentState is StateIdle) { tryMoveScatterTarget(TracingTarget); } } else { mHateSystem.Remove(mTracingTarget.TargetUnit); if (mTracingTarget == CurrentState) { doSomething(); } mTracingTarget = null; } } } protected virtual void updateRunningPath() { if (mRunningPath != null) { if (CurrentState == mRunningPath) { if (mRunningPath.IsDone) { mRunningPath = null; } else if (mRunningPath.Target != null) { mOrginPosition.SetX(this.X); mOrginPosition.SetY(this.Y); } } else if (CurrentState is StateIdle) { changeState(this.mRunningPath); } } } protected virtual void updateGuardTarget() { //如果在寻路或保护,则实时更新OrginPosition// if (mGuardTarget != null) { if (!mGuardTarget.IsActive) { mGuardTarget = null; } else { mOrginPosition.SetX(mGuardTarget.TargetUnit.X); mOrginPosition.SetY(mGuardTarget.TargetUnit.Y); } } } protected virtual void updateBackToOrgin() { if (mBackToPosition != null) { if (mBackToPosition.IsDone) { mBackToPosition = null; } } if (Info.GuardRangeLimit > Info.GuardRange) { //if (mCheckInGuardLimit.Update(Parent.UpdateIntervalMS)) if(mNextCheckGuardLimit != null && mNextCheckGuardLimit.IsTrigger()) { if (!CMath.includeRoundPoint(X, Y, Info.GuardRangeLimit, mOrginPosition.X, mOrginPosition.Y)) { backToOrgin(); } else if (mTracingTarget != null) { if (!mTracingTarget.IsActive || !CMath.intersectRound( X, Y, Info.GuardRangeLimit, mTracingTarget.TargetUnit.X, mTracingTarget.TargetUnit.Y, mTracingTarget.TargetUnit.BodyHitSize)) { backToOrgin(); return; } } } } } protected virtual void updateView() { if (mViewTrigger != null) { mViewTrigger.onLookUpdate(X, Y); } } //protected virtual void updateHate(bool slowRefresh, int intervalMS) //{ // if(slowRefresh && mHateSystem != null) // { // mHateSystem.Update(intervalMS); // } //} /// /// 攻击间歇,尝试换个位置,避免怪物堆在一个点 /// protected virtual bool tryMoveScatterTarget(InstanceUnit target) { //只有单位为非碰撞时,才有这个需求// if (!this.IntersectObj) { if (CommonLang.CUtils.localTimeMS > mCheckAttackSpread && CUtils.RandomPercent(Parent.RandomN, Templates.CFG.AI_NPC_ATTACK_IDLE_SCATTER_PCT)) { mCheckAttackSpread = CommonLang.CUtils.localTimeMS + Templates.CFG.AI_NPC_ATTACK_IDLE_INTERVAL; InstanceUnit block = null; Parent.ForEachNearObjects(X, Y, (InstanceZoneObject o, ref bool cancel) => { if ((o != this) && (o is InstanceUnit) && Parent.TouchObject2(this, o)) { block = o as InstanceUnit; cancel = true; } }); if (block != null) { float degree = MathVector.getDegree(X, Y, target.X, target.Y); float distance = this.BodyBlockSize + block.BodyBlockSize; Vector2 turnL = new Vector2(X, Y); Vector2 turnR = new Vector2(X, Y); MathVector.movePolar(turnL, degree + CMath.PI_DIV_2, distance); MathVector.movePolar(turnR, degree - CMath.PI_DIV_2, distance); float dl = MathVector.getDistanceSquare(turnL.X, turnL.Y, target.X, target.Y); float dr = MathVector.getDistanceSquare(turnR.X, turnR.Y, target.X, target.Y); if (dl < dr) { this.startMoveTo(turnL.X, turnL.Y); } else { this.startMoveTo(turnR.X, turnR.Y); } return true; } } } return false; } #endregion //-------------------------------------------------------------------------------------------------------- #region InternalEvents protected virtual void onUnitActivated(InstanceUnit unit) { initViewTrigger(); } protected override void onStateChanged(State old_state, State state) { if (state is StateIdle) { followAndAttack(mHateSystem.GetHated(), AttackReason.Tracing); } if (old_state != null) { if (old_state == mBackToPosition) { mBackToPosition = null; } if (old_state == mTracingTarget) { if (mTracingTarget.IsNoWay) { mHateSystem.Remove(mTracingTarget.TargetUnit); mTracingTarget = null; } } } } protected override void onMoveBlockWithObject(InstanceZoneObject obj) { if (IsNoneSkill) { return; } if (obj is InstanceUnit) { followAndAttack(obj as InstanceUnit, AttackReason.MoveBlocked); } } protected override bool onBlockOther(InstanceZoneObject obj) { if (CurrentActionStatus == UnitActionStatus.Move) { return true; } if (CurrentActionStatus == UnitActionStatus.Idle) { //给自己友军让路// if (obj is InstanceUnit) { InstanceUnit u = obj as InstanceUnit; if (u.Force == this.Force) { return changeState(new StateMoveAway(this, u)); } } } return false; } protected override void onDamaged(InstanceUnit attacker, AttackSource attack, int reduceHP) { if (IsNoneSkill) { return; } if(attacker.IsPet) { InstanceUnit master = attacker.Virtual.GetMasterUnit(); if(master == null) { log.Warn("onDamaged: 找不到宠物主人:" + attacker.Info.ID + ", " + attacker.Parent.GetSceneID()); } else { attacker = master; } } // 被攻击转火 mHateSystem.OnHitted(attacker, attack, reduceHP); onAddEnemy(attacker, true, AttackReason.Damaged); } protected virtual void onAddEnemy(InstanceUnit target, bool group, AttackReason reason) { if (onAddEnemyInternal(target, group, reason)) { if (group && Info.GuardRangeGroup > 0) { Parent.ForEachNearObjects(X, Y, Info.GuardRangeGroup, (InstanceZoneObject o, ref bool cancel) => { if ((o != this) && (o is InstanceGuard)) { InstanceGuard g = o as InstanceGuard; if (g.Force == this.Force && Parent.IsAttackable(g, target, SkillTemplate.CastTarget.Enemy, AttackReason.Look, Info)) { if (CMath.includeRoundPoint(g.X, g.Y, g.Info.GuardRangeLimit, target.X, target.Y)) { g.onAddEnemyInternal(target, true, reason); } } } }); } } } protected bool onAddEnemyInternal(InstanceUnit target, bool group, AttackReason reason) { bool attack = true; if (mOnEnemyAdded != null) { mOnEnemyAdded.Invoke(this, target, ref attack); } if (attack) { if (!mHateSystem.Contains(target)) { mHateSystem.Add(target); } followAndAttack(mHateSystem.GetHated(), reason); } return attack; } protected virtual void OnBackToOrgin() { } #endregion //-------------------------------------------------------------------------------------------------------- #region Events protected override void clearEvents() { base.clearEvents(); mOnEnemyAdded = null; } public delegate void EnemyAdded(InstanceUnit unit, InstanceUnit enemy, ref bool attack); private EnemyAdded mOnEnemyAdded; [EventTriggerDescAttribute("Add敌人")] public event EnemyAdded OnEnemyAdded { add { mOnEnemyAdded += value; } remove { mOnEnemyAdded -= value; } } #endregion //-------------------------------------------------------------------------------------------------------- /// /// 移动状态 /// public class StateMoveAway : State { private Vector2 target; private InstanceUnit other; public StateMoveAway(InstanceGuard unit, InstanceUnit other) : base(unit) { this.other = other; this.target = new Vector2(unit.X, unit.Y); float angle = MathVector.getDegree(other.X, other.Y, unit.X, unit.Y); angle = (float)(-CMath.PI_DIV_2 + unit.RandomN.NextDouble() * CMath.PI_F); MathVector.movePolar(target, angle, (unit.BodyBlockSize)); } override public bool onBlock(State new_state) { return true; } override protected void onStart() { unit.SetActionStatus(UnitActionStatus.Move); } override protected void onUpdate() { unit.SetActionStatus(UnitActionStatus.Move); unit.faceTo(target.X, target.Y); MoveBlockResult result = unit.moveBlockTo(target.X, target.Y, unit.MoveSpeedSEC, zone.UpdateIntervalMS); if ((result.result & MoveResult.RESULTS_MOVE_END) != 0) { InstanceGuard guard = (unit as InstanceGuard); guard.doSomething(); } } override protected void onStop() { } } //---------------------------------------------------------------------------------------------------------- } //-------------------------------------------------------------------------------------------------------- public class InstanceSummon : InstanceGuard, ISummonedUnit { public InstanceUnit SummonerUnit { get; set; } public InstanceSummon(InstanceZone zone, UnitInfo info, string name, int force, int level) : base(zone, info, name, force, level) { } override protected void onUpdate(bool slowRefresh) { base.onUpdate(slowRefresh); if ((PassTimeMS >= Info.LifeTimeMS) || (SummonerUnit != null && SummonerUnit.IsDead())) { kill(); } } } //-------------------------------------------------------------------------------------------------------- }