using BrewMonster; using BrewMonster.Managers; using BrewMonster.Network; using BrewMonster.Scripts; using CSNetwork; using CSNetwork.GPDataType; using PerfectWorld.Scripts; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; public static class AP { // AP events (from EC_PlayerWrapper.h anonymous enum) public const int AP_EVENT_CANNOTMOVE = 0; public const int AP_EVENT_MOVEFINISHED = 1; public const int AP_EVENT_TRACEOK = 2; public const int AP_EVENT_STARTSKILL = 3; public const int AP_EVENT_STOPSKILL = 4; public const int AP_EVENT_STARTUSEITEM = 5; public const int AP_EVENT_STOPUSEITEM = 6; public const int AP_EVENT_PICKUPOK = 7; public const int AP_EVENT_CANNOTPICKUP = 8; public const int AP_EVENT_STARTMELEE = 9; public const int AP_EVENT_STOPMELEE = 10; public const int AP_EVENT_COMBOCONTINUE = 11; public const int AP_EVENT_COMBOFINISH = 12; public const int AP_EVENT_MELEEOUTOFRANGE = 13; // NOTE: C# doesn't have free functions; keep names, put into static class. public static void AP_ActionEvent(int iEvent, int iParam = 0) { if (!CECAutoPolicy.GetInstance().IsAutoPolicyEnabled()) return; var pWrapper = CECAutoPolicy.GetInstance().GetPlayerWrapper(); pWrapper?.OnActionEvent(iEvent, iParam); } public static string AP_GetActionName(int iAction) { return iAction switch { CECPlayerWrapper.ACTION_IDLE => "ACTION_IDLE", CECPlayerWrapper.ACTION_MOVE => "ACTION_MOVE", CECPlayerWrapper.ACTION_CASTSKILL => "ACTION_CASTSKILL", CECPlayerWrapper.ACTION_USEITEM => "ACTION_USEITEM", CECPlayerWrapper.ACTION_PICKUP => "ACTION_PICKUP", CECPlayerWrapper.ACTION_COMBOSKILL => "ACTION_COMBOSKILL", CECPlayerWrapper.ACTION_MELEE => "ACTION_MELEE", _ => "Unknown", }; } public static string AP_GetEventName(int iEvent) { return iEvent switch { AP_EVENT_CANNOTMOVE => "AP_EVENT_CANNOTMOVE", AP_EVENT_MOVEFINISHED => "AP_EVENT_MOVEFINISHED", AP_EVENT_TRACEOK => "AP_EVENT_TRACEOK", AP_EVENT_STARTSKILL => "AP_EVENT_STARTSKILL", AP_EVENT_STOPSKILL => "AP_EVENT_STOPSKILL", AP_EVENT_STARTUSEITEM => "AP_EVENT_STARTUSEITEM", AP_EVENT_STOPUSEITEM => "AP_EVENT_STOPUSEITEM", AP_EVENT_PICKUPOK => "AP_EVENT_PICKUPOK", AP_EVENT_CANNOTPICKUP => "AP_EVENT_CANNOTPICKUP", AP_EVENT_STARTMELEE => "AP_EVENT_STARTMELEE", AP_EVENT_STOPMELEE => "AP_EVENT_STOPMELEE", AP_EVENT_COMBOCONTINUE => "AP_EVENT_COMBOCONTINUE", AP_EVENT_COMBOFINISH => "AP_EVENT_COMBOFINISH", AP_EVENT_MELEEOUTOFRANGE => "AP_EVENT_MELEEOUTOFRANGE", _ => "Unknown", }; } } public class CECPlayerWrapper { // Actions (from EC_PlayerWrapper.h enum) public const int ACTION_IDLE = 0; public const int ACTION_MOVE = 1; public const int ACTION_CASTSKILL = 2; public const int ACTION_USEITEM = 3; public const int ACTION_PICKUP = 4; public const int ACTION_COMBOSKILL = 5; public const int ACTION_MELEE = 6; private const int MAX_ATTACK_ERROR = 3; private const int INVALIDOBJ_TIMEOUT = 30000; private const float MATTER_SEARCH_RANGE = 60.0f; public struct DelayTask { public int iType; public int iParam1; public int iParam2; public DelayTask(int _) { iType = ACTION_IDLE; iParam1 = 0; iParam2 = 0; } } // ===== Base Action ===== public abstract class Action { protected readonly int type; protected readonly CECPlayerWrapper host; protected Action(CECPlayerWrapper pHost, int t) { host = pHost; type = t; } public virtual bool StartAction() => true; public virtual void EndAction() { } // return true => action finished public virtual bool Tick(uint dwDeltaTime) => false; public virtual void OnEvent(int iEvent, int iParam) { } public virtual bool CanBreak() => true; public bool HaveNextAction() => host.m_Actions.Count > 0; public int GetType() => type; } public delegate void ActionEventDelegate(int iEvent, int iParam); // ===== Dependencies (keep original names/types; implement in your project) ===== protected CECHostPlayer m_pHost; // ===== Runtime state ===== protected readonly LinkedList m_Actions = new(); protected Action m_pCurAction; protected A3DVECTOR3 m_vOrigPos; protected bool m_bForceAttack; protected int m_iAttackErrCnt; protected int m_iPickupErrCnt; // invalid objects timeout map protected readonly Dictionary m_InvalidObj = new(); // monsters attacking me protected readonly HashSet m_MonsterAttackMe = new(); protected DelayTask m_DelayTask = new DelayTask(0); // Keep accessors like C++ (AutoPolicy.Render reads these directly in C++) public CECHostPlayer GetHostPlayer() => m_pHost; public CECPlayerWrapper(CECHostPlayer pHost) { m_pHost = pHost; m_pCurAction = null; m_iAttackErrCnt = 0; m_iPickupErrCnt = 0; m_bForceAttack = false; m_vOrigPos = m_pHost.GetPos(); } public virtual void Tick(uint dwDeltaTime) { if (m_pCurAction != null) { if (m_pCurAction.Tick(dwDeltaTime)) { EndCurAction(); StartAction(); } } if (m_DelayTask.iType != ACTION_IDLE) ProcessDelayTask(); // decrement invalid timeouts if (m_InvalidObj.Count > 0) { var toRemove = new List(); foreach (var kv in m_InvalidObj) { int t = kv.Value - (int)dwDeltaTime; if (t <= 0) toRemove.Add(kv.Key); else m_InvalidObj[kv.Key] = t; } for (int i = 0; i < toRemove.Count; i++) m_InvalidObj.Remove(toRemove[i]); } } public void StopPolicy() { CECAutoPolicy.GetInstance().StopPolicy(); } public void OnStopPolicy() { ClearAction(); m_bForceAttack = false; m_iAttackErrCnt = 0; m_iPickupErrCnt = 0; m_InvalidObj.Clear(); m_MonsterAttackMe.Clear(); } public bool HaveAction() { return m_pCurAction != null || m_Actions.Count > 0; } public bool AddIdleAction(int iTime) { AddAction(new IdleAction(this, iTime)); return StartAction(); } protected bool AddAction(Action pAction) { if (pAction == null) return false; if (m_pCurAction != null && m_pCurAction.GetType() == pAction.GetType()) return false; for (var node = m_Actions.First; node != null; node = node.Next) { if (node.Value.GetType() == pAction.GetType()) return false; } m_Actions.AddLast(pAction); return true; } protected bool StartAction() { if (m_pCurAction != null) return true; bool ret = false; while (m_Actions.Count > 0) { m_pCurAction = m_Actions.First.Value; m_Actions.RemoveFirst(); ret = m_pCurAction.StartAction(); if (ret) break; EndCurAction(); // if StartAction failed, kill and try next } return ret; } protected bool EndCurAction() { if (m_pCurAction == null) return false; m_pCurAction.EndAction(); m_pCurAction = null; return true; } public void ClearAction() { if (m_pCurAction != null) EndCurAction(); m_Actions.Clear(); } private bool AddOneAction(int iActionType, int iParam = 0) { switch (iActionType) { case ACTION_MOVE: AddAction(new MoveAction(this)); break; case ACTION_CASTSKILL: AddAction(new CastSkillAction(this)); break; case ACTION_USEITEM: AddAction(new UseItemAction(this)); break; case ACTION_PICKUP: AddAction(new PickupAction(this, iParam)); break; case ACTION_COMBOSKILL: AddAction(new ComboSkillAction(this)); break; case ACTION_MELEE: AddAction(new MeleeAction(this)); break; default: throw new ArgumentOutOfRangeException(nameof(iActionType)); } return StartAction(); } // ===== Inventory helpers ===== public int GetItemIndex(int iPack, int tid) { if (iPack >= (int)InventoryType.IVTRTYPE_PACK && iPack <= (int)InventoryType.IVTRTYPE_TASKPACK) { var pPack = m_pHost.GetPack(iPack); return pPack.FindItem(tid); } return -1; } public int GetItemCount(int iPack, int tid) { if (iPack >= (int)InventoryType.IVTRTYPE_PACK && iPack <= (int)InventoryType.IVTRTYPE_TASKPACK) { var pPack = m_pHost.GetPack(iPack); return pPack.GetItemTotalNum(tid); } return 0; } public A3DVECTOR3 GetPos() { return m_pHost.GetPos(); } public void MoveTo(float x, float z) { var pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan(); //pGameUI?.AutoMoveStart((int)x, (int)z, false); AddOneAction(ACTION_MOVE); } public void CancelAction() { EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PRESSCANCEL, MANAGER_INDEX.MAN_PLAYER, 0); } public A3DVECTOR3 GetOrigPos() { return m_vOrigPos; } public void SetOrigPos(A3DVECTOR3 vPos) { m_vOrigPos = vPos; } // ===== Target search ===== private sealed class ObjectSorter : IComparer { private readonly CECPlayerWrapper _host; public ObjectSorter(CECPlayerWrapper host) { _host = host; } public int Compare(CECObject p1, CECObject p2) { float f1 = GetDistToHost(p1); float f2 = GetDistToHost(p2); float df = Math.Abs(f1 - f2); if (df > 0.00001f) return f1 < f2 ? -1 : 1; if (p1.GetClassID() == p2.GetClassID()) { // pointer compare equivalent int h1 = RuntimeHelpers.GetHashCode(p1); int h2 = RuntimeHelpers.GetHashCode(p2); return h1.CompareTo(h2); } // C++: return p1->IsMatter(); => matter first if same dist but different class return p1.IsMatter() ? -1 : 1; } private float GetDistToHost(CECObject p) { if (p == null) return 0.0f; if (p.IsMonsterNPC()) { var pNPC = p as CECNPC; return pNPC != null ? pNPC.GetDistToHost() : 0.0f; } /* else if (p.IsMatter()) { var pMatter = p as CECMatter; return pMatter != null ? pMatter.GetDistToHost() : 0.0f; }*/ else { return 0.0f; } } } public bool SearchTarget(ref int id, ref int tid) { int iTarget = 0; int iTargetTempl = 0; var pNPCMan = EC_ManMessageMono.Instance.CECNPCMan; var pMatterMan = EC_ManMessageMono.Instance.EC_ManMatter; m_iAttackErrCnt = 0; // Prefer monsters attacking me if (m_MonsterAttackMe.Count > 0) { float fMinDist = 999999.0f; int chosen = 0; foreach (var mid in m_MonsterAttackMe) { var pNPC = pNPCMan.GetNPC(mid); if (pNPC == null) continue; #if UNITY_5_3_OR_NEWER float fDist = (m_pHost.GetPos() - pNPC.GetPos()).Magnitude(); #else float fDist = A3DVECTOR3.Magnitude(m_pHost.GetPos() - pNPC.GetPos()); #endif if (fDist < fMinDist) { fMinDist = fDist; iTarget = mid; iTargetTempl = pNPC.GetTemplateID(); chosen = mid; } } if (iTarget != 0) { m_MonsterAttackMe.Remove(chosen); id = iTarget; tid = iTargetTempl; return true; } } // Collect candidates (monsters + matters) var targets = new List(); var monsters = new List(); pNPCMan.TabSelectCandidates(0, monsters); for (int i = 0; i < monsters.Count; i++) targets.Add(monsters[i]); var matters = new List(); //pMatterMan.FindMattersInRange(MATTER_SEARCH_RANGE, true, matters); for (int i = 0; i < matters.Count; i++) targets.Add(matters[i]); if (targets.Count > 0) { targets.Sort(new ObjectSorter(this)); for (int i = 0; i < targets.Count; i++) { var obj = targets[i]; if (obj.IsMonsterNPC()) { var pNPC = obj as CECNPC; if (pNPC != null && NpcCanAttack(pNPC.GetNPCID())) { iTarget = pNPC.GetNPCID(); iTargetTempl = pNPC.GetTemplateID(); break; } } else if (obj.IsMatter()) { var pMatter = obj as CECMatter; if (pMatter != null && MatterCanPickup(pMatter.GetMatterID(), pMatter.GetTemplateID())) { iTarget = pMatter.GetMatterID(); iTargetTempl = pMatter.GetTemplateID(); break; } } } if (iTarget != 0 && iTargetTempl != 0) { id = iTarget; tid = iTargetTempl; return true; } } id = 0; tid = 0; return false; } public int GetSelectedTarget() { return m_pHost.GetSelectedTarget(); } public void SelectTarget(int iTarget) { m_iAttackErrCnt = 0; m_pHost.SelectTarget(iTarget); } public void Unselect() { m_iAttackErrCnt = 0; m_pHost.SelectTarget(0); } public bool NpcCanAttack(int nid) { if (m_pHost.AttackableJudge(nid, false) != 1) return false; if (m_InvalidObj.ContainsKey(nid)) return false; return true; } public bool NormalAttack() { bool bRet = m_pHost.CmdNormalAttack(false, false, 0, m_bForceAttack ? 1 : -1); if (bRet) { AddAttackError(); AddOneAction(ACTION_MELEE); } return bRet; } // NOTE: header calls this bQueue; cpp uses bDelay. Here keep signature like header. public bool CastComboSkill(int group_id, bool bIgnoreAtkLoop, bool bQueue = false) { if (bQueue) { DoDelayTask(ACTION_COMBOSKILL, group_id, bIgnoreAtkLoop ? 1 : 0); return true; } AddOneAction(ACTION_COMBOSKILL, group_id); bool bRet = m_pHost.ApplyComboSkill(group_id, bIgnoreAtkLoop, m_bForceAttack ? 1 : -1); if (!bRet && m_iAttackErrCnt++ >= 3) { m_iAttackErrCnt = 0; m_pHost.ClearComboSkill(); } return true; } public bool CastSkill(int skill_id, bool bQueue = false) { if (bQueue) { DoDelayTask(ACTION_CASTSKILL, skill_id); return true; } bool bRet = m_pHost.ApplySkillShortcut(skill_id, false, 0, m_bForceAttack ? 1 : -1); if (bRet) { AddAttackError(); AddOneAction(ACTION_CASTSKILL); // self-cast: no trace needed if (m_pHost.GetPrepSkill() != null) AP.AP_ActionEvent(AP.AP_EVENT_TRACEOK); } return bRet; } /* public void UseItem(int iSlot) { bool bRet = m_pHost.UseItemInPack(EC_GPDataType.IVTRTYPE_PACK, iSlot); if (bRet) AddOneAction(ACTION_USEITEM); }*/ public bool MatterCanPickup(int mid, int tid) { int iPickMode = CECAutoPolicy.GetInstance().GetConfigData().iAutoPickMode; if (iPickMode == 0 || (iPickMode == 2 && !GPDataTypeHelper.ISMONEYTID(tid))) return false; if (!m_pHost.CanTakeItem(tid, 1)) return false; if (m_InvalidObj.ContainsKey(mid)) return false; return true; } public void Pickup(int mid) { bool bRet = m_pHost.PickupObject(mid, false); if (m_iPickupErrCnt++ >= 3) { m_iPickupErrCnt = 0; m_InvalidObj[mid] = INVALIDOBJ_TIMEOUT; } if (bRet) AddOneAction(ACTION_PICKUP, mid); } public bool IsPlayerInSlice(int idPlayer) { var pPlayerMan = EC_ManMessageMono.Instance.EC_ManPlayer; var pPlayer = pPlayerMan.GetPlayer(idPlayer); return pPlayer != null && !pPlayer.IsDead(); } /* public int GetWeaponEndurance() { var pWeapon = m_pHost.GetEquipment().GetItem((int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WEAPON) as CECIvtrWeapon; if (pWeapon != null) { if (pWeapon.IsRangeWeapon()) { var pArrow = m_pHost.GetEquipment().GetItem(EC_GPDataType.EQUIPIVTR_PROJECTILE) as CECIvtrArrow; if (pArrow == null || pArrow.GetCount() == 0) return 0; } return pWeapon.GetCurEndurance(); } return 0; }*/ public bool IsDead() => m_pHost.IsDead(); /* public bool IsRevivedByOther() { return m_pHost.GetReviveLostExp() >= 0.0f; } public void AcceptRevive() { UnityGameSession.c2s_CmdRevivalAgree(); }*/ /* public bool ReviveByItem() { if (m_pHost.GetCoolTime((int)CoolTimeIndex.GP_CT_SOUL_STONE) == 0) { UnityGameSession.c2s_CmdReviveItem(); return true; } return false; }*/ public void ReviveInTown() { UnityGameSession.c2s_CmdReviveVillage(); } public void SetForceAttack(bool bFlag) { m_bForceAttack = bFlag; } /* public bool IsInSanctuary() { return m_pHost.IsInSanctuary(); }*/ public bool IsMonsterAttackMe() { return m_MonsterAttackMe.Count > 0; } public A3DVECTOR3 GetObjectPos(int object_id) { if (GPDataTypeHelper.ISPLAYERID(object_id)) { var pMan = EC_ManMessageMono.Instance.EC_ManPlayer; var pPlayer = pMan.GetElsePlayer(object_id); return pPlayer != null ? pPlayer.GetPos() : new A3DVECTOR3(0, 0, 0); } else if (GPDataTypeHelper.ISNPCID(object_id)) { var pMan = EC_ManMessageMono.Instance.CECNPCMan; var pNPC = pMan.GetNPC(object_id); return pNPC != null ? pNPC.GetPos() : new A3DVECTOR3(0, 0, 0); } else if (GPDataTypeHelper.ISMATTERID(object_id)) { var pMan = EC_ManMessageMono.Instance.EC_ManMatter; var pMatter = pMan.GetMatter(object_id); return pMatter != null ? pMatter.GetPos() : new A3DVECTOR3(0, 0, 0); } return new A3DVECTOR3(0, 0, 0); } public void SetInvalidObject(int object_id) { m_InvalidObj[object_id] = INVALIDOBJ_TIMEOUT; } public void OnObjectDisappear(int object_id) { m_InvalidObj.Remove(object_id); m_MonsterAttackMe.Remove(object_id); } public void OnMonsterAttackMe(int monster_id) { if (GetSelectedTarget() != monster_id) { m_MonsterAttackMe.Add(monster_id); m_InvalidObj.Remove(monster_id); } } public void AddAttackError() { if (m_iAttackErrCnt++ >= MAX_ATTACK_ERROR) { m_iAttackErrCnt = 0; m_InvalidObj[GetSelectedTarget()] = INVALIDOBJ_TIMEOUT; } } public void ResetAttackError() { m_iAttackErrCnt = 0; } public void ResetPickupError() { m_iPickupErrCnt = 0; } public int GetAttackError() => m_iAttackErrCnt; public int GetPickupError() => m_iPickupErrCnt; public void OnActionEvent(int iEvent, int iParam) { m_pCurAction?.OnEvent(iEvent, iParam); } private void DoDelayTask(int iType, int iParam1 = 0, int iParam2 = 0) { m_DelayTask.iType = iType; m_DelayTask.iParam1 = iParam1; m_DelayTask.iParam2 = iParam2; if (m_pHost.IsMeleeing() || m_pHost.IsPlayerMoving()) CancelAction(); } private void ProcessDelayTask() { if (m_pCurAction != null && !m_pCurAction.CanBreak()) return; ClearAction(); m_iAttackErrCnt = 0; m_iPickupErrCnt = 0; switch (m_DelayTask.iType) { case ACTION_CASTSKILL: CastSkill(m_DelayTask.iParam1); break; case ACTION_COMBOSKILL: CastComboSkill(m_DelayTask.iParam1, m_DelayTask.iParam2 == 1); break; default: throw new InvalidOperationException("Unknown delay task type"); } m_DelayTask.iType = ACTION_IDLE; m_DelayTask.iParam1 = 0; m_DelayTask.iParam2 = 0; } // ========================================================= // Action implementations (converted 1:1) // ========================================================= private sealed class IdleAction : Action { private int m_iTime; public IdleAction(CECPlayerWrapper pPlayer, int iTime) : base(pPlayer, ACTION_IDLE) { m_iTime = iTime; } public override bool Tick(uint dwDeltaTime) { if (HaveNextAction()) return true; if (m_iTime > 0) { m_iTime -= (int)dwDeltaTime; return m_iTime <= 0; } return true; } } private sealed class MoveAction : Action { private bool m_bFinish; private int m_iTimeOut; public MoveAction(CECPlayerWrapper pPlayer) : base(pPlayer, ACTION_MOVE) { m_bFinish = false; m_iTimeOut = 30000; } public override bool CanBreak() => false; public override bool Tick(uint dwDeltaTime) { m_iTimeOut -= (int)dwDeltaTime; if (m_iTimeOut <= 0) return true; return m_bFinish; } public override void OnEvent(int iEvent, int iParam) { if (iEvent == AP.AP_EVENT_CANNOTMOVE || iEvent == AP.AP_EVENT_MOVEFINISHED) m_bFinish = true; } } private sealed class CastSkillAction : Action { private const int TRACE = 0; private const int SPELL = 1; private int m_iStep; private bool m_bFinish; private int m_iTimeOut; private bool m_bSuccess; public CastSkillAction(CECPlayerWrapper pPlayer) : base(pPlayer, ACTION_CASTSKILL) { m_iStep = TRACE; m_iTimeOut = 30000; m_bSuccess = false; m_bFinish = false; } public override void EndAction() { if (m_bSuccess) host.ResetAttackError(); } public override bool CanBreak() { return m_iStep == TRACE && !host.GetHostPlayer().IsPlayerMoving(); } public override bool Tick(uint dwDeltaTime) { m_iTimeOut -= (int)dwDeltaTime; if (m_iTimeOut <= 0) return true; return m_bFinish; } public override void OnEvent(int iEvent, int iParam) { if (m_iStep == TRACE) { if (iEvent == AP.AP_EVENT_TRACEOK) { m_iStep = SPELL; m_iTimeOut = 2000; } else if (iEvent == AP.AP_EVENT_MOVEFINISHED) { m_bFinish = true; } } else if (m_iStep == SPELL) { if (iEvent == AP.AP_EVENT_STARTSKILL) { m_iTimeOut = iParam * 2; m_bSuccess = true; } else if (iEvent == AP.AP_EVENT_STOPSKILL) { m_bFinish = true; } } } } private sealed class UseItemAction : Action { private bool m_bFinish; private int m_iTimeOut; public UseItemAction(CECPlayerWrapper pPlayer) : base(pPlayer, ACTION_USEITEM) { m_bFinish = false; m_iTimeOut = 2000; } public override bool CanBreak() => false; public override bool Tick(uint dwDeltaTime) { m_iTimeOut -= (int)dwDeltaTime; if (m_iTimeOut <= 0) return true; return m_bFinish; } public override void OnEvent(int iEvent, int iParam) { if (iEvent == AP.AP_EVENT_STARTUSEITEM) { m_iTimeOut = iParam * 2; } else if (iEvent == AP.AP_EVENT_STOPUSEITEM) { m_bFinish = true; } } } private sealed class PickupAction : Action { private const int TRACE = 0; private const int PICKUP = 1; private int m_iStep; private bool m_bFinish; private int m_iTimeOut; private bool m_bSuccess; private readonly int m_iMatterID; public PickupAction(CECPlayerWrapper pPlayer, int iMatterID) : base(pPlayer, ACTION_PICKUP) { m_iMatterID = iMatterID; m_iTimeOut = 30000; m_iStep = TRACE; m_bSuccess = false; m_bFinish = false; } public override void EndAction() { if (m_bSuccess) host.ResetPickupError(); } public override bool CanBreak() { return m_iStep == TRACE && !host.GetHostPlayer().IsPlayerMoving(); } public override bool Tick(uint dwDeltaTime) { m_iTimeOut -= (int)dwDeltaTime; if (m_iTimeOut <= 0) return true; return m_bFinish; } public override void OnEvent(int iEvent, int iParam) { if (m_iStep == TRACE) { if (iEvent == AP.AP_EVENT_TRACEOK) { m_iStep = PICKUP; m_iTimeOut = 2000; } else if (iEvent == AP.AP_EVENT_MOVEFINISHED) { m_bFinish = true; } } else if (m_iStep == PICKUP) { if (iEvent == AP.AP_EVENT_PICKUPOK) { m_bFinish = true; m_bSuccess = true; } else if (iEvent == AP.AP_EVENT_CANNOTPICKUP) { m_bFinish = true; host.SetInvalidObject(m_iMatterID); host.ResetPickupError(); } } } } private sealed class MeleeAction : Action { private const int TRACE = 0; private const int MELEE = 1; private bool m_bFinish; private int m_iStep; private int m_iTimeOut; private bool m_bMeleeing; private bool m_bLastMeleeStopped; public MeleeAction(CECPlayerWrapper pPlayer) : base(pPlayer, ACTION_MELEE) { m_iStep = TRACE; m_iTimeOut = 30000; m_bMeleeing = false; m_bFinish = false; m_bLastMeleeStopped = true; } public override bool CanBreak() { return m_iStep == TRACE && !host.GetHostPlayer().IsPlayerMoving(); } public override bool Tick(uint dwDeltaTime) { if (!m_bMeleeing) { m_iTimeOut -= (int)dwDeltaTime; if (m_iTimeOut <= 0) return true; return m_bFinish; } return false; } public override void OnEvent(int iEvent, int iParam) { if (iEvent == AP.AP_EVENT_TRACEOK) { if (m_iStep == TRACE) { m_iStep = MELEE; m_iTimeOut = 2000; } } else if (iEvent == AP.AP_EVENT_MOVEFINISHED) { if (m_iStep == TRACE) m_bFinish = true; } else if (iEvent == AP.AP_EVENT_STARTMELEE) { if (m_iStep == MELEE) { m_bMeleeing = true; m_iTimeOut = 10000; host.ResetAttackError(); } } else if (iEvent == AP.AP_EVENT_STOPMELEE) { if (!m_bLastMeleeStopped) { m_bMeleeing = false; m_bLastMeleeStopped = true; return; } if (m_iStep == MELEE && m_bMeleeing) { m_bMeleeing = false; m_bFinish = true; host.ResetAttackError(); } } else if (iEvent == AP.AP_EVENT_MELEEOUTOFRANGE) { if (!m_bLastMeleeStopped) { m_bLastMeleeStopped = true; return; } if (m_iStep == MELEE) { m_iStep = TRACE; m_iTimeOut = 30000; m_bLastMeleeStopped = (iParam == 1); } } } } private sealed class ComboSkillAction : Action { private const int TRACE = 0; private const int SPELL = 1; private const int MELEE = 2; private int m_iState; private int m_iTimeOut; private bool m_bFinish; private bool m_bMeleeing; private bool m_bCasting; private bool m_bDelayContinue; private bool m_bSelfSkillFlag; private bool m_bSending; private bool m_bLastMeleeStopped; public ComboSkillAction(CECPlayerWrapper pPlayer) : base(pPlayer, ACTION_COMBOSKILL) { m_iState = TRACE; m_iTimeOut = 30000; m_bMeleeing = false; m_bDelayContinue = false; m_bCasting = false; m_bSelfSkillFlag = false; m_bSending = false; m_bFinish = false; m_bLastMeleeStopped = true; } public override bool CanBreak() { return !m_bSending && !m_bCasting && !m_bMeleeing && !host.GetHostPlayer().IsPlayerMoving(); } public override bool Tick(uint dwDeltaTime) { var pCombo = host.GetHostPlayer().GetComboSkill(); if (pCombo == null && !m_bMeleeing) return true; if (pCombo != null && !pCombo.IsIgnoreAtkLoop() && pCombo.GetTarget() != host.GetSelectedTarget() && !m_bCasting && !m_bMeleeing) return true; if (!m_bMeleeing) { m_iTimeOut -= (int)dwDeltaTime; if (m_iTimeOut <= 0) return true; return m_bFinish; } return false; } public override void OnEvent(int iEvent, int iParam) { if (iEvent == AP.AP_EVENT_COMBOCONTINUE) { if (m_iState == MELEE) { m_bDelayContinue = true; m_bSelfSkillFlag = (iParam == 1); } else { if (iParam == 1) { m_iState = SPELL; m_iTimeOut = 2000; m_bSending = true; } else { m_iState = TRACE; m_iTimeOut = 30000; } } } else if (iEvent == AP.AP_EVENT_TRACEOK) { if (m_iState == TRACE) { m_iState = (iParam == 0) ? MELEE : SPELL; m_iTimeOut = 2000; m_bSending = true; } } else if (iEvent == AP.AP_EVENT_MOVEFINISHED) { if (m_iState == TRACE) m_bFinish = true; } else if (iEvent == AP.AP_EVENT_STARTMELEE) { if (m_iState == MELEE) { m_iTimeOut = 10000; m_bMeleeing = true; m_bSending = false; host.ResetAttackError(); } } else if (iEvent == AP.AP_EVENT_STOPMELEE) { if (!m_bLastMeleeStopped) { m_bMeleeing = false; m_bLastMeleeStopped = true; return; } if (m_iState == MELEE && m_bMeleeing) { m_bMeleeing = false; host.ResetAttackError(); var pCombo = host.GetHostPlayer().GetComboSkill(); if (pCombo != null && pCombo.IsStop()) { m_bFinish = true; } else { m_iTimeOut = 10000; if (m_bDelayContinue) { m_bDelayContinue = false; if (m_bSelfSkillFlag) { m_iState = SPELL; m_iTimeOut = 2000; m_bSending = true; } else { m_iState = TRACE; m_iTimeOut = 30000; } } } } } else if (iEvent == AP.AP_EVENT_MELEEOUTOFRANGE) { if (!m_bLastMeleeStopped) { m_bLastMeleeStopped = true; return; } if (m_iState == MELEE) { m_iState = TRACE; m_iTimeOut = 30000; m_bLastMeleeStopped = (iParam == 1); } } else if (iEvent == AP.AP_EVENT_STARTSKILL) { if (m_iState == SPELL) { m_iTimeOut = iParam * 2; m_bCasting = true; m_bSending = false; host.ResetAttackError(); } } else if (iEvent == AP.AP_EVENT_STOPSKILL) { if (m_iState == SPELL && m_bCasting) { m_bCasting = false; host.ResetAttackError(); var pCombo = host.GetHostPlayer().GetComboSkill(); if (pCombo != null && pCombo.IsStop()) m_bFinish = true; else m_iTimeOut = 10000; } } else if (iEvent == AP.AP_EVENT_COMBOFINISH) { m_bFinish = true; } } } public enum AP_EVENT { AP_EVENT_CANNOTMOVE, // ²»ÄÜÒÆ¶¯ AP_EVENT_MOVEFINISHED, // ÒÆ¶¯½áÊø AP_EVENT_TRACEOK, // ×·×ٳɹ¦ AP_EVENT_STARTSKILL, // ¼¼ÄÜ¿ªÊ¼ AP_EVENT_STOPSKILL, // ¼¼ÄÜÍ£Ö¹ AP_EVENT_STARTUSEITEM, // ʹÓÃÎïÆ·¿ªÊ¼ AP_EVENT_STOPUSEITEM, // ʹÓÃÎïÆ·½áÊø AP_EVENT_PICKUPOK, // ³É¹¦¼ñÈ¡ÎïÆ· AP_EVENT_CANNOTPICKUP, // ËûÈËÎïÆ·£¬²»ÄܼñÈ¡ AP_EVENT_STARTMELEE, // ¿ªÊ¼ÆÕ¹¥ AP_EVENT_STOPMELEE, // ½áÊøÆÕ¹¥ AP_EVENT_COMBOCONTINUE, // ×éºÏ¼¼Íƽø AP_EVENT_COMBOFINISH, // ×éºÏ¼¼ÖÕÖ¹£¬Ó¦¶Ô×îºóÒ»¸ö¼¼ÄÜûÓзųöµÄÇé¿ö AP_EVENT_MELEEOUTOFRANGE, // ÆÕ¹¥³¬³ö¾àÀë }; }