#define OPNE_JSG_AI_TOOLS using CommonAI.Zone; using CommonAI.Zone.Helper; using CommonAI.Zone.Instance; using System; using System.Collections.Generic; using System.Linq; using System.Text; using XmdsCommon.Plugin; using CommonAI.Zone.Formula; using XmdsCommon.Message; using CommonLang; using XmdsCommonServer.Message; using CommonLang.Vector; using XmdsCommon.EditorData; using CommonAI.Zone.ZoneEditor; using CommonLang.Concurrent; using static CommonAI.Zone.Instance.InstanceUnit; using XmdsCommon.ZoneClient.XmdsClientVirtual; using XmdsCommonServer.Plugin.Scene; using static CommonAI.XmdsConstConfig; using System.Collections; using CommonLang.Log; using CommonAI.Data; using static XmdsCommonServer.Plugin.Units.XmdsInstanceUnitStateMachine; using CommonAI; namespace XmdsCommonServer.Plugin.Units { public class XmdsInstancePlayer : InstancePlayer { public enum GuardMode { Mode_Point, Mode_Map, } private static HashMap CreateInfoMap = new HashMap(); //嘲讽状态 protected XmdsInstanceUnitStateMock mMockTarget; private TimeInterval mFindItemTimeInterval; private long mNextAutoFindPickItemTime; private GuardMode mCurGuardMode = GuardMode.Mode_Point; private Vector2 mGuardPos = new Vector2(); private bool mDoStopSkill = false; private static int DEFAULT_CROSS_SERVER_ID = -999; private const char SPLIT = ':'; private List mAutoGuardTargetList = new List(); private int mAutoGuardTargetTimes = 0; private const int MAX_AUTO_GUARD_TARGET_TIMES = 500; private bool mIsReady = false; private XmdsInstanceUnitStateMachine.XmdsStateFollowAndAttack mXmdsFocusTarget; private bool mBotTestAutoFight = false; private int zoneID; //解决部分情况下未上发(或者上发时机不对)idle,导致别人看到自己在move,唯独自己看到自己是正常的 private int mNotMoveTimes; private Vector2 mLastMovePos = new Vector2(); //定点自动战斗情况下,玩家不能像怪物一样回原地,所以需要定时检测一下,重置定点的位置 private long mLastAutoAttackTime = 0; //private Vector2 mLastAutoFightPos = new Vector2(); //下一次通知背包已满时间 //private long mNextNotifyBagFullTime = 0; public XmdsInstancePlayer(InstanceZone zone, UnitInfo info, string name, int force, int alliesForce, int level) : base(zone, info, name, force, level, alliesForce) { mFindItemTimeInterval = new TimeInterval(XmdsConfig.Instance.GUARD_FIND_ITEM_CYCLE_TIME); mCheckGuard.FirstTimeEnable = true; if (TemplateManager.IsEditor) { mIsReady = true; mBotTestAutoFight = true; } else if ((Info.Properties as XmdsUnitProperties).BotTestTemplate) { mIsReady = true; } OnPlayerCreate(); this.OnHandleAction += OnActionHandle; } static void OnActionHandle(InstanceUnit unit, ObjectAction act) { if (act is PlayerDramaEndAction && unit.AoiStatus != null) { var dramaId = (act as PlayerDramaEndAction).dramaId; if (unit.AoiStatus is XmdsPlayerAOI) { if ((unit.AoiStatus as XmdsPlayerAOI).QuestId == dramaId) { unit.setAoiStatus(null); } return; } if (unit.AoiStatus is DarmaAOI) { if ((unit.AoiStatus as DarmaAOI).darmaId == dramaId) { unit.setAoiStatus(null); } return; } } } private void clearAutoGuardTargetList() { if (mAutoGuardTargetList != null) { mAutoGuardTargetList.Clear(); } mAutoGuardTargetTimes = 0; } protected override void Disposing() { foreach (var item in mQuestScript) { item.Value.Dispose(); } clearAutoGuardTargetList(); mXmdsFocusTarget = null; lastAttackUnit = null; base.Disposing(); } private System.Action readyAction; public void PlayerReady() { mIsReady = true; if (readyAction != null) { readyAction.Invoke(); } } public bool IsPlayerReady() { return mIsReady; } //-------------------------------------------------------------------------------------- public override bool IsVisible { get { return base.IsVisible && mIsReady; } } public override bool IsInvincible { get { return (!mIsReady || base.IsInvincible); } } public XmdsVirtual_Player VirtualPlayer { get { return base.Virtual as XmdsVirtual_Player; } } public bool CanPickDropableItem { get { return VirtualPlayer.VirtualInventorySize >= 1; } } /// /// 是否是机器人 /// //public bool IsBotTest { get; private set; } //-------------------------------------------------------------------------------------- #region 任务相关 private HashMap mQuestScript = new HashMap(); public Quest.QuestScript GetQuestScript(string quest_id) { return mQuestScript.Get(quest_id); } public IList GetAllQuestScript() { return new List(mQuestScript.Values); } public void SetQuestScript(string quest_id, int questType, Quest.QuestScript script) { if (mQuestScript.ContainsKey(quest_id)) { XmdsVirtual.FormatLog(CommonLang.Log.LoggerLevel.ERROR, "SetQuestScript Error : ContainsKey" + quest_id); RemoveQuestScript(quest_id); } mQuestScript.Add(quest_id, script); script.Init(quest_id, questType, this); script.Start(); } public void RemoveQuestScript(string quest_id) { Quest.QuestScript qs = mQuestScript.Get(quest_id); if (qs != null) { qs.Dispose(); mQuestScript.Remove(quest_id); } } public List GetQuests() { return new List(mQuests.Values); } #endregion //-------------------------------------------------------------------------------------- #region 作弊惩罚 private TimeExpire mLockTime; public bool IsLock { get { return mLockTime != null; } } //索敌范围. private int mGuardSearchRange = XmdsConfig.Instance.NORMALSCENE_AUTO_GUARD_RANGE; /// /// 设置自动战斗索敌范围. /// /// public void SetGuardSearchRange(int v) { mGuardSearchRange = v; } /// /// 检测到作弊,禁止一切操作一段时间 /// /// public void Lock(int timeMS) { this.startIdle(); if (mLockTime != null && mLockTime.ExpireTimeMS >= timeMS) { return; } else { mLockTime = new TimeExpire(timeMS); } } private void upateLock() { if (mLockTime != null && mLockTime.Update(Parent.UpdateIntervalMS)) { mLockTime = null; } } #endregion //-------------------------------------------------------------------------------------- #region BUFF特殊状态锁定移动. private TimeExpire mBuffLockMoveTime; public bool IsBuffLockMove { get { return mBuffLockMoveTime != null; } } public void BuffMoveLock(int timeMS) { this.startIdle(); if (mBuffLockMoveTime != null && mBuffLockMoveTime.ExpireTimeMS >= timeMS) { return; } else { mBuffLockMoveTime = new TimeExpire(timeMS); } } private void UpdateBuffMoveLock() { if (mBuffLockMoveTime != null && mBuffLockMoveTime.Update(Parent.UpdateIntervalMS)) { mBuffLockMoveTime = null; } } #endregion //-------------------------------------------------------------------------------------- protected override void onUpdate(bool slowRefresh) { base.onUpdate(slowRefresh); this.upateLock(); this.UpdateBuffMoveLock(); this.UpdateDoStopSkill(); this.updateFollowTeam(); this.UpdateAndModifyErrorData(); } //-------------------------------------------------------------------------------------- protected override void onAction(ObjectAction act) { if (IsLock) return; if (act is UnitAxisAction || act is UnitMoveAction) { //BUFF锁定客户端移动. if (IsBuffLockMove == true) { return; } } base.onAction(act); if (this.IsDead() == true) { //单位遗言更改. if (act is PlayerTestamentChangeEventAction) { PlayerTestamentChangeEventAction evt = act as PlayerTestamentChangeEventAction; ChangeTestament(evt.TestamentID); } } //自动战斗状态变更通知. if (act is PlayerAutoGuardEventC2B) { var evt = act as PlayerAutoGuardEventC2B; if (evt.Flag == false) { if (this.CurrentState is StateSkill) { mDoStopSkill = true; } } return; } else if (act is PlayerEnterCrossServerRequestC2B) { //战斗服判断单位是否在PVPV状态,如果不是发送切换场景请求. if ((this.Virtual as XmdsVirtual).CombatState != BattleStatus.PVP) { //发送协议. TransUnitEventB2R evt = new TransUnitEventB2R(); //约定跨服场景ID. evt.SceneID = DEFAULT_CROSS_SERVER_ID; evt.playerId = this.PlayerUUID; evt.TargetX = 0; evt.TargetY = 0; this.queueEvent(evt); } else { (this.Virtual as XmdsVirtual).SendMsgToClient(CommonAI.XmdsConstConfig.TIPS_CAN_NOT_ENTER_SCENE); } } else if (act is GetMonsterSufferDamageInfoC2B) { var ret = act as GetMonsterSufferDamageInfoC2B; OnReceiveGetMonsterSufferDamageInfoC2B(ret); } foreach (var q in mQuestScript) { if (q.Value.TryDoAction(act)) { break; } } } protected override bool doLaunchSkill(UnitLaunchSkillAction sk) { //Console.WriteLine(" ------------------------------doLaunchSkill - " + sk.SkillID + ", " + this.ID + ", targetID: " + // sk.TargetObjID + ", XY:" + this.X + ", " + this.Y + ", direction : " + this.Direction + ", " + this.Parent.UUID); //if(sk.SkillID == 110160) // sk.SkillID = 110360; //如果在采集中,不让放技能,并通知客户端. if (CurrentActionStatus == UnitActionStatus.Pick) { this.VirtualPlayer.SendMsgToClient(CommonAI.XmdsConstConfig.TIPS_BE_BUSY); return false; } var skill = this.getSkillState(sk.SkillID); if (skill == null) { return false; } return base.doLaunchSkill(sk); } public bool PlayerChangeSkill(int oldSkillID, int newSkillID, SkillLevelData levelData) { return this.Virtual.ChangeSkill(oldSkillID, newSkillID, levelData); } // 获取队伍的天命属性加成 public override int GetTeamFateValue(UnitFateType fateType) { if(this.TeamVirtual == null) { return this.Virtual.GetPetTalentAddition(fateType); } return this.TeamVirtual.GetTeamFateValue(fateType); } //查找自动拾取的物品 public override InstanceItem findGuardPickItem() { if (CanPickDropableItem) { return base.findGuardPickItem(); } return null; } //查找自动战斗等一切情况下的物品拾取 public InstanceItem findAutoGuardPickItem() { if (CanPickDropableItem) { InstanceItem min = null; float min_len = float.MaxValue; float searchValue = this.mAutoPickRange * this.mAutoPickRange; Parent.ForEachNearObjects(this.X, this.Y, this.mAutoPickRange, (InstanceItem u, ref bool cancel) => { if (!u.Info.Pickable && u.IsPickable(this)) { float len = MathVector.getDistanceSquare(u.X, u.Y, this.X, this.Y); if (min_len > len && len <= searchValue) { min_len = len; min = u; } } }); return min; } return null; } protected override void doPickObject(UnitPickObjectAction pick) { InstanceZoneObject obj = Parent.getObject(pick.PickableObjectID); if (obj is InstanceItem) { InstanceItem item = obj as InstanceItem; var zip = (item.Info.Properties as XmdsItemProperties); switch (zip.ItemType) { case XmdsItemProperties.XmdsItemType.Equip: //if (CanPickDropableItem) XmdsDropableInstanceItem.IsPickable已经检查 { item.DirectPickItem(this, () => { RemoveDropItemB2C rd = new RemoveDropItemB2C(item.ID, this.ID); this.queueEvent(rd); }); } break; default: { if (item.PickItem(this) && item.mFrom == 1) { RemoveDropItemB2C rd = new RemoveDropItemB2C(item.ID, this.ID); this.queueEvent(rd); } } break; } } else if (obj is InstanceUnit) { InstanceUnit unit = obj as InstanceUnit; this.PickUnit(unit); } } protected void TryDirectPickNearItems() { int size = this.VirtualPlayer.VirtualInventorySize; if (size == 0) { return; } Parent.ForEachNearObjects(this.X, this.Y, this.mAutoPickRange, (InstanceItem u, ref bool cancel) => { if (size > 0) { var itemProp = u.Info.Properties as XmdsItemProperties; if (itemProp.AllowAutoGuardPick == true && u.PassTimeMS > 1000) { //自己可以捡的装备再扣除背包数量. if (u.DirectPickItem(this, () => { RemoveDropItemB2C rd = new RemoveDropItemB2C(u.ID, this.ID); this.queueEvent(rd); })) { size--; } } } else { cancel = true; } }); //this.VirtualPlayer.VirtualInventorySize = size; } public void setMocking(InstanceUnit unit) { if(unit == null) { if(mMockTarget != null) { mMockTarget.mMarkRemove = true; } mMockTarget = null; } else { mMockTarget = new XmdsInstanceUnitStateMock(this, unit); DoMockingAttack(); } } protected override void updateGuard() { if (mMockTarget != null) { DoMockingAttack(); } else if(this.mFocusPickItem != null && this.mFocusPickItem.IsActive) { return; } else if (IsGuard == true && (mXmdsFocusTarget == null || (mXmdsFocusTarget != null && !mXmdsFocusTarget.IsActive))) { mXmdsFocusTarget = null; InstanceUnit enemy = null; if (lastAttackUnit != null && lastAttackUnit.IsActive == true)//上一次攻击的单位如果没死且能攻击,继续攻击他. { //上次攻击的单位如果超过了一屏范围,取消攻击. if (CMath.getDistance(this.X, this.Y, lastAttackUnit.X, lastAttackUnit.Y) > this.mGuardSearchRange) { lastAttackUnit = null; } else { enemy = lastAttackUnit; } } else { lastAttackUnit = null; } if (enemy == null)//如果无效再找一次. { enemy = findGuardTarget(SkillTemplate.CastTarget.Enemy, AttackReason.Look); } if (enemy != null) { doXmdsFocusAttack(enemy, SkillTemplate.CastTarget.Enemy); return; } //UpdateXmdsGuard(); } //自动拾取检测 if(this.DoAutoPick()) { return; } if (mFocusPickItem != null && !mFocusPickItem.IsActive) { mFocusPickItem = null; } if (mFindItemTimeInterval.Update(Parent.UpdateIntervalMS)) { TryDirectPickNearItems(); } if (IsGuard) { // 旋风斩贴近目标 // var state = CurrentState as StateSkill; if (state != null) { Vector2 movePos = (mXmdsFocusTarget == null) ? state.GetMoveToPos() : new Vector2(mXmdsFocusTarget.Target.X, mXmdsFocusTarget.Target.Y); if (movePos != null && state.IsControlMoveable && !state.unit.IsCannotMove && state.SkillData.AutoFightFollower) { if(state.setMoveTo(movePos.X, movePos.Y)) { // 移动结束 if((this.CurrentActionSubstate & (byte)UnitActionSubStatus.ChargeAtkMove) > 0) { this.RemoveActionSubState(UnitActionSubStatus.ChargeAtkMove); this.AddActionSubState(UnitActionSubStatus.ChargeAtkIdle); } } else { // 移动 if ((this.CurrentActionSubstate & (byte)UnitActionSubStatus.ChargeAtkIdle) > 0) { this.RemoveActionSubState(UnitActionSubStatus.ChargeAtkIdle); this.AddActionSubState(UnitActionSubStatus.ChargeAtkMove); } } return; } } // 没目标定期检测目标 // if ((mXmdsFocusTarget == null) && (mFocusPickItem == null)) { if (mCheckGuard.Update(Parent.UpdateIntervalMS)) { var enemy = findGuardTarget(SkillTemplate.CastTarget.Enemy, AttackReason.Look); if (enemy != null) { doXmdsFocusAttack(enemy, SkillTemplate.CastTarget.Enemy); return; } var alias = findGuardTarget(SkillTemplate.CastTarget.AlliesIncludeSelf, AttackReason.Look); if (alias != null) { doXmdsFocusAttack(alias, SkillTemplate.CastTarget.AlliesIncludeSelf); return; } //if (CommonLang.CUtils.localTimeMS - this.mLastAutoAttackTime > 3000/* && this.X == this.mLastAutoFightPos.X && this.Y == this.mLastAutoFightPos.Y*/) //{ // this.mLastAutoAttackTime = CommonLang.CUtils.localTimeMS; // this.mGuardPos.SetX(this.X); // this.mGuardPos.SetY(this.Y); //} } } } } //是否可以自动拾取 public override bool DoAutoPick(bool ignoreInterval = false) { //非自动战斗或者非跟随,退出 if (!this.IsGuard && (this.leadingMan == null || (this.leadingMan != null && !this.canFollowTeam))) { return false; } if(this.VirtualPlayer.VirtualInventorySize <= 0) { //只要自动战斗就提示这个,不合理,客户端自己加 //if(mNextNotifyBagFullTime < CommonLang.CUtils.localTimeMS) //{ // mNextNotifyBagFullTime = CommonLang.CUtils.localTimeMS + 6000; // this.Virtual.SendMsgToClient(CommonAI.XmdsConstConfig.TIPS_BAG_FULL); //} return false; } if (!this.Parent.CheckAutoDropItem() || !IsBattleCanPick()) { return false; } if (mFocusPickItem == null || !mFocusPickItem.IsActive) { bool leaderInView = false; if (this.mTeamVirtual != null && this.canFollowTeam && this.leadingMan != null && this.leadingMan.Enable) { // 如果正在跟随图中,取消拾取 if(this.CurrentState is StateFollowLeader) { StateFollowLeader folloLeader = this.CurrentState as StateFollowLeader; if(folloLeader != null && folloLeader.FollowState == StateFollow.MoveState.Move) { return false; } } if (!CMath.includeRoundPoint(X, Y, this.mAutoPickRange, leadingMan.X, leadingMan.Y)) { return false; } else { leaderInView = true; } } bool canAutoPick = IsGuard || this.CurrentState is StateFollowAndPickItem || leaderInView; if (canAutoPick && (ignoreInterval || mNextAutoFindPickItemTime < CommonLang.CUtils.localTimeMS)) { var item = findAutoGuardPickItem(); if (item != null) { mNextAutoFindPickItemTime = CommonLang.CUtils.localTimeMS + XmdsConfig.Instance.GUARD_FIND_ITEM_CYCLE_TIME; doFocusPickItem(item, false); return true; } } } else { return true; } return false; } //是否跟随队长 public override bool IsFollowMaster() { if(this.leadingMan != null && this.canFollowTeam) { return true; } return false; } private bool IsBattleCanPick() { if(this.Virtual.GetBattleStatus() != BattleStatus.None) { // 不主动开战 if(this.mXmdsFocusTarget != null && this.mXmdsFocusTarget.TargetUnit.IsActive && !this.mXmdsFocusTarget.TargetUnit.IsFullHP()) { return false; } } return true; } //通知拾取消息 public override void NotifyRemoveDropItemB2C(InstanceItem item) { RemoveDropItemB2C rd = new RemoveDropItemB2C(item.ID, this.ID); this.queueEvent(rd); } private void DoMockingAttack() { //doXmdsFocusAttack(mMockTarget.GetTargetUnit(), SkillTemplate.CastTarget.Enemy, false, this.DefaultSkillStatus()); if (!(this.CurrentState is XmdsInstanceUnitStateMock) || !(this.CurrentState is StateSkill)) { changeState(mMockTarget); queueEvent(new PlayerFocuseTargetEvent(this.ID, mMockTarget.GetTargetUnit().ID, SkillTemplate.CastTarget.Enemy)); ; } } public override BuffState AddBuff(BuffTemplate buff, InstanceUnit sender, int pointMaxOverLayer = 0, bool forever = false, bool bMaxOverlayer = false, int buffExt = 0, bool isControlBuf = false, bool maxReset = false, int addLayers = 0) { XmdsBuffProperties prop = buff.Properties as XmdsBuffProperties; if (prop.BuffAbilityList != null) { int count = prop.BuffAbilityList.Count; for (int i = 0; i < count; i++) { if (XmdsInstanceUtils.IsDebuff(prop.BuffAbilityList[i].ability)) { isControlBuf = true; break; } } } return base.AddBuff(buff, sender, pointMaxOverLayer, forever, bMaxOverlayer, buffExt, isControlBuf, maxReset, addLayers); } protected override void doHitAttack(InstanceUnit attacker, AttackSource source) { if (this.IsDead()) { //XmdsAttackProperties zap = (source.Attack.Properties as XmdsAttackProperties); if (attacker.CurrentState is StateSkill) { var skilltemplate = (attacker.CurrentState as StateSkill).SkillData; if ((skilltemplate.Properties as XmdsSkillProperties).TargetType == XmdsSkillProperties.XmdsSkillTargetType.AllianceDead) { TemplateManager.Formula.OnHit(attacker, source, this); } } } base.doHitAttack(attacker, source); } /// /// 更改遗言. /// /// private void ChangeTestament(byte testamentID) { (this.Virtual as XmdsVirtual_Player).SyncTestament(testamentID); } public override void InitSkills(LaunchSkill baseSkill, params LaunchSkill[] skills) { if (this.Virtual == null || (this.Virtual as XmdsVirtual).IsFinishSkillInit() == false) { return; } base.InitSkills(baseSkill, skills); } public override bool tryLaunchRandomSkillAndCancelCurrentSkill(InstanceUnit target, bool autoFocusNearTarget = false) { //自动战斗不允许中断当前技能施放. return false; } private void UpdateDoStopSkill() { if (this.CurrentState is StateSkill) { if (mDoStopSkill) { var ss = (this.CurrentState as StateSkill); if (ss.IsCancelableByMove || ss.IsCancelableBySkill) { ss.block(); mDoStopSkill = false; } } } else { mDoStopSkill = false; } } protected override void doGuard(UnitGuardAction act) { //没有通关不能自动战斗. if (act.guard == true && this.Virtual != null && (this.Virtual as XmdsVirtual_Player).AllowUseAutoGuard() == false) { // 机器人除外. if (IsRobot) { return; } } cleanFocus(); lastAttackUnit = null; XmdsVirtual.FormatLog(CommonLang.Log.LoggerLevel.DEBUG, "act.reason:" + act.reason); if (act.guard == false) { // mAutoGuardTargetList.Clear(); } else { try { ParseGuardActionReason(act.reason); } catch (Exception error) { XmdsVirtual.FormatLog(CommonLang.Log.LoggerLevel.ERROR, "ParseGuardActionReason Error : " + act.reason + " error : " + error.ToString()); } } base.doGuard(act); } /// /// 解析挂机指令,新增指令头,告知是定点挂机还是全图游走. /// 第0位 map/point: /// 第1位 怪物模板ID: /// EG: map:怪物模板ID:怪物模板ID /// /// private void ParseGuardActionReason(string reason) { if (mCurGuardMode == GuardMode.Mode_Point) { mGuardPos.SetX(this.X); mGuardPos.SetY(this.Y); } # if OPNE_JSG_AI_TOOLS if (this.IsRobot) { this.mGuardSearchRange = 100; reason = CommonAI.XmdsConstConfig.AUTO_GUARD_MODE_MAP; } #endif if (string.IsNullOrEmpty(reason)) { return; } string[] array = reason.Split(SPLIT); if (array != null && array.Length > 0) { string head = array[0]; if (head == CommonAI.XmdsConstConfig.AUTO_GUARD_MODE_MAP) //全图模式. { mCurGuardMode = GuardMode.Mode_Map; } else if (head == CommonAI.XmdsConstConfig.AUTO_GUARD_MODE_POINT)//定点模式. { mCurGuardMode = GuardMode.Mode_Point; mGuardPos.SetX(this.X); mGuardPos.SetY(this.Y); } //array可能是point: point:0 point:taskId:targetId if (array.Length >= 3) { clearAutoGuardTargetList(); if (!string.IsNullOrEmpty(array[1])) { int taskId = int.Parse(array[1]); for (int i = 2; i < array.Length; i++) { if (!string.IsNullOrEmpty(array[i])) { int targetId = int.Parse(array[i]); if (targetId > 0) { mAutoGuardTargetList.Add(targetId); log.Debug("--------------addTarget id: " + targetId); } } } } } } } private float GetGuardPosX() { switch (mCurGuardMode) { case GuardMode.Mode_Map: return this.X; case GuardMode.Mode_Point: return mGuardPos.X; default: return this.X; } } private float GetGuardPosY() { switch (mCurGuardMode) { case GuardMode.Mode_Map: return this.Y; case GuardMode.Mode_Point: return mGuardPos.Y; default: return this.Y; } } //查找自动攻击单位. public override InstanceUnit findGuardTarget(SkillTemplate.CastTarget expect = SkillTemplate.CastTarget.Enemy, AttackReason reason = AttackReason.Look) { InstanceUnit min = null; if (getAvailableAutoLaunchSkill(expect) != null) { //有优先目标取优先目标攻击. if (mAutoGuardTargetList.Count > 0) { //using (var list = ListObjectPool.AllocAutoRelease()) List list = new List(); { Parent.ForEachNearObjects(GetGuardPosX(), GetGuardPosY(), mGuardSearchRange, (InstanceUnit u, ref bool cancel) => { if (Parent.IsAttackable(this, u, expect, reason, Info)) { XmdsVirtual zv = u.Virtual as XmdsVirtual; if (zv != null) { if (mAutoGuardTargetList.Contains(zv.GetVirtualTemplateID())) { list.Add(u); } } } }); if (list.Count > 0) { list.Sort((a, b) => { float da = CMath.getDistanceSquare(a.X, a.Y, this.X, this.Y); float db = CMath.getDistanceSquare(b.X, b.Y, this.X, this.Y); return (int)(da - db); }); min = list[0]; if (min != null) { mAutoGuardTargetTimes = 0; } } log.Debug("findGuardTarget times: " + mAutoGuardTargetTimes + " count:" + mAutoGuardTargetList.Count + " min:" + (min == null ? "null" : "finded")); //找不到目标怪就直接返回null等下一次查找,直到等待MAX_AUTO_GUARD_TARGET_TIMES次数 if (mAutoGuardTargetList.Count > 0 && min == null) { mAutoGuardTargetTimes += 1; if (mAutoGuardTargetTimes >= MAX_AUTO_GUARD_TARGET_TIMES) { clearAutoGuardTargetList(); } else { return null; } } } } //没有优先目标,按照默认逻辑走. //原本是走父类逻辑,但是现在要分挂机模式,要分索敌范围. //base.findGuardTarget(expect, reason); if (min == null) { if (getAvailableSkill(expect) != null) { bool isMeSteah = this.IsHasSubState(UnitActionSubStatus.Stealth); float min_len = float.MaxValue; Parent.ForEachNearObjects(GetGuardPosX(), GetGuardPosY(), mGuardSearchRange, (InstanceUnit u, ref bool cancel) => { // 别人隐身了,自己没有隐身,自动攻击不到 if(u.Virtual.GetBattleStatus() == BattleStatus.None && u.IsHasSubState(UnitActionSubStatus.Stealth) && !isMeSteah) { return; } if (Parent.IsAttackable(this, u, expect, reason, Info)) { #if OPNE_JSG_AI_TOOLS //降低机器人之间的互相PK概率 if (this.IsRobot && u.IsPlayer) { XmdsInstancePlayer uPlayer = u as XmdsInstancePlayer; if (uPlayer.IsRobot && this.RandomN.Next() % 100 < 95) { return; } } //AI随机攻击目标 if (this.IsRobot && this.RandomN.Next() % 100 < 15) { min = u; cancel = true; return; } #endif float len = MathVector.getDistanceSquare(u.X, u.Y, this.X, this.Y); if (min_len > len) { min_len = len; min = u; } } }); } return min; } } return min; } /// /// 当任务完成时 /// /// protected override void onQuestCommitted(QuestData qd) { int targetId = XmdsQuestData.GetTargetID(qd); if (this.mAutoGuardTargetList.Contains(targetId)) { clearAutoGuardTargetList(); } } public override void OnReconnected(UnitInfo temp, int force, int level, Vector2 enterPos) { (this.Virtual as XmdsVirtual_Player).OnUnitReEnter(temp, force, level, enterPos); base.OnReconnected(temp, force, level, enterPos); } public override void OnDisconnected() { base.OnDisconnected(); var act = new UnitGuardAction(); act.guard = false; doGuard(act); var actFollow = new UnitFollowAction(); actFollow.follow = false; doFollow(actFollow); } protected override void doFollow(UnitFollowAction act) { base.doFollow(act); if (act.follow == true) { if (this.TeamVirtual != null && this.stateFollow != null) { if (this.TeamVirtual.TeamLeader != null && this.TeamVirtual.TeamLeader.Enable) { if (this.TeamVirtual.acceptAutoFollow(this)) { this.TeamVirtual.changeMemberAutoFollow(); this.stateFollow.Distance = this.TeamVirtual.TeamLeader.RadiusSize + (this.BodyBlockSize * (this.FollowIndex + 1)) * 2; //this.changeState(this.stateFollow); } else { this.TeamVirtual.cancelAutoFollow(this); this.TeamVirtual.changeMemberAutoFollow(); } } else if (this.TeamVirtual.TeamLeader == null) { this.TeamVirtual.cancelAutoFollow(this); this.TeamVirtual.changeMemberAutoFollow(); } } } else { if (this.TeamVirtual != null) { this.TeamVirtual.cancelAutoFollow(this); this.TeamVirtual.changeMemberAutoFollow(); } } } public void SendBotTestAutoFight() { if (mBotTestAutoFight == true) { var act = new UnitGuardAction(); act.guard = true; doGuard(act); } } #region 自动释放技能. /// /// 获得当前能够使用的技能. /// /// protected SkillState GetAvailableAutoLaunchSkill(ref InstanceUnit enemy) { SkillState ret = null; do { //没蓝直接转普攻. if (mXmdsFocusTarget == null) { break; } List list = SkillStatus as List; XmdsStateFollowAndAttack current = CurrentState as XmdsStateFollowAndAttack; //优先多段攻击技能 if (current != null) { for (int si = list.Count - 1; si >= 0; --si) { SkillState sst = list[si]; if (sst == null || (DefaultSkill != null && DefaultSkill.ID == sst.Data.ID)) { continue; } if (sst.LaunchSkill.AutoLaunch && sst.IsActive && sst.IsMutilAction() && sst.IsHasNext() && sst.IsCD && sst.IsDone && this.VirtualPlayer.SkillAutoLaunchTest(sst)) { //System.Console.WriteLine(" - GetAvailableAutoLaunchSkill - " + sst.ID + ", " + sst.ActionIndex); ret = sst; break; } } } //其次技能. if (ret != null) { break; } for (int si = list.Count - 1; si >= 0; --si) { SkillState sst = list[si]; //非PVP, PVE状态下不允许释放技能 if (DefaultSkill != null && sst.Data.ID != DefaultSkill.ID && sst.LaunchSkill.AutoLaunch && (Virtual.IsInPVP() || Virtual.IsInPVE())) { if (!sst.IsCD && sst.IsActive && sst.IsDone && sst.LaunchSkill.AutoLaunch && sst.CanAutoLaunch() && this.VirtualPlayer.SkillAutoLaunchTest(sst)) { //判断需要能量值 if (!this.Virtual.LaunchSkillCheckTalentInfo(sst)) { continue; } //切换作用不同的技能,这时需要切换技能目标,会导致丢失原目标. if (mXmdsFocusTarget.ExpectTarget != sst.Data.ExpectTarget) { enemy = findGuardTarget(sst.Data.ExpectTarget, AttackReason.Look); } if (enemy != null) { ret = sst; } break; } } } } while (false); //没有技能时施放普攻. if (ret == null && DefaultSkill != null) { if (enemy != null) { ret = getSkillState(DefaultSkill.ID); } } return ret; } /// /// 判断目标技能是否可以自动释放. /// /// /// //private bool CanAutoLaunchSkill(int skillid) //{ // bool ret = false; // var v = (this.Virtual as XmdsVirtual); // if (v != null) // { // ret = v.AllowAutoLaunch(skillid); // } // return ret; //} public override SkillState getRandomLaunchableExpectSkill(InstanceUnit target, SkillTemplate.CastTarget expectTarget, AttackReason reason = AttackReason.Tracing, bool checkRange = false) { var ss = GetAvailableAutoLaunchSkill(ref target); if (mXmdsFocusTarget != null && target != null && ss != null) { //mXmdsFocusTarget.TargetUnit = target; //mXmdsFocusTarget.ExpectSkillState = ss; //mXmdsFocusTarget.ChangeMoveTarget(target); mXmdsFocusTarget.ChangeAttackTarget(ss, target); } return ss; } private InstanceUnit lastAttackUnit = null; public override SkillState getRandomLaunchableExpectSkill(SkillTemplate.CastTarget expectTarget) { InstanceUnit unit = null; if (mXmdsFocusTarget != null) { unit = mXmdsFocusTarget.TargetUnit; } var ss = GetAvailableAutoLaunchSkill(ref unit); if (mXmdsFocusTarget != null && unit != null && ss != null) { //mXmdsFocusTarget.TargetUnit = unit; //mXmdsFocusTarget.ExpectSkillState = ss; //mXmdsFocusTarget.ChangeMoveTarget(unit); if (ss.Data.ExpectTarget != mXmdsFocusTarget.ExpectTarget) { if (mXmdsFocusTarget.ExpectTarget == SkillTemplate.CastTarget.Enemy) { lastAttackUnit = mXmdsFocusTarget.TargetUnit; } } mXmdsFocusTarget.ChangeAttackTarget(ss, unit); } return ss; } public void OnReceiveGetMonsterSufferDamageInfoC2B(GetMonsterSufferDamageInfoC2B evt) { var ret = evt as GetMonsterSufferDamageInfoC2B; //找到指定的怪物单位,获取伤害信息并排序. InstanceUnit iu = this.Parent.getUnit(ret.MonsterID); if (iu != null && iu is XmdsInstanceMonster) { XmdsVirtual_Monster zvm = (iu as XmdsInstanceMonster).Virtual as XmdsVirtual_Monster; if (zvm != null) { HashMap map = zvm.GetSufferDamage(); if (map != null) { MonsterSufferDamageInfoB2C msg = new MonsterSufferDamageInfoB2C(); List infoList = new List(); int playerRank = 0; int playerDamage = 0; //获取数据并排序. foreach (var kvp in map) { infoList.Add(kvp.Value); } //伤害从大到小排序. infoList.Sort((a, b) => { return b.Damage - a.Damage; }); //找到自己的排名和伤害. for (int i = 0; i < infoList.Count; i++) { var temp = infoList[i]; if (this.PlayerUUID == temp.PlayerUUID) { playerRank = i + 1; playerDamage = temp.Damage; break; } } //只取10条详细信息发送. if (infoList.Count > 10) { infoList.RemoveRange(10, infoList.Count - 10); } msg.PlayerDamage = playerDamage; msg.PlayerRank = playerRank; msg.InfoList = infoList; this.queueEvent(msg); } } } } #endregion #region 绕开底层重新实现自动战斗AI. private XmdsInstanceUnitStateMachine.XmdsStateFollowAndAttack doXmdsFocusAttack(InstanceUnit target, SkillTemplate.CastTarget expect_target = SkillTemplate.CastTarget.Enemy, bool autoFocusNearTarget = true, InstanceUnit.SkillState lanuchSkill = null) { if ((mAttackTo == null || mAttackTo.IsAttack) && Parent.IsAttackable(this, target, expect_target, AttackReason.Tracing, Info)) { this.mLastAutoAttackTime = CommonLang.CUtils.localTimeMS; if (mXmdsFocusTarget != null) { if (mXmdsFocusTarget.TargetUnit != target) { mXmdsFocusTarget = new XmdsInstanceUnitStateMachine.XmdsStateFollowAndAttack(this, target, expect_target, autoFocusNearTarget, lanuchSkill); if (changeState(mXmdsFocusTarget)) { queueEvent(new PlayerFocuseTargetEvent(this.ID, target.ID, expect_target)); } } } else { mXmdsFocusTarget = new XmdsInstanceUnitStateMachine.XmdsStateFollowAndAttack(this, target, expect_target, autoFocusNearTarget, lanuchSkill); if (changeState(mXmdsFocusTarget)) { queueEvent(new PlayerFocuseTargetEvent(this.ID, target.ID, expect_target)); } } } else { mXmdsFocusTarget = null; } return mXmdsFocusTarget; } private void doXmdsFocusAttack(InstanceUnit target, SkillState state) { SkillTemplate.CastTarget expect_target = state.Data.ExpectTarget; if ((mAttackTo == null || mAttackTo.IsAttack) && Parent.IsAttackable(this, target, expect_target, AttackReason.Tracing, Info)) { if (mXmdsFocusTarget != null) { if (mXmdsFocusTarget.TargetUnit != target) { mXmdsFocusTarget = new XmdsInstanceUnitStateMachine.XmdsStateFollowAndAttack(this, target, expect_target, true); mXmdsFocusTarget.ExpectSkillState = state; if (changeState(mXmdsFocusTarget)) { queueEvent(new PlayerFocuseTargetEvent(this.ID, target.ID, expect_target)); } } } else { mXmdsFocusTarget = new XmdsInstanceUnitStateMachine.XmdsStateFollowAndAttack(this, target, expect_target, true); mXmdsFocusTarget.ExpectSkillState = state; if (changeState(mXmdsFocusTarget)) { queueEvent(new PlayerFocuseTargetEvent(this.ID, target.ID, expect_target)); } } } else { mXmdsFocusTarget = null; } } protected override void cleanFocus() { mFocusTarget = null; mXmdsFocusTarget = null; base.cleanFocus(); } protected override void doFocusTarget(UnitFocuseTargetAction focus) { if (IsGuard) { mAttackTo = null; InstanceZoneObject src = Parent.getObject(focus.targetUnitID); //目标不能是宠物 if ((src is InstanceUnit) && !(src is InstancePet)) { if (!IsNoneSkill && ((src as InstanceUnit).IsActive)) { doXmdsFocusAttack(src as InstanceUnit); } } else if ((src is InstanceItem)) { if (src.Enable) { doFocusPickItem(src as InstanceItem); } } else { cleanFocus(); } } } public override void OnUnitDead() { this.cleanFocus(); // 死亡取消自动战斗 if (this.IsGuard) { this.IsGuard = false; this.Virtual.doEvent(JSGCustomOpType.UpdateAutoBattleFlag); } } public override void doSomething() { if (mWaitingSkill != null && CurrentState is StateSkill) { if (doLaunchSkill(mWaitingSkill)) { return; } } else if(this.DoAutoPick()) { return; } else if (IsGuard) { if (this.canFollowTeam && leadingMan != null) { if (leadingMan.AoiStatus != null) { //若队长进入位面 UnitGuardAction action = new UnitGuardAction(); action.guard = false; action.reason = "guard to follow"; doGuard(action); startIdle(); return; } else if (leadingMan.Virtual.GetBattleStatus() == BattleStatus.None) { UnitGuardAction action = new UnitGuardAction(); action.guard = false; action.reason = "guard to follow"; doGuard(action); updateStateToFollowTeam(); mXmdsFocusTarget = null; return; } this.stateFollow.Distance = this.leadingMan.RadiusSize + (this.BodyBlockSize * (this.FollowIndex + 1)) * 2; //var distance = leadingMan.RadiusSize + this.BodyBlockSize * 2; if (!CMath.includeRoundPoint(X, Y, this.GetFollowDistance(true), leadingMan.X, leadingMan.Y)) { UnitGuardAction action = new UnitGuardAction(); action.guard = false; action.reason = "guard to follow"; doGuard(action); updateStateToFollowTeam(); mXmdsFocusTarget = null; return; } } if (mFocusPickItem != null && mFocusPickItem.IsActive) { changeState(mFocusPickItem); return; } else { mFocusPickItem = null; } if (mXmdsFocusTarget != null && mXmdsFocusTarget.IsActive) { if (Parent.IsAttackable(this, mXmdsFocusTarget.TargetUnit, SkillTemplate.CastTarget.AlliesIncludeSelf, AttackReason.Attack, Info)) { mXmdsFocusTarget = null; } else { changeState(mXmdsFocusTarget); return; } } else { mXmdsFocusTarget = null; } if (mAttackTo != null && !mAttackTo.IsDone) { changeState(mAttackTo); return; } } else if (this.canFollowTeam && leadingMan != null) { if (leadingMan.Enable) { if (this.CurrentState != this.stateFollow) { if (leadingMan.AoiStatus != null) { //若队长进入位面 return; } this.stateFollow.Distance = this.leadingMan.RadiusSize + (this.BodyBlockSize * (this.FollowIndex + 1)) * 2; if (!CMath.includeRoundPoint(X, Y, this.GetFollowDistance(false), leadingMan.X, leadingMan.Y)) { this.updateStateToFollowTeam(); mXmdsFocusTarget = null; } else {//组队切场景后,如果队长不移动,队员状态不改变 this.updateStateToFollowTeam(); mXmdsFocusTarget = null; } } else { //发生寻路卡死 //transportToTeamLeader(); } //var distance = leadingMan.RadiusSize + this.BodyBlockSize * 2; //if (!CMath.includeRoundPoint(X, Y, distance, leadingMan.X, leadingMan.Y)) //{ // this.updateStateToFollowTeam(); // mXmdsFocusTarget = null; // return; //} return; } else { //leadingMan忙,切换leadingMan //mTeamVirtual.changeMemberAutoFollow(); } } else if (CurrentState is StateChuanGongA || CurrentState is StateChuanGongB || CurrentState is StateClearYaoQi) { if (CommonLang.CUtils.localTimeMS < chuangongTime) { return; } } else if(CurrentState is StateDaZuo) { return; } else if (CurrentState is StateDaZuoRecoveryAttr) { return; } startIdle(); } #endregion private float GetFollowDistance(bool isGuard) { if (isGuard) { return Templates.CFG.PET_FOLLOW_DISTANCE_MAX; } return this.stateFollow.Distance; } private void OnPlayerCreate() { lock (CreateInfoMap) { AtomicInteger c = null; zoneID = (this.Parent as EditorScene).Data.ID; if (CreateInfoMap.TryGetValue(zoneID, out c)) { c++; } else { c = new AtomicInteger(1); CreateInfoMap.Add(zoneID, c); } } } ~XmdsInstancePlayer() { lock (CreateInfoMap) { AtomicInteger c = null; if (CreateInfoMap.TryGetValue(zoneID, out c)) { c--; } } } //组队跟随 private XmdsTeamVirtual mTeamVirtual; public XmdsTeamVirtual TeamVirtual { set { mTeamVirtual = value; } get { return mTeamVirtual; } } private byte mFollowIndex; public byte FollowIndex { set { mFollowIndex = value; } get { return mFollowIndex; } } protected XmdsInstancePlayer leadingMan; public XmdsInstancePlayer Leader { get { return leadingMan; } set { if (leadingMan != value) { leadingMan = value; if (leadingMan == null) { this.stateFollow = null; } else { if (this.stateFollow == null) { this.stateFollow = new StateFollowLeader(this, leadingMan, 30); } else { this.stateFollow.Follower = leadingMan; } //this.stateFollow = StateFollowTeam.createStateFollowTeam(this, leadingMan); } } } } public bool canFollowTeam = false; public StateFollowLeader stateFollow; override public bool isTeamMemberAndFollow() { return canFollowTeam; } //切换至跟随状态 public void updateStateToFollowTeam() { if (this.mIsReady) { if (this.canFollowTeam) { if (this.stateFollow != null && this.CurrentState != this.stateFollow) { this.changeState(this.stateFollow); } else if (this.stateFollow == null) { mTeamVirtual.changeMemberAutoFollow(); } } } } public void cancelGuard() { if (IsGuard) { log.Debug("------guardCanceled: " + this.Name); UnitGuardAction action = new UnitGuardAction(); action.guard = false; action.reason = "sdfsdfsdfs"; doGuard(action); SetAutoBattleB2C evt = new SetAutoBattleB2C(); evt.isAutoBattle = 0; this.queueEvent(evt); } } /// /// /// private void updateFollowTeam() { if (this.CurrentState == this.stateFollow && this.mTeamVirtual != null && this.canFollowTeam && this.leadingMan != null && this.leadingMan.Enable) { // 当前如果正在拾取状态,先捡完再说 if(this.CurrentState is StateFollowAndPickItem) { return; } if (CMath.includeRoundPoint(X, Y, this.GetFollowDistance(true), leadingMan.X, leadingMan.Y)) { //在范围内 var leader = this.leadingMan; if (leader.AoiStatus != null) { //队长在位面中 UnitGuardAction action = new UnitGuardAction(); action.guard = false; action.reason = "sdfsdfsdfs"; doGuard(action); startIdle(); //updateStateToFollowTeam(); return; } var vir = leader.Virtual as XmdsVirtual; if (vir.CombatState == BattleStatus.PVE) { if (IsGuard == false) { //队长在PVE状态 UnitGuardAction action = new UnitGuardAction(); action.guard = true; action.reason = CommonAI.XmdsConstConfig.AUTO_GUARD_MODE_POINT; doGuard(action); } } else if (vir.CombatState == BattleStatus.PVP) { //队长在PVP状态 //检查玩家Pk状态在“自由”,“队伍”攻击模式下,则跟随队长攻击目标,否则无视 var myVir = this.Virtual as XmdsVirtual; if (IsGuard == false/* &&( myVir.GetCurPKMode() == PKInfo.PKMode.Team || myVir.GetCurPKMode() == PKInfo.PKMode.All)*/) { UnitGuardAction action = new UnitGuardAction(); action.guard = true; action.reason = CommonAI.XmdsConstConfig.AUTO_GUARD_MODE_POINT; doGuard(action); } } } else { UnitGuardAction action = new UnitGuardAction(); action.guard = false; action.reason = "sdfsdfsdfs"; doGuard(action); updateStateToFollowTeam(); } } } //检测修正部分异常数据 private void UpdateAndModifyErrorData() { if(this.CurrentActionStatus == UnitActionStatus.Move) { if(this.mNotMoveTimes == 0) { this.mNotMoveTimes = 1; this.mLastMovePos.SetX(this.X); this.mLastMovePos.SetY(this.Y); } else if(this.mLastMovePos.X == this.X && this.mLastMovePos.Y == this.Y) { this.mNotMoveTimes++; } else { this.mNotMoveTimes = 0; } if(this.mNotMoveTimes > 10) { this.mNotMoveTimes = 0; this.doSomething(); } } else { this.mNotMoveTimes = 0; } } //移动到队长旁 public void moveToLeader() { if (!this.mIsReady) { this.readyAction = moveToLeader; return; } if (this.leadingMan != null) { if (this.CurrentState != this.stateFollow) { this.changeState(this.stateFollow); var virt = this.Virtual as XmdsVirtual; if (!virt.IsMounted && !virt.IsInDebuffStatus() && !CMath.includeRoundPoint(this.X, this.Y, this.Templates.CFG.TEAMER_FOLLOW_DISTANCE_LIMIT, this.leadingMan.X, this.leadingMan.Y)) { PlayerSummonMountEventR2B r2b = new PlayerSummonMountEventR2B(); r2b.TimeMS = 0; r2b.IsSummonMount = true; (this.Virtual as XmdsVirtual).ReceiveMsgR2B(r2b); } } //float dx, dy; //var distance = this.leadingMan.RadiusSize + (this.BodyBlockSize * (this.FollowIndex + 1)) * 2; //CMath.RandomPosInRound(Parent.RandomN, this.leadingMan.X, this.leadingMan.Y, // distance, out dx, out dy); //if (CMath.includeRoundPoint( // this.X, this.Y, // distance, // dx, dy)) //{ // this.changeState(this.stateFollow); // return; //} //this.changeState(new StateIdle(this)); ////StateFollowTeam stateMove = StateFollowTeam.createStateFollowTeam(this, leadingMan); //StateMove stateMove = new StateMove(this, dx, dy); //stateMove.EndMoveAction = (m) => //{ // this.changeState(this.stateFollow); // return false; //}; //var virt = this.Virtual as XmdsVirtual; //if (!virt.IsMounted && !CMath.includeRoundPoint(this.X, this.Y, this.Templates.CFG.TEAMER_FOLLOW_DISTANCE_LIMIT, this.leadingMan.X, this.leadingMan.Y)) //{ // PlayerSummonMountEventR2B r2b = new PlayerSummonMountEventR2B(); // r2b.TimeMS = 0; // r2b.IsSummonMount = true; // (this.Virtual as XmdsVirtual).ReceiveMsgR2B(r2b); //} //this.changeState(stateMove); } } protected override void onAdded(bool pointLv) { base.onAdded(pointLv); //吴永辉:进入PVP地图清除技能CD bugId:2797 XmdsServerScene scene = this.Parent as XmdsServerScene; SceneType sceneType = this.VirtualPlayer.mUnit.GetSceneType(); if (sceneType == SceneType.DanTiao || sceneType == SceneType.JJC || sceneType == SceneType.CrossServer || sceneType == SceneType.MengZhan || sceneType == SceneType.FiveVFive || sceneType == SceneType.SourceDungeon || sceneType == SceneType.GuildBoss || sceneType == SceneType.SecretDungeon) { this.ClearAllSkillCD(); } } } //队伍跟随 public class StateFollowLeader : VStateFollowLeader { private InstanceZoneObject follower; public InstanceZoneObject Follower { get { return follower; } set { follower = value; this.target = follower; } } public StateFollowLeader(InstanceUnit unit, InstanceZoneObject target, int holdTime = 0) : base(unit, target, holdTime) { follower = target; } protected override void onStart() { if (follower != null) { unit.SetActionStatus(UnitActionStatus.Move); MoveAI.FindPath(follower); } } protected override void onUpdate() { base.onUpdate(); switch (FollowState) { case MoveState.Move: var virt = unit.Virtual as XmdsVirtual; if (!virt.IsMounted && !virt.IsInDebuffStatus() && !CMath.includeRoundPoint(unit.X, unit.Y, unit.Templates.CFG.TEAMER_FOLLOW_DISTANCE_LIMIT, follower.X, follower.Y)) { //Console.WriteLine("请求上马"); PlayerSummonMountEventR2B r2b = new PlayerSummonMountEventR2B(); r2b.TimeMS = 0; r2b.IsSummonMount = true; (unit.Virtual as XmdsVirtual).ReceiveMsgR2B(r2b); } break; } } protected override void onUpdateFollowed(IPositionObject target) { unit.doSomething(); } protected override void onChangedToMove(IPositionObject target) { var virt = unit.Virtual as XmdsVirtual; if (!virt.IsMounted && !virt.IsInDebuffStatus() && !CMath.includeRoundPoint(unit.X, unit.Y, unit.Templates.CFG.TEAMER_FOLLOW_DISTANCE_LIMIT, follower.X, follower.Y)) { PlayerSummonMountEventR2B r2b = new PlayerSummonMountEventR2B(); r2b.TimeMS = 0; r2b.IsSummonMount = true; (unit.Virtual as XmdsVirtual).ReceiveMsgR2B(r2b); } } } }