1335 lines
37 KiB
C#
1335 lines
37 KiB
C#
|
|
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<Action> 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<int, int> m_InvalidObj = new();
|
|
|
|
// monsters attacking me
|
|
protected readonly HashSet<int> 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<int>();
|
|
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<CECObject>
|
|
{
|
|
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<CECObject>();
|
|
|
|
var monsters = new List<CECNPC>();
|
|
pNPCMan.TabSelectCandidates(0, monsters);
|
|
for (int i = 0; i < monsters.Count; i++)
|
|
targets.Add(monsters[i]);
|
|
|
|
var matters = new List<CECMatter>();
|
|
//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, // ÆÕ¹¥³¬³ö¾àÀë
|
|
};
|
|
}
|