Files
test/Assets/Scripts/CECHostPlayer.cs
T
2025-10-11 16:09:00 +07:00

1247 lines
46 KiB
C#

using BrewMonster;
using BrewMonster.Network;
using CSNetwork;
using CSNetwork.Common;
using CSNetwork.GPDataType;
using CSNetwork.Protocols;
using CSNetwork.Protocols.RPCData;
using PerfectWorld.Scripts.Managers;
using PerfectWorld.Scripts.Player;
using PerfectWorld.Scripts.Task;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using TMPro;
using Unity.VisualScripting;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Scene = UnityEngine.SceneManagement.Scene;
public class CECHostPlayer : CECPlayer
{
[SerializeField] private TextMeshPro txtName;
[SerializeField] private CharacterController controller;
[SerializeField] private Joystick joystick;
[SerializeField] private Button btnJump;
[SerializeField] private Button btnRun;
[SerializeField] private Transform parentModel;
PlayerStateMachine _playerStateMachine;
PlayerMoveState _moveState;
PlayerIdleState _idleState;
public CECHostMove m_MoveCtrl;
CECHPWorkMan m_pWorkMan; // Host work manager
uint m_dwLIES; // Logic-influence extend states
FACTION_FORTRESS_ENTER m_fortressEnter; // ½øÈë»ùµØÐÅÏ¢
PVPINFO m_pvp; // pvp information
bool m_bInSanctuary = false; // true, player is in sanctuary
int m_idFaction = 0; // ID of player's faction
public bool m_bPrepareFight = false; // true, prepare to fight
int m_iJumpCount = 0;
bool m_bJumpInWater = false;
public A3DVECTOR3 m_vVelocity; // Velocity
float playerSpeed = 5.0f;
float jumpHeight = 1.5f;
float gravityValue = -9.81f;
StateAnim stateAnim = StateAnim.Idle;
Vector3 playerVelocity;
bool isGrounded = false;
bool isRun = false;
Vector3 m_vLastSevPos;
public CDR_INFO m_CDRInfo;
public GNDINFO m_GndInfo;
public float m_fVertSpeed = 0f;
// ====== Ground cast config ======
[Header("Ground Cast")]
[Tooltip("Khoảng thêm ngoài skinWidth để SphereCast xuống (m ngắn)")]
[SerializeField] private float extraGroundDistance = 0.05f;
[Tooltip("Bớt bán kính một chút để tránh tự va vào capsule (epsilon)")]
[SerializeField] private float radiusEpsilon = 0.005f;
[Tooltip("Layer mặt đất")]
[SerializeField] private LayerMask groundMask;
[Tooltip("Layer mặt đất")]
[SerializeField] private float slopeToleranceDeg = 2f;
// cache tùy chọn (không bắt buộc)
float ccRadius, ccSkin;
RaycastHit lastGroundHit;
Camera mainCam;
private void Awake()
{
base.Awake();
_moveState = new PlayerMoveState(this);
_idleState = new PlayerIdleState(this);
_playerStateMachine = new PlayerStateMachine();
m_MoveCtrl = new CECHostMove(this);
// Cache: không bắt buộc, nhưng gọn tay và ít gọi property lặp.
if (controller != null)
{
ccRadius = controller.radius;
ccSkin = controller.skinWidth;
}
}
public void SetModelHostPlayer()
{
_pPlayerModel = NPCManager.Instance.GetModelPlayer();
Scene scene = SceneManager.GetSceneByName("WorldRender");
SceneManager.MoveGameObjectToScene(_pPlayerModel, scene);
_pPlayerModel.transform.SetParent(parentModel);
_pPlayerModel.transform.localPosition = Vector3.zero;
_pPlayerModel.SetActive(true);
}
private void Start()
{
mainCam = Camera.main;
if (mainCam == null)
{
mainCam = FindFirstObjectByType<Camera>();
}
_playerStateMachine.InitState(_idleState);
// btnJump.onClick.AddListener(HandleJump);
}
private void Update()
{
//Debug.Log($"(ulong)Time.deltaTime * 1000 {(ulong)(Time.deltaTime * 1000)}");
m_MoveCtrl.Tick((ulong)(Time.deltaTime * 1000));
// Nếu có thay đổi runtime, có thể lấy lại mỗi vài giây/Start nếu bạn thích:
// ccRadius = controller.radius; ccSkin = controller.skinWidth;
_playerStateMachine.UpdateState();
if (Input.GetMouseButtonDown(1) && mainCam != null)
{
int idTraceTarget = 0, idSelTarget = 0;
bool bForceAttack = false;
int iTraceReason = CECHPWorkTrace.Trace_reason.TRACE_NONE;
bool bWikiMonster = false;
Ray ray = mainCam.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
CECObject clickedObject = hit.collider.gameObject.GetComponent<CECObject>();
if (clickedObject != null)
{
int idObject = CECObject.GetObjectID(clickedObject);
if (idObject != 0)
{
CECNPC pNPC = EC_ManMessageMono.Instance._CECNPCMan.GetNPC(idObject);
if (pNPC != null)
{
if (!pNPC.IsDead())
{
idTraceTarget = idObject;
}
if (idTraceTarget != 0)
{
if (AttackableJudge(idObject, bForceAttack) == 1)
iTraceReason = CECHPWorkTrace.Trace_reason.TRACE_ATTACK;
else if (pNPC.IsServerNPC())
{
if (!IsInBattle() || InSameBattleCamp(pNPC))
iTraceReason = CECHPWorkTrace.Trace_reason.TRACE_TALK;
}
}
}
}
}
}
if (idTraceTarget != 0)
{
//if (m_pWorkMan.IsSitting())
//{
// g_pGame.GetGameSession().c2s_CmdStandUp();
// return;
//}
// Trace a object
if (iTraceReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK)
{
//if (!CanDo(CANDO_MELEE))
// return;
Debug.LogError("Attack");
NormalAttackObject(idTraceTarget, bForceAttack);
}
//else
//{
// if (!CanDo(CANDO_MOVETO))
// return;
// if (iTraceReason == CECHPWorkTrace::TRACE_PICKUP)
// {
// PickupObject(idTraceTarget, false);
// }
// else if (iTraceReason == CECHPWorkTrace::TRACE_GATHER)
// {
// PickupObject(idTraceTarget, true);
// }
// else if (CECHPWork * pWork = m_pWorkMan.GetWork(CECHPWork::WORK_TRACEOBJECT))
// {
// CECHPWorkTrace* pWorkTrace = dynamic_cast<CECHPWorkTrace*>(pWork);
// pWorkTrace.SetTraceTarget(pWorkTrace.CreatTraceTarget(idTraceTarget, iTraceReason, bForceAttack));
// }
// else if (m_pWorkMan.CanStartWork(CECHPWork::WORK_TRACEOBJECT) && !bWikiMonster)
// {
// CECHPWorkTrace* pWork = (CECHPWorkTrace*)m_pWorkMan.CreateWork(CECHPWork::WORK_TRACEOBJECT);
// pWork.SetTraceTarget(pWork.CreatTraceTarget(idTraceTarget, iTraceReason, bForceAttack));
// m_pWorkMan.StartWork_p1(pWork);
// }
//}
}
}
}
public void StopMovement()
{
m_MoveCtrl.SendStopMoveCmd(transform.position, 5f, (int)GPMoveMode.GP_MOVE_WALK);
}
public void HandleMovement()
{
// 1) Kiểm tra grounded bằng SphereCast ngắn dựa trên radius + skinWidth
isGrounded = GroundCheck(out lastGroundHit);
// 2) Input tạm thời: giữ nguyên như bạn
if (UnityEngine.Input.GetKeyDown(KeyCode.LeftShift)) SetStatusRun(true);
if (UnityEngine.Input.GetKeyUp(KeyCode.LeftShift)) SetStatusRun(false);
if (UnityEngine.Input.GetKeyDown(KeyCode.Space)) HandleJump();
// 3) Trọng lực / sticky
if (isGrounded && playerVelocity.y < 0f)
{
// Đè nhẹ để bám đất (tránh nhấp-nháy)
playerVelocity.y = -2f;
}
else
{
playerVelocity.y += gravityValue * Time.deltaTime;
}
// 4) Chuyển động phẳng
float x = joystick.Horizontal;
float z = joystick.Vertical;
Vector3 move = new Vector3(x, 0, z);
move = Vector3.ClampMagnitude(move, 1f);
if (move != Vector3.zero)
{
transform.forward = move;
m_MoveCtrl.GroundMove(Time.deltaTime);
m_MoveCtrl.SendMoveCmd(transform.position, controller.velocity, (int)GPMoveMode.GP_MOVE_RUN);
}
else
{
}
Vector3 finalMove = (move * playerSpeed) + (playerVelocity.y * Vector3.up);
controller.Move(finalMove * Time.deltaTime);
}
private void JoystickRelease(JoystickRealeaseEvent joystickRealeaseEvent)
{
_playerStateMachine.ChangeState(_idleState);
}
private bool GroundCheck(out RaycastHit hit)
{
float radius = controller.radius;
float skin = controller.skinWidth;
float height = controller.height;
// Tâm capsule theo world
Vector3 cWorld = transform.TransformPoint(controller.center);
float hemi = Mathf.Max(0f, (height * 0.5f) - radius);
// Hai điểm top/bottom của capsule nhân vật (đang đứng)
Vector3 pTop = cWorld + Vector3.up * hemi;
Vector3 pBottom = cWorld - Vector3.up * hemi;
// Ta tạo một "đoạn capsule ngắn" gần đáy để sweep xuống
// Nhấc đoạn bắt đầu lên 1 chút để không bắt đầu trong trạng thái giao nhau
Vector3 startBottom = pBottom + Vector3.up * (skin + 0.01f);
Vector3 startTop = startBottom + Vector3.up * (radius * 2f - 0.02f); // chiều cao đoạn ngắn ~2*radius
float castRadius = Mathf.Max(0f, radius - radiusEpsilon);
float castDistance = skin + extraGroundDistance; // quãng sweep ngắn
bool hitSomething = Physics.CapsuleCast(
startTop, startBottom, castRadius,
Vector3.down, out hit, castDistance,
groundMask, QueryTriggerInteraction.Ignore
);
if (!hitSomething) return false;
// Lọc theo slope limit
float maxSlope = controller.slopeLimit + slopeToleranceDeg;
float slope = Vector3.Angle(hit.normal, Vector3.up);
if (slope > maxSlope) return false;
return true;
}
private void HandleJump()
{
if (isGrounded)
{
playerVelocity.y = Mathf.Sqrt(jumpHeight * -2f * gravityValue);
}
}
public void ProcessMessage(in ECMSG Msg)
{
Debug.LogWarning("HoangDev : ProcessMessageProcessMessageProcessMessage");
var msg = (int)Msg.dwMsg;
switch (msg)
{
case int value when value == EC_MsgDef.MSG_HST_CORRECTPOS: OnMsgHstCorrectPos(Msg); break;
case int value when value == EC_MsgDef.MSG_HST_GOTO: OnMsgHstGoto(Msg); break;
case int value when value == EC_MsgDef.MSG_HST_IVTRINFO:
{
OnMsgHstIvtrInfo(Msg);
break;
}
case int value when value == EC_MsgDef.MSG_HST_OWNITEMINFO:
{
OnMsgHstOwnItemInfo(Msg);
break;
}
case int value when value == EC_MsgDef.MSG_HST_TASKDATA:
{
OnMsgHstTaskData(Msg);
//Debug.LogError("[Dat]- OnMsgHstTaskData");
break;
}
case int value when value == EC_MsgDef.MSG_HST_ITEMOPERATION:
OnMsgHstItemOperation(Msg);
break;
case int value when value == EC_MsgDef.MSG_HST_PICKUPITEM:
OnMsgHstPickupItem(Msg);
break;
case int value when value == EC_MsgDef.MSG_HST_ATKRESULT: OnMsgHstAttackResult(Msg); break;
//case int value when value == EC_MsgDef.MSG_HST_ATTACKED: OnMsgHstAttacked(Msg); break;
case int value when value == EC_MsgDef.MSG_HST_HURTRESULT: OnMsgHstHurtResult(Msg); break;
}
}
public void OnMsgHstAttackResult(ECMSG Msg)
{
cmd_host_attack_result pCmd = (cmd_host_attack_result)Msg.dwParam1;
int iAttackTime = 0;
PlayAttackEffect(pCmd.idTarget, 0, 0, pCmd.iDamage, (uint)pCmd.attack_flag, pCmd.attack_speed * 50, ref iAttackTime);
TurnFaceTo(pCmd.idTarget);
if (iAttackTime != 0)
{
if (m_pWorkMan.GetRunningWork(CECHPWork.Host_work_ID.WORK_HACKOBJECT) is CECHPWorkMelee pCurWork)
{
pCurWork.SetIdleTime(iAttackTime);
}
}
}
public void PlayAttackEffect(int idTarget, int idSkill, int skillLevel, int nDamage,
uint dwModifier, int nAttackSpeed, ref int piAttackTime/* NULL */, int nSection = 0)
{
/* if (!IsAllResReady())
return;
if (idSkill == 0)
{
int idWeapon = IsShapeChanged() ? 0 : GetWeaponID();
int nTimeFly = 10;
if (idWeapon != 0)
{
// ¿´¿´ÊDz»ÊÇÔ¶³ÌÎäÆ÷
DATA_TYPE dt = default;
WEAPON_ESSENCE? pWeapon = (WEAPON_ESSENCE)ElementDataManProvider.GetElementDataMan().get_data_ptr((uint)idWeapon, ID_SPACE.ID_SPACE_ESSENCE, ref dt);
if (dt == DATA_TYPE.DT_WEAPON_ESSENCE && pWeapon != null && pWeapon.Value.require_projectile != 0)
{
nTimeFly = 700;
if (m_aEquips[(int)IndexOfIteminEquipmentInventory.EQUIPIVTR_PROJECTILE] != 0)
idWeapon = m_aEquips[(int)IndexOfIteminEquipmentInventory.EQUIPIVTR_PROJECTILE];
}
}
if (g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->FindAttackByAttacker(GetPlayerInfo().cid))
{
// signal early attack event
ClearComActFlagAllRankNodes(true);
}
// melee attack
CECAttackEvent* pAttack = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->AddMeleeAttack(
GetPlayerInfo().cid, idTarget, idWeapon, dwModifier, nDamage, nTimeFly);
if (pAttack)
{
if (!IsDead() && (dwModifier & CECAttackEvent::MOD_RETORT) == 0
&& (dwModifier & CECAttackEvent::MOD_ATTACK_AURA) == 0
&& PlayAttackAction(nAttackSpeed, piAttackTime, &pAttack->m_bSignaled)
&& (dwModifier & CECAttackEvent::MOD_BEAT_BACK) == 0)
{
}
else
{
pAttack->m_bSignaled = true;
}
}
}
else
{
if (skillLevel == 0)
{
if (m_pCurSkill)
skillLevel = m_pCurSkill->GetSkillLevel();
else
skillLevel = 1;
}
CECAttackEvent* pAttack = NULL;
// first try to find if there is already a skill attack event in attackman
CECAttackerEvents attackerEvents = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->FindAttackByAttacker(GetPlayerInfo().cid);
if (attackerEvents)
{
if (CECAttackEvent * pAttack = attackerEvents.Find(idSkill, nSection))
{
// Ãæ¹¥»÷µÄ·ÇµÚÒ»´ÎÉ˺¦ÏûÏ¢
pAttack->AddTarget(idTarget, dwModifier, nDamage);
goto EXIT;
}
else
{
attackerEvents.Signal();
}
}
if (GNET::ElementSkill::IsGoblinSkill(idSkill) &&
GNET::ElementSkill::GetType(idSkill) == 2)
{
pAttack = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->AddSkillAttack(
GetPlayerInfo().cid, GetPlayerInfo().cid, idTarget, GetWeaponID(), idSkill, skillLevel, dwModifier, nDamage);
}
else
{
// begin a skill attack
pAttack = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->AddSkillAttack(
GetPlayerInfo().cid, m_idCurSkillTarget, idTarget, GetWeaponID(), idSkill, skillLevel, dwModifier, nDamage);
}
if (pAttack)
{
pAttack->SetSkillSection(nSection);
if (!IsDead() && (dwModifier & CECAttackEvent::MOD_RETORT) == 0
&& (dwModifier & CECAttackEvent::MOD_ATTACK_AURA) == 0
&& PlaySkillAttackAction(idSkill, nAttackSpeed, NULL, nSection, &pAttack->m_bSignaled)
&& (dwModifier & CECAttackEvent::MOD_BEAT_BACK) == 0)
{
}
else
{
pAttack->m_bSignaled = true;
}
}
EXIT:
// For skill attacking, time is always set to 0
if (piAttackTime)
*piAttackTime = 0;
}*/
}
private void OnMsgHstHurtResult(ECMSG Msg)
{
/* BMLogger.LogError("HoangDev : OnMsgHstHurtResult");
int cmd = Convert.ToInt32(Msg.dwParam2);
if (cmd == CommandID.BE_HURT)
{
cmd_be_hurt pCmd = (cmd_be_hurt)Msg.dwParam1;
if (pCmd.damage != 0)
Damaged(pCmd->damage);
}
else if (cmd == CommandID.HURT_RESULT)
{
cmd_hurt_result pCmd = (cmd_hurt_result)Msg.dwParam1;
if (pCmd.target_id == m_PlayerInfo.cid)
return; // Host himself will receive BE_HURT, so ignore this.
if (UnityGameSession.Instance.GameSession.ISPLAYERID(pCmd.target_id))
{
CECElsePlayer pTarget = m_pPlayerMan.GetElsePlayer(pCmd.target_id);
if (pTarget)
pTarget->Damaged(pCmd->damage);
}
else if (UnityGameSession.Instance.GameSession.ISNPCID(pCmd.target_id))
{
CECNPC pTarget = EC_ManMessageMono.Instance._CECNPCMan.GetNPC(pCmd.target_id);
if (pTarget)
pTarget.Damaged(pCmd.damage);
}
}*/
}
public void OnMsgHstPickupItem(in ECMSG Msg)
{
var data = Msg.dwParam1 as byte[];
int cmd = Convert.ToInt32(Msg.dwParam2);
switch (cmd)
{
case CommandID.PICKUP_ITEM:
{
// Parse the pickup item data from the server response
if (data != null && data.Length >= 16)
{
int tid = BitConverter.ToInt32(data, 0);
int expire_date = BitConverter.ToInt32(data, 4);
uint iAmount = BitConverter.ToUInt32(data, 8);
uint iSlotAmount = BitConverter.ToUInt32(data, 12);
byte byPackage = data[16];
byte bySlot = data[17];
Debug.Log($"[Inventory] PICKUP_ITEM: tid={tid}, expire_date={expire_date}, iAmount={iAmount}, iSlotAmount={iSlotAmount}, byPackage={byPackage}, bySlot={bySlot}");
// Notify pickupItem script about successful pickup
pickupItem pickupScript = UnityEngine.Object.FindFirstObjectByType<pickupItem>();
if (pickupScript != null)
{
pickupScript.OnPickupSuccess(tid);
}
// Create new inventory item data
var newItem = new InventoryItemData
{
Package = byPackage,
Slot = bySlot,
TemplateId = tid,
ExpireDate = expire_date,
State = 0,
Count = (int)iAmount,
Crc = 0,
Content = null
};
// Add item to inventory
EC_Inventory.SetItem(byPackage, bySlot, newItem);
Debug.Log($"[Inventory] Successfully added item {tid} to package {byPackage}, slot {bySlot} with count {iAmount}");
// Trigger UI refresh if an EC_InventoryUI is present in scene
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
if (ui != null)
{
ui.RefreshAll();
}
}
else
{
Debug.LogWarning("[Inventory] PICKUP_ITEM: Invalid data length");
}
break;
}
}
}
public void OnMsgHstItemOperation(ECMSG Msg)
{
var data = Msg.dwParam1 as byte[];
int cmd = Convert.ToInt32(Msg.dwParam2);
switch (cmd)
{
case CommandID.PLAYER_DROP_ITEM:
{
// Parse the drop item data from the server response
if (data != null && data.Length >= 6)
{
byte byPackage = data[0];
byte bySlot = data[1];
int count = BitConverter.ToInt32(data, 2);
int tid = BitConverter.ToInt32(data, 6);
byte reason = data[10];
Debug.Log($"[Inventory] PLAYER_DROP_ITEM: package={byPackage}, slot={bySlot}, count={count}, tid={tid}, reason={reason}");
// Update the inventory by removing the item
bool success = EC_Inventory.RemoveItem(byPackage, bySlot, count);
if (success)
{
Debug.Log($"[Inventory] Successfully removed {count} items from package {byPackage}, slot {bySlot}");
// Trigger UI refresh if an EC_InventoryUI is present in scene
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
if (ui != null)
{
ui.RefreshAll();
}
}
else
{
Debug.LogWarning($"[Inventory] Failed to remove items from package {byPackage}, slot {bySlot}");
}
}
else
{
Debug.LogWarning("[Inventory] PLAYER_DROP_ITEM: Invalid data length");
}
break;
}
case CommandID.EQUIP_ITEM:
{
byte index_inv = data[0];
byte index_equip = data[1];
// Update client-side data: move item between PACK_INVENTORY and PACK_EQUIPMENT
var invItem = EC_Inventory.GetItem(EC_Inventory.PACK_INVENTORY, index_inv, true);
var equipItem = EC_Inventory.GetItem(EC_Inventory.PACK_EQUIPMENT, index_equip, true);
if (invItem != null)
{
invItem.Package = EC_Inventory.PACK_EQUIPMENT;
invItem.Slot = index_equip;
EC_Inventory.SetItem(EC_Inventory.PACK_EQUIPMENT, index_equip, invItem);
}
if (equipItem != null)
{
equipItem.Package = EC_Inventory.PACK_INVENTORY;
equipItem.Slot = index_inv;
EC_Inventory.SetItem(EC_Inventory.PACK_INVENTORY, index_inv, equipItem);
}
// Trigger UI refresh if an EC_InventoryUI is present in scene
var ui = GameObject.FindObjectOfType<EC_InventoryUI>();
if (ui != null)
{
ui.RefreshAll();
}
break;
}
}
}
public void OnMsgHstOwnItemInfo(ECMSG Msg)
{
int cmd = Convert.ToInt32(Msg.dwParam2);
switch (cmd)
{
case CommandID.OWN_ITEM_INFO:
{
Debug.Log("[Inventory] OWN_ITEM_INFO received");
var data = Msg.dwParam1 as byte[];
int hostId = Convert.ToInt32(Msg.dwParam3);
PerfectWorld.Scripts.Managers.EC_Inventory.LogInventoryPacket("OWN_ITEM_INFO", data, hostId);
break;
}
}
}
public void OnMsgHstIvtrInfo(ECMSG Msg)
{
var data = Msg.dwParam1 as byte[];
int cmd = Convert.ToInt32(Msg.dwParam2);
int hostId = Convert.ToInt32(Msg.dwParam3);
switch (cmd)
{
case CommandID.OWN_IVTR_DATA:
{
Debug.Log("[Inventory] OWN_IVTR_DATA received");
PerfectWorld.Scripts.Managers.EC_Inventory.LogInventoryPacket("OWN_IVTR_DATA", data, hostId);
break;
}
case CommandID.OWN_IVTR_DETAIL_DATA:
{
Debug.Log("[Inventory] OWN_IVTR_DETAIL_DATA received");
PerfectWorld.Scripts.Managers.EC_Inventory.LogInventoryPacket("OWN_IVTR_DETAIL_DATA", data, hostId);
// Parse and store
if (data != null && data.Length >= 6)
{
byte byPackage = data[0];
byte ivtrSize = data[1];
if (PerfectWorld.Scripts.Managers.EC_IvtrItem.TryParseInventoryDetail(data, out var pkg, out var size, out var items))
{
PerfectWorld.Scripts.Managers.EC_Inventory.UpdatePack(pkg, size, items);
}
}
break;
}
}
}
public void OnMsgHstCorrectPos(in ECMSG Msg)
{
Debug.LogWarning("HoangDev : OnMsgHstCorrectPos");
byte[] buf = (byte[])Msg.dwParam1; // chỗ bạn lưu pDataBuf
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
cmd_host_correct_pos pCmd = (cmd_host_correct_pos)Marshal.PtrToStructure(
handle.AddrOfPinnedObject(), typeof(cmd_host_correct_pos));
handle.Free();
Debug.LogWarning("HoangDev :pCmd.pos " + pCmd.pos);
SetPos(pCmd.pos);
}
public void OnMsgHstGoto(in ECMSG Msg)
{
Debug.Log("HoangDev :OnMsgHstGoto");
cmd_notify_hostpos pCmd = (cmd_notify_hostpos)Msg.dwParam1;
}
public void SetPos(Vector3 pos)
{
transform.position = pos;
}
public void SetStatusRun(bool value)
{
if (!isGrounded)
{
Debug.LogError("Player not in ground");
return;
}
isRun = value;
}
public void InitCharacter(cmd_self_info_1 role)
{
string roleName = "(Error decoding name)";
//if (role.name != null && role.name.ByteArray != null)
//{
// roleName = Encoding.UTF8.GetString(role.name.ByteArray, 0, role.name.Length);
//}
SetPlayerInfor(new INFO(role.cid, role.crc_e, role.crc_c));
Vector3 pos = new Vector3(role.pos.x, role.pos.y, role.pos.z);
if (txtName != null) txtName.text = roleName;
transform.position = pos;
SetModelHostPlayer();
Debug.LogError("Pos Character = " + pos);
joystick = FindAnyObjectByType<Joystick>();
EventBus.Subscribe<JoystickRealeaseEvent>(JoystickRelease);
EventBus.Subscribe<JoystickPressEvent>(JoystickStartDrag);
if (TryGetComponent<PlayerVisual>(out var visual))
{
visual.InitHostPlayerEventDoneHandler();
}
// Create work manager
m_pWorkMan = new CECHPWorkMan(this);
if (m_pWorkMan == null)
{
return;
}
}
private void JoystickStartDrag(JoystickPressEvent joystickPressEvent)
{
_playerStateMachine.ChangeState(_moveState);
}
private void OnDestroy()
{
EventBus.Unsubscribe<JoystickRealeaseEvent>(JoystickRelease);
EventBus.Unsubscribe<JoystickPressEvent>(JoystickStartDrag);
}
public void InitCharacter(info_player_1 role)
{
string roleName = "(Error decoding name)";
//if (role.name != null && role.name.ByteArray != null)
//{
// roleName = Encoding.UTF8.GetString(role.name.ByteArray, 0, role.name.Length);
//}
Vector3 pos = new Vector3(role.pos.x, role.pos.y, role.pos.z);
if (txtName != null) txtName.text = roleName;
transform.position = pos;
SetModelHostPlayer();
Debug.LogError("Pos Character = " + pos);
}
#region Task
public void OnMsgHstTaskData(ECMSG Msg)
{
int cmd = Convert.ToInt32(Msg.dwParam2);
if (cmd == CommandID.TASK_DATA)
{
Debug.Log("[Dat]- OnMsgHstTaskData- TASK_DATA");
//a_LogOutput(1, "[Dat]- EC_HostMsg- OnMsgHstTaskData- TASK_DATA");
//cmd_task_data* pCmd = (cmd_task_data*)Msg.dwParam1;
//ASSERT(pCmd);
//int iActiveListSize = (int)pCmd.active_list_size;
//BYTE* pData = (BYTE*)pCmd + sizeof(size_t);
//void* pActiveListbuf = pData;
//pData += iActiveListSize;
//int iFinishedListSize = *(int*)pData;
//pData += sizeof(int);
//void* pFinishedListBuf = pData;
//pData += iFinishedListSize;
//int iFinishTimeListSize = *(int*)pData;
//pData += sizeof(int);
//void* pFinishTimeListBuf = pData;
//pData += iFinishTimeListSize;
//int iFinishedCountListSize = *(int*)pData;
//pData += sizeof(int);
//void* pFinishedCountListBuf = pData;
//pData += iFinishedCountListSize;
//int iStorageTasksListSize = *(int*)pData;
//pData += sizeof(int);
//void* pStorageTaskListBuf = pData;
//pData += iStorageTasksListSize;
//A3DRELEASE(m_pTaskInterface);
//todo
//var m_pTaskInterface = new CECTaskInterface(this);
//if (!m_pTaskInterface.Init(null, 0, null, 0,
// null, 0, null, 0, null, 0))
//{
// //a_LogOutput(1, "CECHostPlayer::OnMsgHstTaskData, failed to initialize task interface");
// return;
//}
//m_pTaskInterface.CheckPQEnterWorldInit();
//// check if player has equipped goblin
//if (m_pEquipPack.GetItem(EQUIPIVTR_GOBLIN) != NULL)
//{
// CECIvtrGoblin* pIvtrGoblin = (CECIvtrGoblin*)m_pEquipPack.GetItem(EQUIPIVTR_GOBLIN);
// m_pGoblin = new CECHostGoblin();
// m_pGoblin.Init(pIvtrGoblin.GetTemplateID(), pIvtrGoblin, this);
//}
//// Note: this command now is also used as the end flag of responding
//// for GET_ALL_DATA request
//g_pGame.GetGameSession().LoadConfigData();
//// ¸ù¾Ý×°±¸°ü¹ü¸üÐÂ×°±¸¼¼Äܵ½¼¼ÄÜÁбí
//if (UpdateEquipSkills())
// UpdateEquipSkillCoolDown();
}
else if (cmd == CommandID.TASK_VAR_DATA)
{
//cmd_task_var_data* pCmd = (cmd_task_var_data*)Msg.dwParam1;
//ASSERT(pCmd);
//if (m_pTaskInterface)
// OnServerNotify(m_pTaskInterface, pCmd.data, pCmd.size);
//else
// ASSERT(m_pTaskInterface);
}
}
#endregion
private bool NormalAttackObject(int idTarget, bool bForceAttack, bool bMoreClose = false)
{
if (idTarget == 0 || idTarget == m_PlayerInfo.cid)
{
// We should have check target isn't dead
return false;
}
//if (!EC_Game.GetGameRun().GetWorld().GetObject(idTarget, 1))
// return false;
bool bStartNewWork = false;
bool bUseAutoPF = false;
//CECPlayerWrapper* pWrapper = CECAutoPolicy::GetInstance().GetPlayerWrapper();
//if (CECAutoPolicy::GetInstance().IsAutoPolicyEnabled() && pWrapper.GetAttackError() >= 2)
bUseAutoPF = true;
CECHPWorkTrace pWorkTrace = null;
CECHPWork pWork = null;
if ((pWork = m_pWorkMan.GetWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT)) != null)
{
pWorkTrace = pWork as CECHPWorkTrace;
}
else if ((pWork = m_pWorkMan.GetWork(CECHPWork.Host_work_ID.WORK_HACKOBJECT)) != null)
{
if ((pWork as CECHPWorkMelee).GetTarget() == idTarget)
return false; // Host is attacking the target
pWorkTrace = (CECHPWorkTrace)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT);
bStartNewWork = true;
}
else if (m_pWorkMan.CanStartWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT))
{
pWorkTrace = (CECHPWorkTrace)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT);
bStartNewWork = true;
}
if (pWorkTrace != null)
{
pWorkTrace.SetTraceTarget(pWorkTrace.CreatTraceTarget(idTarget, CECHPWorkTrace.Trace_reason.TRACE_ATTACK, bForceAttack), bUseAutoPF);
pWorkTrace.SetMoveCloseFlag(bMoreClose);
if (bStartNewWork)
m_pWorkMan.StartWork_p1(pWorkTrace);
return true;
}
return false;
}
public int AttackableJudge(int idTarget, bool bForceAttack)
{
if (CannotAttack())
return 0;
//if (CDlgAutoHelp::IsAutoHelp())
// return 0;
if (idTarget == 0 || idTarget == m_PlayerInfo.cid)
return -1;
CECObject pObject = EC_ManMessageMono.Instance.GetObject(idTarget, 1);
if (!pObject)
return -1;
// If target is pet, it's attacked possibility depends on it's monster
if (GPDataTypeHelper.ISNPCID(idTarget))
{
CECNPC pNPC = (CECNPC)pObject;
int idMaster = pNPC.GetMasterID();
if (idMaster != 0)
{
// master¿ÉÄÜÊÇhostplayer
if (idMaster == m_PlayerInfo.cid)
return 0;
//// Follow pet cannot be attacked
//if (pNPC.IsPetNPC() && ((CECPet)pNPC).IsFollowPet())
// return 0;
idTarget = idMaster;
pObject = EC_ManMessageMono.Instance.GetObject(idTarget, 1);
if (!pObject)
return -1;
}
}
int iRet = 0;
if (GPDataTypeHelper.ISNPCID(idTarget))
{
CECNPC pNPC = (CECNPC)pObject;
// If this npc is host's pet, cannot be attacked
//if (pNPC.GetMasterID() == m_PlayerInfo.cid)
// return 0;
// If it's a pet and can not be attacked, pet can be attacked only if it's a fighting pet
//if (pNPC.IsPetNPC() && !((CECPet)pNPC).CanBeAttacked())
// return 0;
if (IsInBattle()) // Host is in battle
{
if (InSameBattleCamp(pNPC))
iRet = 0;
else
{
if (pNPC.IsMonsterNPC())
iRet = 1;
else if (pNPC.IsServerNPC() && (IsInFortress() || pNPC.GetRoleInBattle() == 8)) // ¶Ô·þÎñÐÍNPCµÄ¹¥»÷£¬°ïÅÉ»ùµØ»ò³Çսʱ¿ÉÓÃ
iRet = 1;
else
iRet = 0;
}
}
else if (pNPC.IsServerNPC())
{
// In sanctuary we cannot attack NPCs
if (!IsPVPOpen() || m_bInSanctuary || !bForceAttack)
iRet = 0;
else
iRet = 1;
}
else // Is monster
{
iRet = 1;
}
if (iRet == 1 && pNPC.GetOwnerFaction() > 0)
{
// Õë¶Ô°ïÅÉ PVP Õ½ÕùÖнûÖ¹²¿·Ö¹¥»÷
if (GetFactionID() == pNPC.GetOwnerFaction() || // ²»¹¥»÷ͬ°ï¹Ö
pNPC.IsFactionPVPMineCar() && !CanAttackFactionPVPMineCar() || // ÎÞ·¨ÔÙ¹¥»÷Ëû°ï¿ó³µÇé¿ö
pNPC.IsFactionPVPMineBase() && !CanAttackFactionPVPMineBase())
{ // ÎÞ·¨ÔÙ¹¥»÷Ëû°ï´æ¿óµãÇé¿ö
iRet = 0;
}
}
}
// TO DO: fix later
//else if (GPDataTypeHelper.ISPLAYERID(idTarget))
//{
// // Check duel at first
// if (m_pvp.iDuelState == Duel_state.DUEL_ST_INDUEL && m_pvp.idDuelOpp == idTarget)
// return 1;
// else if (m_pvp.iDuelState == Duel_state.DUEL_ST_STOPPING && m_pvp.idDuelOpp == idTarget)
// return 0;
// // In sanctuary we cannot attack other players
// if (m_bInSanctuary)
// return 0;
// //ASSERT(pObject.GetClassID() == CECObject::OCID_ELSEPLAYER);
// EC_ElsePlayer pPlayer = (EC_ElsePlayer)pObject;
// ROLEBASICPROP bp = pPlayer.GetBasicProps();
// EC_GAME_SETTING gs = g_pGame.GetConfigs().GetGameSettings();
// if (m_pvp.bFreePVP)
// {
// if (IsTeamMember(idTarget))
// return 0;
// // In free pvp mode, for example, host is in arena.
// if (bForceAttack)
// iRet = 1;
// else if (gs.bAtk_NoMafia && IsFactionMember(pPlayer.GetFactionID()))
// iRet = 0;
// else if (gs.bAtk_NoWhite && !pPlayer.IsInvader() && !pPlayer.IsPariah())
// iRet = 0;
// else if (gs.bAtk_NoAlliance && g_pGame.GetFactionMan().IsFactionAlliance(pPlayer.GetFactionID()))
// iRet = 0;
// else if (gs.bAtk_NoForce && GetForce() > 0 && GetForce() == pPlayer.GetForce())
// iRet = 0;
// else
// iRet = 1;
// }
// else if (m_iBattleCamp != GP_BATTLE_CAMP_NONE)
// {
// // Host is in battle
// int iCamp = pPlayer.GetBattleCamp();
// if (iCamp != GP_BATTLE_CAMP_NONE && iCamp != m_iBattleCamp)
// iRet = 1;
// else
// iRet = 0;
// }
// else // Normal mode
// {
// if (IsTeamMember(idTarget))
// return 0;
// if (!IsPVPOpen() || !pPlayer.IsPVPOpen() || m_BasicProps.iLevel < EC_MAXNOPKLEVEL || bp.iLevel < EC_MAXNOPKLEVEL)
// iRet = 0;
// else if (bForceAttack)
// iRet = 1;
// else if (!gs.bAtk_Player)
// iRet = 0;
// else if (gs.bAtk_NoMafia && IsFactionMember(pPlayer.GetFactionID()))
// iRet = 0;
// else if (gs.bAtk_NoWhite && !pPlayer.IsInvader() && !pPlayer.IsPariah())
// iRet = 0;
// else if (gs.bAtk_NoAlliance && g_pGame.GetFactionMan().IsFactionAlliance(pPlayer.GetFactionID()))
// iRet = 0;
// else if (gs.bAtk_NoForce && GetForce() > 0 && GetForce() == pPlayer.GetForce())
// iRet = 0;
// else
// iRet = 1;
// }
//}
else
{
return -1;
}
return iRet;
}
private float A3d_Magnitude(A3DVECTOR3 v)
{
return Mathf.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
public int GetCharacterID() { return m_PlayerInfo.cid; }
public bool CannotAttack() { return (m_dwLIES & (uint)Logic_Influence_Extned_states.LIES_DISABLEFIGHT) != 0; }
public bool CanTouchTarget(A3DVECTOR3 vHostPos, A3DVECTOR3 vTargetPos, float fTargetRad, int iReason, float fMaxCut = 1.0f)
{
float fDist = A3d_Magnitude(vTargetPos - vHostPos);
switch (iReason)
{
case 1: // melee
{
float fRange;
if (fMaxCut >= 0.0f)
{
float fCutDist = m_ExtProps.ak.AttackRange * 0.3f;
if (fCutDist > fMaxCut)
fCutDist = fMaxCut;
fRange = m_ExtProps.ak.AttackRange - fCutDist;
}
else
fRange = m_ExtProps.ak.AttackRange * 0.7f;
if (fDist - fTargetRad <= fRange)
return true;
break;
}
//case 2: // cast magic
//{
// if (m_pPrepSkill)
// {
// float fRange = m_pPrepSkill.GetCastRange(m_ExtProps.ak.AttackRange, GetPrayDistancePlus());
// if (fRange > 0.0f)
// {
// if (fDist - fTargetRad <= fRange)
// return true;
// }
// else
// return true;
// }
// break;
//}
case 3: // talk
{
if (fDist - fTargetRad <= 5.0f)
return true;
break;
}
default: // no special reason
{
if (fDist < (fTargetRad + m_fTouchRad) * 3.0f)
return true;
break;
}
}
return false;
}
public bool CanTouchTarget(A3DVECTOR3 vTargetPos, float fTargetRad, int iReason, float fMaxCut = 1.0f)
{
A3DVECTOR3 vector = new A3DVECTOR3(gameObject.transform.position.x, gameObject.transform.position.y, gameObject.transform.position.z);
return CanTouchTarget(vector, vTargetPos, fTargetRad, iReason, fMaxCut);
}
public bool IsRooting()
{
var mask = (uint)(Logic_Influence_Extned_states.LIES_ROOT
| Logic_Influence_Extned_states.LIES_SLEEP
| Logic_Influence_Extned_states.LIES_STUN);
return (m_dwLIES & mask) != 0;
}
bool IsInFortress() { return m_fortressEnter.role_in_war != 0; }
bool IsPVPOpen() { return m_pvp.bEnable; }
// Get faction ID
int GetFactionID() { return m_idFaction; }
public bool IsJumping() { return m_iJumpCount > 0; }
public bool IsPlayingAction(int iAction)
{
if (iAction == (int)PLAYER_ACTION_TYPE.ACT_WALK && _playerStateMachine.State is PlayerMoveState)
{
return true;
}
if (iAction == (int)PLAYER_ACTION_TYPE.ACT_STAND && _playerStateMachine.State is PlayerIdleState)
{
return true;
}
return false;
}
public void ResetJump() { m_iJumpCount = 0; m_bJumpInWater = false; }
// Get move speed
public float GetFlySpeed() { return m_ExtProps.mv.flight_speed; }
public float GetSwimSpeed() { return m_ExtProps.mv.swim_speed; }
//public float GetSwimSpeedSev()
//{
// float fSpeedSev = GetSwimSpeed();
// while (true)
// {
// if (!IsUnderWater()) break;
// //CECWorld* pWorld = g_pGame.GetGameRun().GetWorld();
// //if (!pWorld) break;
// const A3DVECTOR3 vPos = GetPos();
// float fTerrainHeight = pWorld.GetTerrainHeight(vPos);
// float fWaterHeight = pWorld.GetWaterHeight(vPos);
// if (fWaterHeight <= fTerrainHeight) break;
// float fBorderLine = fWaterHeight - 2.0f;
// if (vPos.y <= fBorderLine) break;
// // ·þÎñÆ÷¶Ë½«Ë®ÃæÒÔÏÂ2Ã×ÒÔÉÏ´¦ÀíΪ run_speed£¨ÓÐÎÊÌ⣩
// // µ«Î´Ê¹ÓüÓËÙ¼¼ÄÜʱ swim_speed СÓÚ run_speed£¬
// // ¿ÉÒÔÔÚË®ÃæÒÔÏÂ2Ã×ÒÔÉÏ»ñÈ¡³¬¹ý swim_speed µÄËÙ¶È£¬Òò´Ë£¬´Ë´¦È¡Á½Õß½ÏСֵΪºÏÀí×ö·¨
// fSpeedSev = min(m_ExtProps.mv.run_speed, fSpeedSev);
// break;
// }
// return fSpeedSev;
//}
}
public enum StateAnim
{
Idle = 1,
Walk = 2,
Run = 3,
Jump = 4
}
// Mask of some special extend states which will influence host game logic.
// Logic Influence Extned states
[Flags]
public enum Logic_Influence_Extned_states
{
LIES_SLEEP = 0x0001,
LIES_STUN = 0x0002,
LIES_ROOT = 0x0004,
LIES_NOFGIHT = 0x0008,
LIES_DISABLEFIGHT = 0x000B,
}
// ½øÈë»ùµØÐÅÏ¢
public struct FACTION_FORTRESS_ENTER
{
public int faction_id;
public int role_in_war; // 0 : ÖÐÁ¢»ò²»ÔÚ»ùµØ 1:¹¥·½ 2: ÊØ·½
public int end_time;
public FACTION_FORTRESS_ENTER(int faction_id, int role_in_war, int end_time)
{
this.faction_id = faction_id;
this.role_in_war = role_in_war;
this.end_time = end_time;
}
}
// PVP infomation
public struct PVPINFO
{
public bool bEnable; // PVP switch
public uint dwCoolTime;
public uint dwMaxCoolTime;
public bool bFreePVP; // Free PVP flag, ignore bEnable flag
public bool bInPVPCombat; // true, in PVP combat
public int iDuelState; // Duel state
public int idDuelOpp; // Duel opponent
public int iDuelTimeCnt; // Duel time counter
public int iDuelRlt; // Duel result. 0, no defined; 1-win; 2-lose; 3-draw
};
// Current ground information
public struct GNDINFO
{
public float fGndHei; // Ground height
public float fWaterHei; // Water height
public A3DVECTOR3 vGndNormal; // Terrain normal
public bool bOnGround; // On ground flag
};