4115 lines
162 KiB
C#
4115 lines
162 KiB
C#
using BrewMonster.Assets.PerfectWorld.Scripts.Players;
|
||
using BrewMonster.Managers;
|
||
using BrewMonster.Network;
|
||
using BrewMonster.PerfectWorld.Scripts.Vfx;
|
||
using BrewMonster.Scripts;
|
||
using BrewMonster.Scripts.Managers;
|
||
using BrewMonster.Scripts.Pet;
|
||
using BrewMonster.Scripts.Skills;
|
||
using BrewMonster.Scripts.World;
|
||
using BrewMonster.UI;
|
||
using CSNetwork;
|
||
using CSNetwork.GPDataType;
|
||
using CSNetwork.Protocols.RPCData;
|
||
using Cysharp.Threading.Tasks;
|
||
using ModelRenderer.Scripts.GameData;
|
||
using PerfectWorld.Scripts;
|
||
using PerfectWorld.Scripts.Managers;
|
||
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using static BrewMonster.CECPlayer;
|
||
using cmd_select_target = CSNetwork.GPDataType.cmd_select_target;
|
||
using cmd_player_chgshape = CSNetwork.GPDataType.cmd_player_chgshape;
|
||
using Host_work_ID = BrewMonster.Scripts.CECHPWork.Host_work_ID;
|
||
using ObjectCoords = System.Collections.Generic.List<CSNetwork.GPDataType.OBJECT_COORD>;
|
||
using Trace_reason = BrewMonster.CECHPWorkTrace.Trace_reason;
|
||
|
||
namespace BrewMonster
|
||
{
|
||
public partial class CECHostPlayer : CECPlayer
|
||
{
|
||
[SerializeField] private CharacterController controller;
|
||
|
||
[SerializeField] private Joystick joystick;
|
||
[SerializeField] private Button btnJump;
|
||
[SerializeField] private Button btnRun;
|
||
|
||
public CECHostMove m_MoveCtrl;
|
||
|
||
private CECHPWorkMan m_pWorkMan; // Host work manager
|
||
private uint m_dwLIES; // Logic-influence extend states
|
||
private FACTION_FORTRESS_ENTER m_fortressEnter; // ½øÈë»ùµØÐÅÏ¢
|
||
//private PVPINFO m_pvp; // pvp information
|
||
//private bool m_bInSanctuary = false; // true, player is in sanctuary
|
||
//private int m_idFaction = 0; // ID of player's faction
|
||
public bool m_bPrepareFight = false; // true, prepare to fight
|
||
private int m_iJumpCount = 0;
|
||
private bool m_bJumpInWater = false;
|
||
|
||
public A3DVECTOR3 m_vVelocity; // Velocity
|
||
|
||
|
||
bool m_bChangingFace; // true, host is changing face
|
||
private int m_iRoleCreateTime;
|
||
private int m_iRoleLastLoginTime; // Role last login time
|
||
private int m_iAccountTotalCash;
|
||
|
||
private CECPetCorral m_pPetCorral;
|
||
private List<CECObject> m_aTabSels = new List<CECObject>();
|
||
private List<CECSkill> m_aPtSkills = new List<CECSkill>();
|
||
private List<CECSkill> m_aPsSkills = new List<CECSkill>();
|
||
private List<CECSkill> m_aEquipSkills = new List<CECSkill>();
|
||
private List<CECSkill> m_aGoblinSkills = new List<CECSkill>();
|
||
|
||
#if UNITY_EDITOR
|
||
[SerializeField] private int m_startingSkillID = 0; // Starting skill ID for H key shortcut (0 = use cycling, >0 = use specific ID)
|
||
private int m_currentSkillCycleIndex = 0;
|
||
#endif
|
||
|
||
private bool m_bEnterGame;
|
||
public CECSkill m_pPrepSkill;
|
||
private float playerSpeed = 5.0f;
|
||
private float jumpHeight = 1.5f;
|
||
private float gravityValue = -9.81f;
|
||
private StateAnim stateAnim = StateAnim.Idle;
|
||
private Vector3 playerVelocity;
|
||
private bool isGrounded = false;
|
||
private bool isRun = false;
|
||
private Vector3 m_vLastSevPos;
|
||
public CDR_INFO m_CDRInfo;
|
||
public GNDINFO m_GndInfo;
|
||
private int m_idUCSelTarget; // Uncertificately selected object's ID
|
||
public float m_fVertSpeed = 0f;
|
||
int m_idSevNPC = 0; // Current service NPC
|
||
bool m_bTalkWithNPC = false; // true, is talking with NPC
|
||
List<ushort> m_aWayPoints = new List<ushort>(); // Active way points
|
||
bool m_bIsInKingService = false; // ÊÇ·ñÕýÔÚ½øÐйúÍõ·þÎñ²Ù×÷
|
||
CECActionSwitcherBase m_pActionSwitcher;
|
||
private int m_iWorldContribution;
|
||
private int m_iWorldContributionSpend;
|
||
private bool m_bSpellDSkill;
|
||
private CECSkill m_pTargetItemSkill; // Target item skill
|
||
public CECComboSkill m_pComboSkill; // Combo skill
|
||
public A3DVECTOR3 m_vAccel; // Accelerate\
|
||
public byte m_RealmLevel;
|
||
REINCARNATION_TOME m_ReincarnationTome; // תÉú
|
||
public bool m_bRushFly = false; // true, in rush fly mode
|
||
|
||
private CECCounter m_IncantCnt;
|
||
private bool m_bMelee;
|
||
private int MAX_JUMP_COUNT = 2;
|
||
bool m_bUsingTrashBox = false; // Whether being using trash box
|
||
private float m_fPrayDistancePlus;
|
||
private bool m_bInRebuildPet = false; // Whether rebuilding pet / 是否正在重建宠物
|
||
private uint m_dwGMFlags = 0; // GM flags / GM标志
|
||
private A3DVECTOR3 g_vOrigin = new A3DVECTOR3(0f);
|
||
|
||
private float EC_SLOPE_Y = 0.5f;
|
||
int m_iOldWalkMode = Move_Mode.MOVE_STAND; // Copy of work mode
|
||
public uint m_dwMoveRelDir = 0; // Move relative direction flags
|
||
public ON_AIR_CDR_INFO m_AirCDRInfo;
|
||
public ushort m_wMoveStamp = 0;
|
||
private CECCounter m_GatherCnt; // Gather counter
|
||
Dictionary<int, COOLTIME> m_skillCoolTime = new Dictionary<int, COOLTIME>();
|
||
COOLTIME[] m_aCoolTimes = new COOLTIME[(int)CoolTimeIndex.GP_CT_MAX]; // Cool times
|
||
|
||
CECCounter m_PetOptCnt = new CECCounter(); // Pet operation time counter
|
||
protected bool[] m_playerLimits = new bool[(int)PLAYER_LIMIT.PLAYER_LIMIT_MAX];
|
||
|
||
// תÉú´ÎÊý
|
||
byte m_ReincarnationCount = 0;
|
||
|
||
// Host config data version
|
||
const int HOSTCFG_VERSION = 11;
|
||
|
||
// Favorite auction version
|
||
const int FAVOR_AUCTION_VERSION = 1;
|
||
|
||
// ID of return-town skill
|
||
const int ID_RETURNTOWN_SKILL = 167;
|
||
|
||
// ID of summon player skill
|
||
const int ID_SUMMONPLAYER_SKILL = 1824;
|
||
|
||
private CECAutoTeam m_pAutoTeam;
|
||
private CECTeam m_pTeam; // Current team (null when not in team)
|
||
|
||
// ====== Ground cast config ======
|
||
[Header("Ground Cast")]
|
||
[Tooltip("Khoảng thêm ngoài skinWidth để SphereCast xuống (m ngắn)")]
|
||
[SerializeField]
|
||
private float extraGroundDistance = 1f;
|
||
|
||
[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 = 1 << 6;
|
||
|
||
[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;
|
||
Ray ray;
|
||
RaycastHit[] hits = new RaycastHit[5];
|
||
bool isDataAwaitToReady = false;
|
||
|
||
private BaseVfxObject m_pSelectedGFX;
|
||
private BaseVfxObject m_pHoverGFX;
|
||
|
||
// Cursor estimation optimization
|
||
private Vector2 m_lastMousePosition;
|
||
private float m_cursorUpdateTimer;
|
||
private const float CURSOR_UPDATE_INTERVAL = 0.05f; // 20 times per second instead of 60+
|
||
private UnityEngine.InputSystem.Mouse m_cachedMouse;
|
||
private UnityEngine.InputSystem.Keyboard m_cachedKeyboard;
|
||
|
||
int[] targetsCastSkill;
|
||
|
||
public bool IsChangingFace()
|
||
{
|
||
return m_bChangingFace;
|
||
}
|
||
|
||
// ===== Inventory packs (instance-based) =====
|
||
// 0 = normal pack, 1 = equip pack, 2 = task pack (see InventoryConst.IVTRTYPE_*)
|
||
private readonly EC_Inventory m_pPack = new EC_Inventory();
|
||
/// <summary>
|
||
/// m_pEquipPack is changed to m_equipInventory
|
||
/// </summary>
|
||
private readonly EC_Inventory m_pEquipPack = new EC_Inventory();
|
||
private readonly EC_Inventory m_pTaskPack = new EC_Inventory();
|
||
|
||
public EC_Inventory IvtrPack => m_pPack;
|
||
public EC_Inventory IvtrEquipPack => m_pEquipPack;
|
||
public EC_Inventory IvtrTaskPack => m_pTaskPack;
|
||
|
||
public Transform PointCam { get => pointCam; }
|
||
|
||
private void OnApplicationQuit()
|
||
{
|
||
if (m_pTaskInterface != null)
|
||
m_pTaskInterface.Despose();
|
||
}
|
||
|
||
public bool IsMeleeing()
|
||
{
|
||
return m_bMelee;
|
||
}
|
||
|
||
public CECAutoTeam GetAutoTeam()
|
||
{
|
||
return m_pAutoTeam;
|
||
}
|
||
|
||
public override CECTeam GetTeam()
|
||
{
|
||
return m_pTeam;
|
||
}
|
||
|
||
internal void SetTeam(CECTeam team)
|
||
{
|
||
m_pTeam = team;
|
||
}
|
||
public EC_Inventory GetPack()
|
||
{
|
||
return m_pPack;
|
||
}
|
||
public EC_Inventory GetTaskPack()
|
||
{
|
||
return m_pTaskPack;
|
||
}
|
||
|
||
public EC_Inventory GetEquipment()
|
||
{
|
||
return m_pEquipPack;
|
||
}
|
||
|
||
// Get work manager // 获取工作管理器
|
||
public CECHPWorkMan GetWorkMan()
|
||
{
|
||
return m_pWorkMan;
|
||
}
|
||
|
||
// Get navigate player // 获取导航玩家
|
||
public int GetWorldContribution() { return m_iWorldContribution; }
|
||
public int GetWorldContributionSpend() { return m_iWorldContributionSpend; }
|
||
private CECHostNavigatePlayer m_pNavigatePlayer = null;
|
||
public CECHostNavigatePlayer GetNavigatePlayer()
|
||
{
|
||
if (m_pNavigatePlayer == null)
|
||
{
|
||
// Create navigate player on-demand (C++: host owns a navigate/clone player + controller)
|
||
// 按需创建导航玩家(C++:宿主玩家持有导航/克隆玩家 + 控制器)
|
||
m_pNavigatePlayer = new CECHostNavigatePlayer(this);
|
||
}
|
||
return m_pNavigatePlayer;
|
||
}
|
||
|
||
// Check if in force navigate state // 检查是否在强制导航状态
|
||
public bool IsInForceNavigateState()
|
||
{
|
||
CECHostNavigatePlayer pNavigatePlayer = GetNavigatePlayer();
|
||
if (pNavigatePlayer != null && pNavigatePlayer.GetNavigateCtrl() != null)
|
||
{
|
||
return pNavigatePlayer.GetNavigateCtrl().IsInForceNavigateState();
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// Handle navigation event // 处理导航事件
|
||
public void OnNaviageEvent(int task, int e)
|
||
{
|
||
UnityEngine.Debug.Log($"[CECHostPlayer] OnNaviageEvent: Task={task}, Event={e} ({(BrewMonster.Scripts.CECNavigateCtrl.NavigateEvent)e})");
|
||
CECHostNavigatePlayer pNavigatePlayer = GetNavigatePlayer();
|
||
if (pNavigatePlayer != null)
|
||
{
|
||
UnityEngine.Debug.Log($"[CECHostPlayer] OnNaviageEvent: Forwarding to NavigatePlayer");
|
||
pNavigatePlayer.OnNavigateEvent(task, e);
|
||
}
|
||
else
|
||
{
|
||
UnityEngine.Debug.LogWarning($"[CECHostPlayer] OnNaviageEvent: NavigatePlayer is null");
|
||
}
|
||
}
|
||
|
||
public EC_Inventory GetInventory(byte byPackage)
|
||
{
|
||
switch (byPackage)
|
||
{
|
||
case InventoryConst.IVTRTYPE_PACK:
|
||
return m_pPack;
|
||
case InventoryConst.IVTRTYPE_EQUIPPACK:
|
||
return m_pEquipPack;
|
||
case InventoryConst.IVTRTYPE_TASKPACK:
|
||
return m_pTaskPack;
|
||
default:
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
base.Awake();
|
||
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;
|
||
}
|
||
|
||
// 任务状态检查节流(毫秒) // Throttle task status check (milliseconds)
|
||
// NOTE: CECCounter uses "Period" as threshold; SetCounter only sets current value.
|
||
// We want task checking roughly every 3 seconds, not every frame.
|
||
m_TaskCounter.SetPeriod(3000f);
|
||
m_TaskCounter.Reset(true); // trigger first check immediately
|
||
m_bTitleDataReady = false;
|
||
//playerTransform = transform;
|
||
m_GatherCnt ??= new CECCounter();
|
||
m_GatherCnt.SetPeriod(1000);
|
||
m_GatherCnt.Reset(true);
|
||
m_PetOptCnt.SetPeriod(1000);
|
||
m_PetOptCnt.Reset(true);
|
||
|
||
|
||
CreateInventories();
|
||
// run a process on background to keep track of task status.
|
||
TickTask().Forget();
|
||
}
|
||
|
||
public bool LoadResources()
|
||
{
|
||
//BMLogger.LogError("HoangDev: CECHostPlayer::LoadResources");
|
||
RoleInfo RoleInfo = UnityGameSession.Instance.GetRoleInfo();
|
||
m_iProfession = RoleInfo.occupation;
|
||
m_iGender = RoleInfo.gender;
|
||
|
||
m_iRoleCreateTime = RoleInfo.create_time;
|
||
m_iRoleLastLoginTime = RoleInfo.lastlogin_time;
|
||
m_iAccountTotalCash = RoleInfo.cash_add;
|
||
|
||
EC_Game.GetGameRun().AddPlayerName(m_PlayerInfo.cid, m_strName);
|
||
|
||
if (!LoadPlayerSkeleton(true))
|
||
{
|
||
BMLogger.LogError("HoangDev CECHostPlayer::LoadResources, Failed to load skeleton");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private bool LoadPlayerSkeleton(bool bAtOnce)
|
||
{
|
||
EC_PLAYERLOADRESULT Ret = default;
|
||
if (bAtOnce /*|| !IsLoadThreadReady()*/)
|
||
{
|
||
// Under normal circumstances, only HostPlayer can reach here
|
||
/* if (!LoadPlayerModel(m_iProfession, m_iGender, m_CustomizeData.bodyID, aEquips, szPetPath, Ret, false, false))
|
||
{
|
||
a_LogOutput(1, "CECPlayer::Init, failed to call LoadPlayerModel() !");
|
||
return false;
|
||
}*/
|
||
|
||
SetPlayerLoadedResult(Ret);
|
||
|
||
/* if (IsShapeChanged() && !QueueLoadDummyModel(m_iShape, true))
|
||
{
|
||
// ignore the dummy model loading failure
|
||
a_LogOutput(1, "CECPlayer::Init, failed to call QueueLoadDummyModel() !");
|
||
}*/
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private bool SetPlayerLoadedResult(EC_PLAYERLOADRESULT ret)
|
||
{
|
||
OnAllResourceReady();
|
||
return true;
|
||
}
|
||
|
||
private void OnAllResourceReady()
|
||
{
|
||
CECHostSkillModel.Instance.Initialize();
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
mainCam = Camera.main;
|
||
if (mainCam == null)
|
||
{
|
||
mainCam = FindFirstObjectByType<Camera>();
|
||
}
|
||
// btnJump.onClick.AddListener(HandleJump);
|
||
|
||
// Cache input devices for better performance
|
||
m_cachedMouse = UnityEngine.InputSystem.Mouse.current;
|
||
m_cachedKeyboard = UnityEngine.InputSystem.Keyboard.current;
|
||
}
|
||
|
||
protected override void Update()
|
||
{
|
||
base.Update();
|
||
|
||
if (!isDataAwaitToReady)
|
||
{
|
||
return;
|
||
}
|
||
// Always set the pointCam's world rotation value to Vector3.zero
|
||
// because rotating the character with the camera will cause a lagging effect.
|
||
if (pointCam != null)
|
||
{
|
||
pointCam.rotation = Quaternion.identity;
|
||
}
|
||
|
||
// Update cursor based on what's under mouse
|
||
EstimateCursor();
|
||
//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;
|
||
EstimateMoveEnv(GetPos());
|
||
OnKeyDown();
|
||
|
||
// Update timers
|
||
UpdateTimers(Time.deltaTime);
|
||
|
||
m_pWorkMan?.Tick(Time.deltaTime);
|
||
|
||
// Update GFXs
|
||
UpdateGFXs(Time.deltaTime);
|
||
|
||
//m_dwMoveRelDir = 0;
|
||
m_fVertSpeed = 0.0f;
|
||
// Auto team / Automatic party grouping
|
||
// m_pAutoTeam.Tick(Time.deltaTime);
|
||
|
||
// TODO: Remove later
|
||
// UpdatePosWing();
|
||
}
|
||
|
||
//public void HandleMovement()
|
||
//{
|
||
// // 1) Kiểm tra grounded bằng SphereCast ngắn dựa trên radius + skinWidth
|
||
// isGrounded = GroundCheck(out lastGroundHit);
|
||
// m_GndInfo.bOnGround = isGrounded;
|
||
// // 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)
|
||
// {
|
||
// Vector3 finalMove = (move * playerSpeed) + (playerVelocity.y * Vector3.up);
|
||
// controller.Move(finalMove * Time.deltaTime);
|
||
// transform.forward = move;
|
||
// m_MoveCtrl.GroundMove(Time.deltaTime);
|
||
// m_MoveCtrl.SendMoveCmd(playerTransform.position, controller.velocity, (int)GPMoveMode.GP_MOVE_RUN);
|
||
// m_aabb.Center = EC_Utility.ToA3DVECTOR3(playerTransform.position) +
|
||
// new A3DVECTOR3(0.0f, m_aabb.Extents.y, 0.0f);
|
||
// m_aabb.CompleteMinsMaxs();
|
||
// m_aabbServer.Center = EC_Utility.ToA3DVECTOR3(playerTransform.position) +
|
||
// new A3DVECTOR3(0.0f, m_aabbServer.Extents.y, 0.0f);
|
||
// m_aabbServer.CompleteMinsMaxs();
|
||
// }
|
||
// else
|
||
// {
|
||
// }
|
||
//}
|
||
|
||
private void JoystickRelease(JoystickRealeaseEvent joystickRealeaseEvent)
|
||
{
|
||
// _playerStateMachine.ChangeState(_idleState);
|
||
m_dwMoveRelDir = 0;
|
||
}
|
||
public 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)
|
||
{
|
||
var msg = (int)Msg.dwMsg;
|
||
//Debug.LogError("HoangDev : ProcessMessageProcessMessageProcessMessage " + msg);
|
||
switch (msg)
|
||
{
|
||
case EC_MsgDef.MSG_HST_CORRECTPOS: OnMsgHstCorrectPos(Msg); break;
|
||
case EC_MsgDef.MSG_HST_GOTO: OnMsgHstGoto(Msg); break;
|
||
case EC_MsgDef.MSG_HST_IVTRINFO:
|
||
{
|
||
OnMsgHstIvtrInfo(Msg);
|
||
break;
|
||
}
|
||
case EC_MsgDef.MSG_HST_OWNITEMINFO:
|
||
{
|
||
OnMsgHstOwnItemInfo(Msg);
|
||
break;
|
||
}
|
||
case EC_MsgDef.MSG_HST_TASKDATA:
|
||
{
|
||
OnMsgHstTaskData(Msg).Forget();
|
||
//Debug.LogError("[Dat]- OnMsgHstTaskData");
|
||
break;
|
||
}
|
||
case EC_MsgDef.MSG_HST_ITEMOPERATION:
|
||
OnMsgHstItemOperation(Msg);
|
||
break;
|
||
case EC_MsgDef.MSG_HST_PICKUPITEM:
|
||
OnMsgHstPickupItem(Msg);
|
||
break;
|
||
case EC_MsgDef.MSG_HST_PURCHASEITEMS:
|
||
OnMsgHstPurchaseItems(Msg);
|
||
break;
|
||
case EC_MsgDef.MSG_HST_PRODUCEITEM:
|
||
OnMsgHstProduceItem(Msg);
|
||
break;
|
||
case EC_MsgDef.MSG_HST_SELTARGET:
|
||
OnMsgHstSelTarget(Msg); break;
|
||
case EC_MsgDef.MSG_HST_USEITEM:
|
||
OnMsgHstUseItem(Msg);
|
||
break;
|
||
case EC_MsgDef.MSG_HST_ATKRESULT: OnMsgHstAttackResult(Msg); break;
|
||
case EC_MsgDef.MSG_HST_ATTACKED: OnMsgHstAttacked(Msg); break;
|
||
case EC_MsgDef.MSG_HST_HURTRESULT: OnMsgHstHurtResult(Msg); break;
|
||
case EC_MsgDef.MSG_HST_INFO00: OnMsgHstInfo00(Msg); break;
|
||
case EC_MsgDef.MSG_HST_NPCGREETING: OnMsgHstNPCGreeting(Msg); break;
|
||
case EC_MsgDef.MSG_HST_WAYPOINT: OnMsgHstWayPoint(Msg); break;
|
||
case EC_MsgDef.MSG_HST_SKILLDATA: OnMsgHstSkillData(Msg); break;
|
||
case EC_MsgDef.MSG_HST_DIED: OnMsgHstDied(Msg); break;
|
||
case EC_MsgDef.MSG_HST_STARTATTACK: OnMsgHstStartAttack(Msg); break;
|
||
case EC_MsgDef.MSG_HST_STOPATTACK: OnMsgHstStopAttack(Msg); break;
|
||
case EC_MsgDef.MSG_HST_SKILLRESULT: OnMsgHstSkillResult(Msg); break;
|
||
case EC_MsgDef.MSG_PM_CASTSKILL: OnMsgPlayerCastSkill(Msg); break;
|
||
case EC_MsgDef.MSG_HST_SETCOOLTIME: OnMsgHstSetCoolTime(Msg); break;
|
||
case EC_MsgDef.MSG_PM_ENCHANTRESULT: OnMsgEnchantResult(Msg); break;
|
||
case EC_MsgDef.MSG_HST_LEARNSKILL: OnMsgHstLearnSkill(Msg); break;
|
||
case EC_MsgDef.MSG_HST_COMBO_SKILL_PREPARE: OnMsgComboSkillPrepare(Msg); break;
|
||
case EC_MsgDef.MSG_HST_CONTINUECOMBOSKILL: OnMsgContinueComboSkill(Msg); break;
|
||
|
||
case EC_MsgDef.MSG_HST_OWNEXTPROP: OnMsgHstExtProp(Msg); break;
|
||
case EC_MsgDef.MSG_PM_PLAYERDOEMOTE: OnMsgPlayerDoEmote(Msg); break;
|
||
case EC_MsgDef.MSG_HST_TARGETISFAR: OnMsgHstTargetIsFar(Msg); break;
|
||
case EC_MsgDef.MSG_PM_PLAYERGATHER: OnMsgPlayerGather(Msg); break;
|
||
case EC_MsgDef.MSG_HST_COOLTIMEDATA: OnMsgHstCoolTimeData(Msg); break;
|
||
case EC_MsgDef.MSG_HST_PRESSCANCEL: OnMsgHstPressCancel(Msg); break;
|
||
case EC_MsgDef.MSG_PM_PLAYERFLY: OnMsgPlayerFly(Msg); break;
|
||
case EC_MsgDef.MSG_HST_PETOPT: OnMsgHstPetOpt(Msg); break;
|
||
case EC_MsgDef.MSG_HST_SETPLAYERLIMIT: OnMsgHstSetPlayerLimit(Msg); break;
|
||
case EC_MsgDef.MSG_PM_PLAYERMOUNT: OnMsgPlayerMount(Msg); break;
|
||
case EC_MsgDef.MSG_HST_EMBEDITEM: OnMsgHstEmbedItem(Msg); break;
|
||
case EC_MsgDef.MSG_HST_JOINTEAM: OnMsgHstJoinTeam(Msg); break;
|
||
case EC_MsgDef.MSG_HST_LEAVETEAM: OnMsgHstLeaveTeam(Msg); break;
|
||
case EC_MsgDef.MSG_HST_NEWTEAMMEM: OnMsgHstNewTeamMem(Msg); break;
|
||
case EC_MsgDef.MSG_HST_TEAMINVITE: OnMsgHstTeamInvite(Msg); break;
|
||
case EC_MsgDef.MSG_HST_TEAMMEMBERDATA: OnMsgHstTeamMemberData(Msg); break;
|
||
case EC_MsgDef.MSG_PM_DUELOPT: OnMsgHstDuelOpt(Msg); break;
|
||
case EC_MsgDef.MSG_PM_PLAYERCHGSHAPE :
|
||
OnMsgPlayerChgShape(Msg); break;
|
||
case EC_MsgDef.MSG_HST_SETMOVESTAMP: OnMsgHstSetMoveStamp(Msg); break;
|
||
|
||
default:
|
||
// Uncomment to debug unhandled messages
|
||
// Debug.LogWarning($"[CECHostPlayer] ProcessMessage: Unhandled message {msg}");
|
||
break;
|
||
}
|
||
|
||
/*if (bActionStartSkill)
|
||
AP_ActionEvent(AP_EVENT_STARTSKILL, iActionTime);
|
||
|
||
if (bDoOtherThing)
|
||
{
|
||
if (m_pComboSkill != null && !m_pComboSkill.IsStop())
|
||
{
|
||
if (CECAutoPolicy.GetInstance().IsAutoPolicyEnabled())
|
||
g_pGame.GetGameRun().PostMessage(MSG_HST_CONTINUECOMBOSKILL, MAN_PLAYER, 0, 0, m_pComboSkill.GetGroupIndex());
|
||
else
|
||
m_pComboSkill.Continue(false);
|
||
}
|
||
else
|
||
{
|
||
if (idTarget != 0 && idTarget != m_PlayerInfo.cid)
|
||
NormalAttackObject(idTarget, true);
|
||
}
|
||
}*/
|
||
}
|
||
private void NotifyUIUpdateTeam(bool showDialog = false)
|
||
{
|
||
//try
|
||
//{
|
||
// var ui = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan();
|
||
// if (ui is CECGameUIMan gui)
|
||
// gui.UpdateTeam(false);
|
||
//}
|
||
//catch { }
|
||
var uiMan = CECUIManager.Instance?.GetInGameUIMan();
|
||
if (uiMan == null)
|
||
return;
|
||
|
||
// CECGameUIMan will forward to DlgTeamMain if it's open, otherwise do nothing.
|
||
var dlgTeamMain = uiMan.GetDialog("Win_TeamMain") as DlgTeamMain;
|
||
if (dlgTeamMain != null)
|
||
{
|
||
if (showDialog)
|
||
{
|
||
CECUIManager.Instance?.ShowUI("Win_TeamMain");
|
||
dlgTeamMain.ShowTeamDialog();
|
||
}
|
||
else
|
||
{
|
||
dlgTeamMain.UpdateTeamInfo();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
BMLogger.LogWarning("[CECHostPlayer] NotifyUIUpdateTeam: Failed to show DlgTeamMain");
|
||
}
|
||
}
|
||
|
||
/// <summary>Update host duel state from S2C duel commands (MSG_PM_DUELOPT).</summary>
|
||
private void OnMsgHstDuelOpt(ECMSG Msg)
|
||
{
|
||
int cmdId = Convert.ToInt32(Msg.dwParam2);
|
||
byte[] data = Msg.dwParam1 as byte[];
|
||
int idOpp = 0;
|
||
if (data != null && data.Length >= 4)
|
||
idOpp = BitConverter.ToInt32(data, 0);
|
||
switch (cmdId)
|
||
{
|
||
case CommandID.DUEL_RECV_REQUEST:
|
||
// Duel invite: show accept/reject popup (origin MSG_PM_DUELOPT with command DUEL_RECV_REQUEST = 214)
|
||
if (idOpp != 0)
|
||
{
|
||
CECUIManager.Instance?.ShowMessageBox(
|
||
title: "",
|
||
message: "Bạn vừa nhận được lời thách đấu. Bạn có chấp nhận không?",
|
||
messageBoxType: MessageBoxType.BothYesNoButton,
|
||
onClickedYes: () => UnityGameSession.c2s_CmdDuelReply(true, idOpp),
|
||
onClickedNo: () => UnityGameSession.c2s_CmdDuelReply(false, idOpp));
|
||
}
|
||
break;
|
||
case CommandID.DUEL_PREPARE:
|
||
m_pvp.iDuelState = Duel_state.DUEL_ST_PREPARE;
|
||
m_pvp.idDuelOpp = idOpp;
|
||
m_pvp.iDuelTimeCnt = data != null && data.Length >= 8 ? BitConverter.ToInt32(data, 4) : 3000;
|
||
break;
|
||
case CommandID.HOST_DUEL_START:
|
||
m_pvp.iDuelState = Duel_state.DUEL_ST_INDUEL;
|
||
m_pvp.idDuelOpp = idOpp;
|
||
m_pvp.iDuelTimeCnt = 0;
|
||
// Origin: server must be notified of force-attack (PVP) so it accepts attacks on the duel opponent
|
||
NotifyServerForceAttack(true);
|
||
break;
|
||
case CommandID.DUEL_STOP:
|
||
case CommandID.DUEL_RESULT:
|
||
m_pvp.iDuelState = Duel_state.DUEL_ST_STOPPING;
|
||
m_pvp.iDuelTimeCnt = data != null && data.Length >= 8 ? BitConverter.ToInt32(data, 4) : 3000;
|
||
if (data != null && data.Length >= 12)
|
||
m_pvp.iDuelRlt = BitConverter.ToInt32(data, 8);
|
||
NotifyServerForceAttack(false);
|
||
break;
|
||
case CommandID.DUEL_CANCEL:
|
||
case CommandID.DUEL_REJECT_REQUEST:
|
||
m_pvp.iDuelState = Duel_state.DUEL_ST_NONE;
|
||
m_pvp.idDuelOpp = 0;
|
||
m_pvp.iDuelTimeCnt = 0;
|
||
NotifyServerForceAttack(false);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// <summary>Host received a team invite (MSG_HST_TEAMINVITE). Payload is cmd_team_leader_invite: idLeader, seq, pickFlag. Shows accept/reject message box and sends reply.</summary>
|
||
private void OnMsgHstTeamInvite(ECMSG Msg)
|
||
{
|
||
byte[] data = Msg.dwParam1 as byte[];
|
||
int idLeader = 0;
|
||
int team_seq = 0;
|
||
if (data != null && data.Length >= 8)
|
||
{
|
||
idLeader = BitConverter.ToInt32(data, 0);
|
||
team_seq = BitConverter.ToInt32(data, 4);
|
||
}
|
||
else if (data != null && data.Length >= 4)
|
||
{
|
||
idLeader = BitConverter.ToInt32(data, 0);
|
||
}
|
||
if (idLeader == 0)
|
||
return;
|
||
int seqCapture = team_seq;
|
||
CECUIManager.Instance?.ShowMessageBox(
|
||
title: "",
|
||
message: "Bạn đã nhận được lời mời tham gia tổ đội. Bạn có chấp nhận không?",
|
||
messageBoxType: MessageBoxType.BothYesNoButton,
|
||
onClickedYes: () => UnityGameSession.c2s_CmdTeamAgreeInvite(idLeader, seqCapture),
|
||
onClickedNo: () => UnityGameSession.c2s_CmdTeamRejectInvite(idLeader));
|
||
}
|
||
|
||
/// <summary>Called when MSG_PM_PLAYERDUELOPT (229) is received; server may send duel start to both participants. If host is one of the two ids, set duel state.</summary>
|
||
public void OnMsgPlayerDuelStart(byte[] data)
|
||
{
|
||
if (data == null || data.Length < 8) return;
|
||
int id1 = BitConverter.ToInt32(data, 0);
|
||
int id2 = BitConverter.ToInt32(data, 4);
|
||
int cid = m_PlayerInfo.cid;
|
||
if (cid == id1)
|
||
{
|
||
m_pvp.iDuelState = Duel_state.DUEL_ST_INDUEL;
|
||
m_pvp.idDuelOpp = id2;
|
||
m_pvp.iDuelTimeCnt = 0;
|
||
NotifyServerForceAttack(true);
|
||
}
|
||
else if (cid == id2)
|
||
{
|
||
m_pvp.iDuelState = Duel_state.DUEL_ST_INDUEL;
|
||
m_pvp.idDuelOpp = id1;
|
||
m_pvp.iDuelTimeCnt = 0;
|
||
NotifyServerForceAttack(true);
|
||
}
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
/// <summary>
|
||
/// Cycles through learned skills by removing all shortcuts and adding 2 new skills to slots 0 and 1.
|
||
/// If m_startingSkillID is set (>0), uses that specific skill ID and the next one (ID+1).
|
||
/// Otherwise, cycles through all learned skills in pairs.
|
||
/// </summary>
|
||
#endif
|
||
|
||
public bool HostIsReady()
|
||
{
|
||
return m_bEnterGame;
|
||
}
|
||
|
||
void SetLevel2(int level2, bool bFirstTime)
|
||
{
|
||
int lastLevel2 = m_BasicProps.iLevel2;
|
||
m_BasicProps.iLevel2 = level2;
|
||
/*if (CanPlayTaoistEffect(lastLevel2, level2, bFirstTime)){
|
||
PlayTaoistEffect();
|
||
}*/
|
||
}
|
||
|
||
/* public override bool IsWorkMoveRunning()
|
||
{
|
||
return m_pWorkMan.IsMovingToPosition();
|
||
}*/
|
||
|
||
public bool IsPosCollideFree(A3DVECTOR3 vTargetPos)
|
||
{
|
||
bool bAvailable = (false);
|
||
|
||
while (true)
|
||
{
|
||
A3DVECTOR3 refake = new A3DVECTOR3();
|
||
float terrianHeight = CECGameRun.Instance.GetWorld().GetTerrainHeight(vTargetPos, ref refake);
|
||
if (terrianHeight > vTargetPos.y + 1E-4f)
|
||
break;
|
||
|
||
A3DVECTOR3 vExt = m_CDRInfo.vExtent;
|
||
env_trace_t trcInfo = default;
|
||
trcInfo.dwCheckFlag = EC_CDR.CDR_EVN.CDR_BRUSH;
|
||
trcInfo.vExt = vExt;
|
||
trcInfo.vStart = vTargetPos + EC_Utility.ToA3DVECTOR3(g_vAxisY) * vExt.y;
|
||
trcInfo.vDelta = new A3DVECTOR3(0);
|
||
if (EC_CDR.CollideWithEnv(ref trcInfo))
|
||
break;
|
||
|
||
bAvailable = true;
|
||
break;
|
||
}
|
||
|
||
return bAvailable;
|
||
}
|
||
|
||
private float CalcAABBOnCollidePos(A3DAABB aabbTarget)
|
||
{
|
||
// ¸ù¾Ý HostPlayer Óëij¶ÔÏó£¨ÈçNPC£© AABB£¬¼ÆËã AABB ³åÍ»ÁÙ½ç״̬ÏÂÁ½Õß¾àÀë (Compute the separation distance between host/player AABBs when they nearly collide)
|
||
// ·µ»ØÖµÓ¦Êʵ±Ôö¼ÓÒ»¸öÔöÁ¿£¬¼´Àë³åͻλÖÃÉÔԶЩ (Return value should keep a slightly safer distance)
|
||
|
||
float fAABBCollideDist = 0.0f;
|
||
|
||
A3DAABB aabbHost = m_aabb;
|
||
A3DVECTOR3 vHostHeight = new A3DVECTOR3(0.0f, aabbHost.Extents.y, 0.0f);
|
||
A3DVECTOR3 vHostRoot = aabbHost.Center - vHostHeight;
|
||
A3DVECTOR3 vTargetHeight = new A3DVECTOR3(0.0f, aabbTarget.Extents.y, 0.0f);
|
||
A3DVECTOR3 vTargetRoot = aabbTarget.Center - vTargetHeight;
|
||
|
||
A3DVECTOR3 vRootDir = vTargetRoot - vHostRoot;
|
||
float fRootDist = vRootDir.Normalize();
|
||
float fMinDist = Mathf.Min(Mathf.Min(aabbHost.Extents.x, aabbHost.Extents.y), aabbHost.Extents.z);
|
||
fMinDist = Mathf.Max(fMinDist, 0.01f);
|
||
|
||
if (fRootDist >= fMinDist)
|
||
{
|
||
A3DVECTOR3 vCenterDelta0 = aabbHost.Center - aabbTarget.Center;
|
||
A3DVECTOR3 vTargetExt = aabbTarget.Extents;
|
||
A3DVECTOR3 vHostExt = aabbHost.Extents;
|
||
A3DVECTOR3 vSumExt = vTargetExt + vHostExt;
|
||
|
||
A3DVECTOR3 t = new A3DVECTOR3(0.0f);
|
||
const float fZero = 0.001f;
|
||
|
||
if (Mathf.Abs(vRootDir.x) >= fZero)
|
||
{
|
||
float t1 = (vSumExt.x - vCenterDelta0.x) / vRootDir.x;
|
||
float t2 = (vSumExt.x + vCenterDelta0.x) / -vRootDir.x;
|
||
if (t1 >= fZero && t1 < fRootDist) t.x = t1;
|
||
if (t2 >= fZero && t2 < fRootDist && t2 > t.x) t.x = t2;
|
||
}
|
||
|
||
if (Mathf.Abs(vRootDir.y) >= fZero)
|
||
{
|
||
float t1 = (vSumExt.y - vCenterDelta0.y) / vRootDir.y;
|
||
float t2 = (vSumExt.y + vCenterDelta0.y) / -vRootDir.y;
|
||
if (t1 >= fZero && t1 < fRootDist) t.y = t1;
|
||
if (t2 >= fZero && t2 < fRootDist && t2 > t.y) t.y = t2;
|
||
}
|
||
|
||
if (Mathf.Abs(vRootDir.z) >= fZero)
|
||
{
|
||
float t1 = (vSumExt.z - vCenterDelta0.z) / vRootDir.z;
|
||
float t2 = (vSumExt.z + vCenterDelta0.z) / -vRootDir.z;
|
||
if (t1 >= fZero && t1 < fRootDist) t.z = t1;
|
||
if (t2 >= fZero && t2 < fRootDist && t2 > t.z) t.z = t2;
|
||
}
|
||
|
||
float fHostMove = 0.0f;
|
||
if (t.x > 0.0f) fHostMove = t.x;
|
||
if (t.y > 0.0f && t.y > fHostMove) fHostMove = t.y;
|
||
if (t.z > 0.0f && t.z > fHostMove) fHostMove = t.z;
|
||
|
||
if (fHostMove > 0.0f)
|
||
{
|
||
fAABBCollideDist = fRootDist - fHostMove;
|
||
}
|
||
}
|
||
|
||
return fAABBCollideDist;
|
||
}
|
||
|
||
private bool CalcCollideFreePos(A3DAABB aabbTarget, out A3DVECTOR3 vPos)
|
||
{
|
||
// ¸ù¾Ýµ±Ç° HostPlayer λÖÃÓë¸ø¶¨Ä¿±êµÄÅöײ°üΧºÐ aabbTarget£¬¼ÆËãÄ¿±ê¸½½ü(ÓëµØÐκÍ͹°ü)ÎÞ³åÍ»µÄλÖà (Find a terrain/brush free position near the target AABB)
|
||
// vPos ´æ·ÅÎÞ³åÍ»µÄλÖ㬿ÉÓÃÓÚ SetPos (vPos stores the safe position for SetPos)
|
||
bool bFound = false;
|
||
vPos = default;
|
||
|
||
A3DAABB aabbHost = m_aabb;
|
||
A3DVECTOR3 vHostHeight = new A3DVECTOR3(0.0f, aabbHost.Extents.y, 0.0f);
|
||
A3DVECTOR3 vHostRoot = aabbHost.Center - vHostHeight;
|
||
A3DVECTOR3 vTargetHeight = new A3DVECTOR3(0.0f, aabbTarget.Extents.y, 0.0f);
|
||
A3DVECTOR3 vTargetRoot = aabbTarget.Center - vTargetHeight;
|
||
|
||
A3DVECTOR3 vRootDir = vTargetRoot - vHostRoot;
|
||
float fRootDist = vRootDir.Normalize();
|
||
float fAABBCollideDist = CalcAABBOnCollidePos(aabbTarget);
|
||
|
||
if (fAABBCollideDist > 0.0f)
|
||
{
|
||
float[] fDeltaTests = { 0.001f, 0.1f };
|
||
foreach (float fDelta in fDeltaTests)
|
||
{
|
||
// ¿¼Âǵ½µØÐÎÆð·üµÈÒòËØ£¬Óëʵ¼Ê³åͻλÖÃÀ¿ªÒ»¶¨¾àÀë²¢Öð¸ö²âÊÔ£¬ÒÔÔö´ó³É¹¦·µ»Ø¼¸ÂÊ (Offset slightly to improve success probability)
|
||
if (fRootDist > fAABBCollideDist + fDelta)
|
||
{
|
||
float fTestMoveDist = fRootDist - (fAABBCollideDist + fDelta);
|
||
A3DVECTOR3 vTestPos = vHostRoot + vRootDir * fTestMoveDist;
|
||
|
||
// ¸ù¾ÝµØÐκÍ͹°ü½øÐÐÐÞÕý¡¢²¢²âÊÔ¿ÉÓÃÐÔ (Adjust with terrain/brush constraints and verify)
|
||
vTestPos.y = ClampAboveGround(vTestPos);
|
||
if (IsPosCollideFree(vTestPos))
|
||
{
|
||
// ºÍ͹°üÎÞÅöײ£¬Ö±½Ó¿ÉÓà (No brush collision, use directly)
|
||
bFound = true;
|
||
vPos = vTestPos;
|
||
}
|
||
else
|
||
{
|
||
// ºÍ͹°üÓÐÅöײ£¬³¢ÊÔÔÚÊúÖ±·½ÏòÉϲéÕÒ (If still colliding, search vertically)
|
||
if (CalcVerticalCollideFreePos(vTestPos, out A3DVECTOR3 vTestPos2))
|
||
{
|
||
bFound = true;
|
||
vPos = vTestPos2;
|
||
}
|
||
}
|
||
|
||
if (bFound)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return bFound;
|
||
}
|
||
|
||
private bool CalcBrushOnCollidePos(A3DVECTOR3 vTestPos, A3DVECTOR3 vDelta, A3DVECTOR3 vExtents,
|
||
out A3DVECTOR3 vPos, out bool bNoCollide)
|
||
{
|
||
// ·µ»Ø true£ºvPos ΪÎÞÅöײλÖã»Èô bNoCollide Ϊ true£¬Ôò vPos Ϊ vTestPos£»·ñÔòΪ¼ÆËã³öµÄλÖà (Return true with vPos pointing at a usable location; if bNoCollide==true the input position was already free)
|
||
// ·µ»Ø false£ºÒâζ×Å bStartSolid = true (Return false implies we started within solid geometry)
|
||
bool bFound = false;
|
||
|
||
A3DVECTOR3 vCenterHeight = new A3DVECTOR3(0.0f, vExtents.y, 0.0f);
|
||
env_trace_t trcInfo = new env_trace_t
|
||
{
|
||
dwCheckFlag = EC_CDR.CDR_EVN.CDR_BRUSH,
|
||
vExt = vExtents,
|
||
vStart = vTestPos + vCenterHeight,
|
||
vDelta = vDelta,
|
||
vTerStart = vTestPos + vCenterHeight,
|
||
vWatStart = vTestPos + vCenterHeight,
|
||
bWaterSolid = false
|
||
};
|
||
|
||
if (EC_CDR.CollideWithEnv(ref trcInfo))
|
||
{
|
||
bNoCollide = false;
|
||
if (!trcInfo.bStartSolid)
|
||
{
|
||
vPos = trcInfo.vStart + trcInfo.fFraction * trcInfo.vDelta - vCenterHeight;
|
||
bFound = true;
|
||
}
|
||
else
|
||
{
|
||
vPos = default;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
vPos = vTestPos;
|
||
bNoCollide = true;
|
||
bFound = true;
|
||
}
|
||
|
||
if (!bFound)
|
||
{
|
||
vPos = default;
|
||
bNoCollide = false;
|
||
}
|
||
|
||
return bFound;
|
||
}
|
||
|
||
private bool CalcVerticalCollideFreePos(A3DVECTOR3 vRefPos, out A3DVECTOR3 vPos)
|
||
{
|
||
// ¼ÆËã vRefPos ¸½½üÊúÖ±·½ÏòÉÏÓë͹°ü¼°µØÐÎÎÞ³åÍ»µÄλÖà (Search along the vertical direction around vRefPos for a collision-free spot)
|
||
// vRefPos Ϊ HostPlayer foot λÖà (vRefPos corresponds to the host's foot)
|
||
bool bFound = false;
|
||
vPos = default;
|
||
|
||
CECWorld world = CECGameRun.Instance.GetWorld();
|
||
if (world == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
while (true)
|
||
{
|
||
A3DAABB hostAabb = m_aabb;
|
||
A3DVECTOR3 vHostExts = hostAabb.Extents;
|
||
|
||
// ¹¹½¨ËõС°æ HostPlayer £¬ÏòÏÂѰÕÒ¿ÉÓÃλÖà (Use a shrinked collider to probe downward)
|
||
A3DVECTOR3 vShrinkExts = new A3DVECTOR3(vHostExts.x, vHostExts.y * 0.5f, vHostExts.z);
|
||
A3DVECTOR3 vShrinkPos = vRefPos + new A3DVECTOR3(0.0f, vHostExts.y * 0.5f, 0.0f);
|
||
|
||
A3DVECTOR3 vStartPos = vShrinkPos;
|
||
A3DVECTOR3 vVerticalDelta = new A3DVECTOR3(0.0f, -1.0f, 0.0f);
|
||
A3DVECTOR3 dummyNormal = default;
|
||
float terrainHeight = world.GetTerrainHeight(vStartPos, ref dummyNormal);
|
||
if (vStartPos.y < terrainHeight + 0.01f)
|
||
{
|
||
// ²âÊÔλÖÃÐè´¦ÓÚµØÐÎÒÔÉÏ (Candidate must be above the terrain)
|
||
break;
|
||
}
|
||
|
||
if (!CalcBrushOnCollidePos(vStartPos, vVerticalDelta, vShrinkExts, out A3DVECTOR3 vCandidate,
|
||
out bool bNoCollide))
|
||
{
|
||
// vStartPos ´¦ÓÚ͹°üÀï (Start position lies inside a brush)
|
||
break;
|
||
}
|
||
|
||
if (bNoCollide)
|
||
{
|
||
// ´Ó vStartPos µ½ vRefPos ¶¼ÎÞÅöײ£¬¿ÉÄÜÊÇ (No collision between vStartPos and vRefPos; possible cases)
|
||
// ÇéÐÎ1£ºvRefPos ´¦Í·¶¥ÓÐ͹°üµ¼ÖÂÅöײ£¨´Ó¶øµ÷Óô˺¯ÊýÐÞÕý£© (Case1: upper brush causes the issue)
|
||
// ÇéÐÎ2£º»òÕß vRefPos ±¾Éí¼´ÎÞÅöײ£¬Îóµ÷Óô˺¯Êý×öÖØ¸´¼ÆËã (Case2: vRefPos was already free)
|
||
// ÇéÐÎ3£º»òÆäËüδ¿¼ÂÇÇé¿ö (Case3: other edge cases)
|
||
// ³¢ÊԴӵײ¿Ïò vRefPos ʹÓÃÔʼ´óС HostPlayer ²éÕÒÎÞÅöײλÖã¬ÒÔ´¦ÀíÉÏÊöÇéÐÎ1 (Try again with the original extents to address case1)
|
||
vStartPos += vVerticalDelta;
|
||
vVerticalDelta = vRefPos - vStartPos;
|
||
if (!CalcBrushOnCollidePos(vStartPos, vVerticalDelta, vHostExts, out vCandidate, out bNoCollide))
|
||
{
|
||
break;
|
||
}
|
||
|
||
if (bNoCollide)
|
||
{
|
||
vCandidate = vRefPos;
|
||
}
|
||
// else vCandidate ÊÇËõС°æ HostPlayer ÎÞÅöײµÄλÖã¬Ò²ÊÇʵ¼Ê´óС HostPlayer ÐÞÕý vRefPos µÄλÖà (Otherwise the computed candidate already fixes the offset)
|
||
}
|
||
|
||
vCandidate.y = ClampAboveGround(vCandidate);
|
||
if (!IsPosCollideFree(vCandidate))
|
||
{
|
||
// λÖóåÍ» (Still interpenetrating)
|
||
break;
|
||
}
|
||
|
||
vPos = vCandidate;
|
||
bFound = true;
|
||
break;
|
||
}
|
||
|
||
return bFound;
|
||
}
|
||
|
||
private float ClampAboveGround(A3DVECTOR3 vPos)
|
||
{
|
||
// ½« vPos ÏÞÖÆµ½µØÃæÒÔÉÏ (Clamp vPos above the ground)
|
||
// ·ÉÐÐ״̬»òË®µ×ʱ£¬ÏÞÖÆÈËÎïÔÚË®ÃæÒÔÉÏ£¬ÀëË®Ãæ/µØÃæÒ»¶¨¾àÀ룻Èôµ÷Õûºó vPos ÊúÖ±ÍùÏÂÓÐ͹°ü£¬Ôò»¹»áÏÞÖÆµ½Àë͹°üÒ»¶¨¾àÀë (When flying/underwater we also keep distance from surfaces and nearby brushes)
|
||
// ×¢Ò⣺vPos ±»µ÷Õûºó£¬ÓпÉÄÜ´¦ÓÚ͹°üÖУ»Ðè¼ì²é·µ»Ø¸ß¶Èµ÷ÕûÖµ£¬ÒÔ±ÜÃâµ÷Õû¹ý´ó (Beware of large adjustments placing us back into brushes)
|
||
|
||
A3DVECTOR3 vTemp = new A3DVECTOR3(vPos);
|
||
CECWorld world = CECGameRun.Instance.GetWorld();
|
||
if (world == null)
|
||
{
|
||
return vTemp.y;
|
||
}
|
||
|
||
while (true)
|
||
{
|
||
A3DVECTOR3 dummyNormal = default;
|
||
float fTerrainHeight = world.GetTerrainHeight(vTemp, ref dummyNormal);
|
||
vTemp.y = EC_Utility.a_ClampFloor(vTemp.y, fTerrainHeight);
|
||
|
||
A3DAABB hostAabb = m_aabb;
|
||
A3DVECTOR3 vExts = hostAabb.Extents;
|
||
|
||
if (IsFlying())
|
||
{
|
||
float fAbove = m_MoveConst.fMinAirHei;
|
||
|
||
// Ïȱ£Ö¤ÀëµØÃæ/Ë®ÃæÒ»¶¨¸ß¶È (Keep some height over terrain/water)
|
||
float fWaterHeight = world.GetWaterHeight(vTemp);
|
||
float fSurface = Mathf.Max(fTerrainHeight, fWaterHeight);
|
||
vTemp.y = EC_Utility.a_ClampFloor(vTemp.y, fSurface + fAbove);
|
||
|
||
// ÔÙ³¢ÊÔÀë͹°üÒ»¶¨¸ß¶È (Then test against brushes)
|
||
if (!CalcBrushOnCollidePos(vTemp, GPDataTypeHelper.g_vAxisY * (fSurface - vTemp.y), vExts,
|
||
out A3DVECTOR3 vHitPos, out bool bNoCollide) || bNoCollide)
|
||
{
|
||
break;
|
||
}
|
||
|
||
// ÉèÖÃÀë͹°üÒ»¶¨¸ß¶È (Clamp to stay above brushes)
|
||
vTemp.y = EC_Utility.a_ClampFloor(vTemp.y, vHitPos.y + fAbove);
|
||
break;
|
||
}
|
||
|
||
float fWaterHeightLow = world.GetWaterHeight(vTemp);
|
||
if (fWaterHeightLow > fTerrainHeight && vTemp.y + vExts.y < fWaterHeightLow - m_MoveConst.fWaterSurf)
|
||
{
|
||
// Ë®µ× (Underwater)
|
||
float fAbove = m_MoveConst.fMinWaterHei;
|
||
|
||
// Ïȱ£Ö¤ÀëË®µ×Ò»¶¨¾àÀë (Keep distance from the riverbed)
|
||
vTemp.y = EC_Utility.a_ClampFloor(vTemp.y, fTerrainHeight + fAbove);
|
||
|
||
// ÔÙ³¢ÊÔÀë͹°üÒ»¶¨¸ß¶È (Then ensure clearance from brushes)
|
||
if (!CalcBrushOnCollidePos(vTemp, GPDataTypeHelper.g_vAxisY * (fTerrainHeight - vTemp.y), vExts,
|
||
out A3DVECTOR3 vHitPos, out bool bNoCollide) || bNoCollide)
|
||
{
|
||
break;
|
||
}
|
||
|
||
// ÉèÖÃÀë͹°üÒ»¶¨¸ß¶È (Clamp relative to brush hit position)
|
||
vTemp.y = EC_Utility.a_ClampFloor(vTemp.y, vHitPos.y + fAbove);
|
||
break;
|
||
}
|
||
|
||
// µØÃæÉÏ£¬ÎÞÐèÔÙ´¦Àí (Already safe on ground)
|
||
break;
|
||
}
|
||
|
||
return vTemp.y;
|
||
}
|
||
|
||
public void HandleRevive(short sReviveType, A3DVECTOR3 pos)
|
||
{
|
||
// Move to revive position and play revive animation
|
||
PlayAction((int)PLAYER_ACTION_TYPE.ACT_REVIVE);
|
||
// Clear any running dead work if exists
|
||
m_pWorkMan?.FinishRunningWork(CECHPWork.Host_work_ID.WORK_DEAD);
|
||
// Clear corpse state so player is alive again
|
||
m_dwStates &= ~(uint)PlayerNPCState.GP_STATE_CORPSE;
|
||
UnityGameSession.c2s_CmdGetAllData(true, true, false);
|
||
UnityGameSession.c2s_CmdSendEnterPKPrecinct();
|
||
UnityGameSession.RequesrQueryPlayerCash();
|
||
}
|
||
|
||
// Message MSG_HST_SELTARGET handler
|
||
void OnMsgHstSelTarget(ECMSG Msg)
|
||
{
|
||
//BMLogger.LogError("HoangDev: OnMsgHstSelTarget");
|
||
if (Convert.ToInt32(Msg.dwParam2) == CommandID.SELECT_TARGET)
|
||
{
|
||
var data = (byte[])Msg.dwParam1;
|
||
cmd_select_target pCmd = GPDataTypeHelper.FromBytes<cmd_select_target>(data);
|
||
// In duel: don't let server force selection back to duel opponent when player chose another target
|
||
if (IsInDuel() && pCmd.idTarget == m_pvp.idDuelOpp && m_idSelTarget != 0 && m_idSelTarget != m_pvp.idDuelOpp)
|
||
return;
|
||
m_idSelTarget = pCmd.idTarget;
|
||
m_idUCSelTarget = 0;
|
||
}
|
||
else if (Convert.ToInt32(Msg.dwParam2) == CommandID.UNSELECT)
|
||
{
|
||
m_idSelTarget = 0;
|
||
}
|
||
}
|
||
|
||
public override void SetUpPlayer()
|
||
{
|
||
base.SetUpPlayer();
|
||
m_iCID = (int)CECObject.Class_ID.OCID_HOSTPLAYER;
|
||
|
||
m_IncantCnt = new CECCounter();
|
||
m_IncantCnt.SetPeriod(1000);
|
||
m_IncantCnt.Reset(true);
|
||
m_bEnterGame = false;
|
||
}
|
||
|
||
public async void InitCharacter(cmd_self_info_1 role)
|
||
{
|
||
SetUpPlayer();
|
||
m_dwStates = (uint)role.state;
|
||
controller = GetComponent<CharacterController>();
|
||
if (!controller)
|
||
{
|
||
BMLogger.LogError("HostPlayer InitCharacter no CharacterController");
|
||
}
|
||
|
||
//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));
|
||
LoadResources();
|
||
await SetPlayerModel(UnityGameSession.Instance.GetRoleInfo().occupation,
|
||
UnityGameSession.Instance.GetRoleInfo().gender);
|
||
isDataAwaitToReady = true;
|
||
Vector3 pos = new Vector3(role.pos.x, role.pos.y, role.pos.z);
|
||
string roleName = Encoding.Unicode.GetString(UnityGameSession.Instance.GetRoleInfo().name.ByteArray);
|
||
SetPlayerName(roleName);
|
||
if (txtName != null) txtName.text = roleName;
|
||
EventBus.Publish(new InfoHostPlayer(roleName));
|
||
playerTransform.position = pos;
|
||
m_dwResFlags = (uint)PlayerResourcesReadyFlag.RESFG_ALL;
|
||
joystick = FindAnyObjectByType<Joystick>();
|
||
EventBus.Subscribe<JoystickRealeaseEvent>(JoystickRelease);
|
||
EventBus.Subscribe<JoystickPressEvent>(OnClickJoystick);
|
||
if (TryGetComponent<PlayerVisual>(out var visual))
|
||
{
|
||
visual.InitPlayerEventDoneHandler();
|
||
}
|
||
|
||
m_aabb.Center = GPDataTypeHelper.g_vOrigin;
|
||
m_aabb.Extents.Set(0.3f, 0.9f, 0.3f);
|
||
m_aabbServer = m_aabb;
|
||
m_MoveConst.fStepHei = 0.8f;
|
||
m_MoveConst.fMinAirHei = 1.6f;
|
||
m_MoveConst.fMinWaterHei = 0.3f;
|
||
m_MoveConst.fShoreDepth = 1.6f;
|
||
m_MoveConst.fWaterSurf = 0.6f;
|
||
CalcPlayerAABB();
|
||
SetPos(pos);
|
||
m_MoveCtrl.SetHostLastPos(EC_Utility.ToA3DVECTOR3(pos));
|
||
m_MoveCtrl.SetLastSevPos(EC_Utility.ToA3DVECTOR3(pos));
|
||
//m_CDRInfo.vTPNormal = GroundCheck(out RaycastHit hit) ? hit.normal : Vector3.zero;
|
||
m_CDRInfo.vExtent = m_aabbServer.Extents;
|
||
Vector3 pStart = pos;
|
||
pos.y += m_CDRInfo.vExtent.y;
|
||
if (Physics.RaycastNonAlloc(pos, Vector3.down, hits, m_CDRInfo.vExtent.y, 1 << 6) > 0)
|
||
{
|
||
m_CDRInfo.vTPNormal = EC_Utility.ToA3DVECTOR3(hits[0].normal);
|
||
}
|
||
else
|
||
{
|
||
m_CDRInfo.vTPNormal = g_vOrigin;
|
||
}
|
||
m_CDRInfo.fYVel = 0.0f;
|
||
m_CDRInfo.fSlopeThresh = EC_SLOPE_Y;
|
||
m_CDRInfo.fStepHeight = m_MoveConst.fStepHei;
|
||
|
||
m_AirCDRInfo.vExtent = m_aabbServer.Extents;
|
||
m_AirCDRInfo.fUnderWaterDistThresh = m_MoveConst.fWaterSurf;
|
||
|
||
// Create work manager
|
||
m_pWorkMan = new CECHPWorkMan(this);
|
||
m_pWorkMan.StartWork_p0(m_pWorkMan.CreateWork(Host_work_ID.WORK_STAND));
|
||
if (IsDead())
|
||
{
|
||
//CECHPWorkDead pWork = (CECHPWorkDead*)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_DEAD);
|
||
//pWork.SetBeDeadFlag(true);
|
||
//m_pWorkMan.StartWork_p0(pWork);
|
||
EventBus.PublishChannel(GetCharacterID(), new ClearComActFlagAllRankNodesEvent(true));
|
||
PlayAction((int)PLAYER_ACTION_TYPE.ACT_GROUNDDIE);
|
||
}
|
||
else
|
||
{
|
||
UnityGameSession.c2s_CmdGetAllData(true, true, false);
|
||
UnityGameSession.c2s_CmdSendEnterPKPrecinct();
|
||
UnityGameSession.RequesrQueryPlayerCash();
|
||
}
|
||
|
||
/*
|
||
else if (IsSitting())
|
||
{
|
||
CECHPWorkSit* pWork = (CECHPWorkSit*)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_SIT);
|
||
pWork.SetBeSittingFlag(true);
|
||
m_pWorkMan.StartWork_p1(pWork);
|
||
}*/
|
||
//m_GndInfo.bOnGround = GroundCheck(out lastGroundHit);
|
||
m_pPetCorral = new CECPetCorral();
|
||
if (m_pWorkMan == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
LoadGfx();
|
||
}
|
||
|
||
public async void LoadGfx()
|
||
{
|
||
// Load GFX
|
||
var gfxCaster = EC_Game.GetGFXCaster();
|
||
// m_pMoveTargetGFX = g_pGame.GetGFXCaster().LoadGFXEx(res_GFXFile(RES_GFX_MOVETARGET));
|
||
m_pSelectedGFX =
|
||
await gfxCaster.LoadGFXEx(EC_Resource.res_GFXFile((int)GfxResourceType.RES_GFX_CURSORHOVER));
|
||
m_pHoverGFX = await gfxCaster.LoadGFXEx(EC_Resource.res_GFXFile((int)GfxResourceType.RES_GFX_CURSORHOVER));
|
||
// m_pFloatDust = g_pGame.GetGFXCaster().LoadGFXEx(res_GFXFile(RES_GFX_FLOATING_DUST));
|
||
|
||
if (true /*CECUIConfig::Instance().GetGameUI().bEnableActionSwitch*/)
|
||
{
|
||
m_pActionSwitcher = new CECActionSwitcher(this);
|
||
}
|
||
else
|
||
m_pActionSwitcher = new CECActionSwitcherBase(this);
|
||
// TODO: Move this to right flow later , it's just for test now
|
||
}
|
||
|
||
private void OnMsgHstPushMove(JoystickPressEvent joystickPressEvent)
|
||
{
|
||
//_playerStateMachine.ChangeState(_moveState);
|
||
/* if (m_pWorkMan.IsSitting())
|
||
{
|
||
g_pGame.GetGameSession().c2s_CmdStandUp();
|
||
return;
|
||
}*/
|
||
m_dwMoveRelDir = 0;
|
||
if (!CanDo(ActionCanDo.CANDO_MOVETO)) return;
|
||
if (joystick.Vertical > 0)
|
||
{
|
||
if (joystick.Horizontal > 0)
|
||
{
|
||
m_dwMoveRelDir |= (uint)(MOVE_DIR.MD_FORWARD | MOVE_DIR.MD_RIGHT);
|
||
}
|
||
else if (joystick.Horizontal < 0)
|
||
{
|
||
m_dwMoveRelDir |= (uint)(MOVE_DIR.MD_FORWARD | MOVE_DIR.MD_LEFT);
|
||
}
|
||
else
|
||
{
|
||
m_dwMoveRelDir |= (uint)MOVE_DIR.MD_FORWARD;
|
||
}
|
||
}
|
||
else if (joystick.Vertical < 0)
|
||
{
|
||
if (joystick.Horizontal > 0)
|
||
{
|
||
m_dwMoveRelDir |= (uint)(MOVE_DIR.MD_BACK | MOVE_DIR.MD_RIGHT);
|
||
}
|
||
else if (joystick.Horizontal < 0)
|
||
{
|
||
m_dwMoveRelDir |= (uint)(MOVE_DIR.MD_BACK | MOVE_DIR.MD_LEFT);
|
||
}
|
||
else
|
||
{
|
||
m_dwMoveRelDir |= (uint)MOVE_DIR.MD_BACK;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (joystick.Horizontal > 0)
|
||
{
|
||
m_dwMoveRelDir |= (uint)(MOVE_DIR.MD_RIGHT);
|
||
}
|
||
else if (joystick.Horizontal < 0)
|
||
{
|
||
m_dwMoveRelDir |= (uint)(MOVE_DIR.MD_LEFT);
|
||
}
|
||
}
|
||
bool bPushMove = true;
|
||
/* if (Msg.dwParam1 == 8 || Msg.dwParam1 == 9)
|
||
{
|
||
if (m_iMoveEnv != (int)MoveEnvironment.MOVEENV_AIR && m_iMoveEnv != (int)MoveEnvironment.MOVEENV_WATER)
|
||
bPushMove = false;
|
||
}*/
|
||
|
||
if (bPushMove /*&& !IsAboutToDie()*/ && CanDo(ActionCanDo.CANDO_MOVETO))
|
||
{
|
||
if (m_pWorkMan.CanStartWork(Host_work_ID.WORK_MOVETOPOS))
|
||
{
|
||
CECHPWorkMove pNewWork = (CECHPWorkMove)m_pWorkMan.CreateWork(Host_work_ID.WORK_MOVETOPOS);
|
||
pNewWork.SetDestination(CECHPWorkMove.DestTypes.DEST_PUSH, g_vOrigin);
|
||
m_pWorkMan.StartWork_p1(pNewWork);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
EventBus.Unsubscribe<JoystickRealeaseEvent>(JoystickRelease);
|
||
EventBus.Unsubscribe<JoystickPressEvent>(OnClickJoystick);
|
||
}
|
||
|
||
//TODO: Remove this function. Since it has been deprecated.
|
||
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;
|
||
playerTransform.position = pos;
|
||
// SetPlayerModel();
|
||
//Debug.LogError("Pos Character = " + pos);
|
||
}
|
||
|
||
/// <summary>Use host's m_pvp (we update it from S2C duel packets). Base IsInDuel() reads CECPlayer.m_pvp which is never set.</summary>
|
||
public new bool IsInDuel() { return m_pvp.iDuelState == Duel_state.DUEL_ST_INDUEL; }
|
||
|
||
/// <summary>Duel opponent character id when in duel; 0 otherwise. Used by HPWork so trace/melee send correct PVP mask.</summary>
|
||
public int GetDuelOpponentId() { return m_pvp.idDuelOpp; }
|
||
|
||
public void ClearAnimation()
|
||
{
|
||
EventBus.PublishChannel(GetCharacterID(), new ClearComActFlagAllRankNodesEvent(true));
|
||
}
|
||
|
||
public CECActionSwitcherBase GetActionSwitcher()
|
||
{
|
||
return m_pActionSwitcher;
|
||
}
|
||
|
||
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 CanTouchTarget(A3DVECTOR3 vHostPos, A3DVECTOR3 vTargetPos, float fTargetRad, int iReason,
|
||
float fMaxCut = 1.0f)
|
||
{
|
||
float fDist = A3d_Magnitude(vTargetPos - vHostPos);
|
||
//bool bResult = false;
|
||
float fRange = 0.0f;
|
||
//string reasonStr = iReason == 1 ? "melee" : (iReason == 2 ? "cast magic" : (iReason == 3 ? "talk" : $"unknown({iReason})"));
|
||
|
||
switch (iReason)
|
||
{
|
||
case 1: // melee
|
||
{
|
||
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)
|
||
{
|
||
//bResult = true;
|
||
/* BMLogger.Log($"[DISTANCE_DEBUG] CanTouchTarget: reason={reasonStr}, " +
|
||
$"hostPos=({vHostPos.x:F2}, {vHostPos.y:F2}, {vHostPos.z:F2}), " +
|
||
$"targetPos=({vTargetPos.x:F2}, {vTargetPos.y:F2}, {vTargetPos.z:F2}), " +
|
||
$"distance={fDist:F2}, targetRadius={fTargetRad:F2}, " +
|
||
$"requiredRange={fRange:F2}, effectiveDistance={fDist - fTargetRad:F2}, " +
|
||
$"result={bResult}, attackRange={m_ExtProps.ak.AttackRange:F2}");*/
|
||
return true;
|
||
}
|
||
|
||
break;
|
||
}
|
||
case 2: // cast magic
|
||
{
|
||
if (m_pPrepSkill != null)
|
||
{
|
||
|
||
//TODO : Check this function GetCastRange
|
||
fRange = m_pPrepSkill.GetCastRange(m_ExtProps.ak.AttackRange, GetPrayDistancePlus());
|
||
|
||
if (fRange > 0.0f)
|
||
{
|
||
if (fDist - fTargetRad <= fRange)
|
||
{
|
||
//bResult = true;
|
||
/* BMLogger.Log($"[DISTANCE_DEBUG] CanTouchTarget: reason={reasonStr}, " +
|
||
$"hostPos=({vHostPos.x:F2}, {vHostPos.y:F2}, {vHostPos.z:F2}), " +
|
||
$"targetPos=({vTargetPos.x:F2}, {vTargetPos.y:F2}, {vTargetPos.z:F2}), " +
|
||
$"distance={fDist:F2}, targetRadius={fTargetRad:F2}, " +
|
||
$"requiredRange={fRange:F2}, effectiveDistance={fDist - fTargetRad:F2}, " +
|
||
$"result={bResult}, skillID={m_pPrepSkill.GetSkillID()}, attackRange={m_ExtProps.ak.AttackRange:F2}");*/
|
||
return true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//bResult = true;
|
||
/*BMLogger.Log($"[DISTANCE_DEBUG] CanTouchTarget: reason={reasonStr}, " +
|
||
$"hostPos=({vHostPos.x:F2}, {vHostPos.y:F2}, {vHostPos.z:F2}), " +
|
||
$"targetPos=({vTargetPos.x:F2}, {vTargetPos.y:F2}, {vTargetPos.z:F2}), " +
|
||
$"distance={fDist:F2}, targetRadius={fTargetRad:F2}, " +
|
||
$"requiredRange=unlimited, result={bResult}, skillID={m_pPrepSkill.GetSkillID()}");*/
|
||
return true;
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
case 3: // talk
|
||
{
|
||
fRange = 5.0f;
|
||
if (fDist - fTargetRad <= fRange)
|
||
{
|
||
//bResult = true;
|
||
return true;
|
||
}
|
||
|
||
break;
|
||
}
|
||
default: // no special reason
|
||
{
|
||
fRange = (fTargetRad + m_fTouchRad) * 3.0f;
|
||
if (fDist < fRange)
|
||
{
|
||
//bResult = true;
|
||
return true;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Log distance check result for debugging
|
||
// 记录距离检查结果用于调试
|
||
/* BMLogger.Log($"[DISTANCE_DEBUG] CanTouchTarget: reason={reasonStr}, " +
|
||
$"hostPos=({vHostPos.x:F2}, {vHostPos.y:F2}, {vHostPos.z:F2}), " +
|
||
$"targetPos=({vTargetPos.x:F2}, {vTargetPos.y:F2}, {vTargetPos.z:F2}), " +
|
||
$"distance={fDist:F2}, targetRadius={fTargetRad:F2}, " +
|
||
$"requiredRange={fRange:F2}, effectiveDistance={fDist - fTargetRad:F2}, " +
|
||
$"result={bResult}, attackRange={m_ExtProps.ak.AttackRange:F2}");*/
|
||
|
||
return false;
|
||
}
|
||
public void RemoveObjectFromTabSels(CECObject pObject)
|
||
{
|
||
for (int i = 0; i < m_aTabSels.Count; i++)
|
||
{
|
||
if (m_aTabSels[i] == pObject)
|
||
{
|
||
m_aTabSels.RemoveAt(i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool CanTouchTarget(A3DVECTOR3 vTargetPos, float fTargetRad, int iReason, float fMaxCut = 1.0f)
|
||
{
|
||
// Use server-tracked position instead of visual position for distance checks
|
||
// This ensures distance checks use the correct position immediately after flashmove
|
||
// 使用服务器跟踪的位置而不是视觉位置进行距离检查
|
||
// 这确保在闪移后立即使用正确的位置进行距离检查
|
||
A3DVECTOR3 vector = EC_Utility.ToA3DVECTOR3(m_MoveCtrl.GetLastSevPos());
|
||
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;
|
||
}
|
||
|
||
public bool IsInFortress()
|
||
{
|
||
return m_fortressEnter.role_in_war != 0;
|
||
}
|
||
|
||
bool IsPVPOpen()
|
||
{
|
||
return m_pvp.bEnable;
|
||
}
|
||
|
||
public float GetPrayDistancePlus()
|
||
{
|
||
return m_fPrayDistancePlus;
|
||
}
|
||
|
||
// Get faction ID
|
||
//public int GetFactionID()
|
||
//{
|
||
// return m_idFaction;
|
||
//}
|
||
public void SetPrayDistancePlus(float prayDistancePlus)
|
||
{
|
||
m_fPrayDistancePlus = prayDistancePlus;
|
||
}
|
||
public bool IsJumping()
|
||
{
|
||
return m_iJumpCount > 0;
|
||
}
|
||
public bool IsFalling()
|
||
{
|
||
return m_iMoveEnv == (int)MoveEnvironment.MOVEENV_GROUND
|
||
&& m_GndInfo.bOnGround == false;
|
||
}
|
||
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();
|
||
LayerMask layerGround = 1 << 6;
|
||
LayerMask layerWater = 1 << 8;
|
||
while (true)
|
||
{
|
||
if (!IsUnderWater()) break;
|
||
CECWorld pWorld = EC_Game.GetGameRun().GetWorld();
|
||
if (pWorld == null) break;
|
||
|
||
A3DVECTOR3 vPos = GetPos();
|
||
Vector3 startPoint = EC_Utility.ToVector3(vPos);
|
||
Vector3 dir = EC_Utility.ToVector3(vPos) + Vector3.down;
|
||
float fTerrainHeight = 0f;
|
||
if (Physics.RaycastNonAlloc(startPoint, dir, hits, layerGround) > 0)
|
||
{
|
||
fTerrainHeight = Vector3.Distance(hits[0].point, startPoint);
|
||
}
|
||
|
||
float fWaterHeight = 0f;
|
||
if (Physics.RaycastNonAlloc(startPoint, dir, hits, layerWater) > 0)
|
||
{
|
||
fWaterHeight = Vector3.Distance(hits[0].point, startPoint);
|
||
}
|
||
|
||
if (fWaterHeight <= fTerrainHeight) break;
|
||
float fBorderLine = fWaterHeight - 2.0f;
|
||
if (vPos.y <= fBorderLine) break;
|
||
|
||
fSpeedSev = Math.Min(m_ExtProps.mv.run_speed, fSpeedSev);
|
||
break;
|
||
}
|
||
|
||
return fSpeedSev;
|
||
}
|
||
|
||
|
||
public void PrepareNPCService(int idSev)
|
||
{
|
||
if (!GPDataTypeHelper.ISNPCID(m_idSevNPC))
|
||
{
|
||
return;
|
||
}
|
||
|
||
DATA_TYPE DataType = new DATA_TYPE();
|
||
object pBuf = ElementDataManProvider.GetElementDataMan()
|
||
.get_data_ptr((uint)idSev, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
|
||
|
||
switch (DataType)
|
||
{
|
||
case DATA_TYPE.DT_NPC_TALK_SERVICE:
|
||
|
||
break;
|
||
|
||
case DATA_TYPE.DT_NPC_SELL_SERVICE:
|
||
case DATA_TYPE.DT_NPC_BUY_SERVICE:
|
||
{
|
||
//// Get NPC's tex rate
|
||
//float fScale = 1.0f;
|
||
//CECNPC pNPC = EC_ManMessageMono.Instance._CECNPCMan.GetNPC(m_idSevNPC);
|
||
//if (pNPC && pNPC.IsServerNPC())
|
||
//{
|
||
// CECNPCServer pServer = (CECNPCServer)pNPC;
|
||
// fScale = (1.0f + pServer.GetTaxRate()) * pServer.GetPriceScale();
|
||
//}
|
||
|
||
//// Fill NPC package
|
||
//NPC_SELL_SERVICE pData = (NPC_SELL_SERVICE)pBuf;
|
||
|
||
//int[] id_goods = new int[InventoryConst.IVTRSIZE_NPCPACK];
|
||
//for (int j = 0; j < InventoryConst.NUM_NPCIVTR; j++)
|
||
//{
|
||
// for (int i = 0; i < InventoryConst.IVTRSIZE_NPCPACK; ++i)
|
||
// id_goods[i] = (int)pData.pages[j].goods[i].id;
|
||
// FillNPCPack(j, pData.pages[j].page_title, id_goods, fScale, false);
|
||
//}
|
||
|
||
//// Clear packs
|
||
//m_pBuyPack.RemoveAllItems();
|
||
//m_pSellPack.RemoveAllItems();
|
||
break;
|
||
}
|
||
case DATA_TYPE.DT_NPC_SKILL_SERVICE:
|
||
case DATA_TYPE.DT_NPC_PETLEARNSKILL_SERVICE:
|
||
{
|
||
//CECNPC pNPC = EC_ManMessageMono.Instance._CECNPCMan.GetNPC(m_idSevNPC);
|
||
//if (!pNPC || !pNPC.IsServerNPC())
|
||
//{
|
||
// return;
|
||
//}
|
||
|
||
//if (DataType == DATA_TYPE.DT_NPC_SKILL_SERVICE)
|
||
// ((CECNPCServer)pNPC).BuildSkillList(idSev);
|
||
//else
|
||
// ((CECNPCServer)pNPC).BuildPetSkillList();
|
||
|
||
break;
|
||
}
|
||
case DATA_TYPE.DT_NPC_REPAIR_SERVICE:
|
||
case DATA_TYPE.DT_NPC_INSTALL_SERVICE:
|
||
case DATA_TYPE.DT_NPC_UNINSTALL_SERVICE:
|
||
case DATA_TYPE.DT_NPC_TASK_IN_SERVICE:
|
||
case DATA_TYPE.DT_NPC_TASK_OUT_SERVICE:
|
||
case DATA_TYPE.DT_NPC_TASK_MATTER_SERVICE:
|
||
case DATA_TYPE.DT_NPC_HEAL_SERVICE:
|
||
case DATA_TYPE.DT_NPC_TRANSMIT_SERVICE:
|
||
case DATA_TYPE.DT_NPC_TRANSPORT_SERVICE:
|
||
case DATA_TYPE.DT_NPC_PROXY_SERVICE:
|
||
case DATA_TYPE.DT_NPC_STORAGE_SERVICE:
|
||
case DATA_TYPE.DT_NPC_DECOMPOSE_SERVICE:
|
||
case DATA_TYPE.DT_NPC_PETNAME_SERVICE:
|
||
case DATA_TYPE.DT_NPC_PETFORGETSKILL_SERVICE:
|
||
|
||
break;
|
||
|
||
case DATA_TYPE.DT_NPC_MAKE_SERVICE:
|
||
{
|
||
//// Fill NPC package
|
||
//NPC_MAKE_SERVICE pData = (NPC_MAKE_SERVICE)pBuf;
|
||
|
||
//for (int j = 0; j < NUM_NPCIVTR; j++)
|
||
// FillNPCPack(j, pData.pages[j].page_title, pData.pages[j].id_goods, 1.0f, true);
|
||
|
||
//// Clear deal pack
|
||
//m_pDealPack.RemoveAllItems();
|
||
break;
|
||
}
|
||
|
||
case DATA_TYPE.DT_NPC_RANDPROP_SERVICE:
|
||
{
|
||
//NPC_RANDPROP_SERVICE* pData = (NPC_RANDPROP_SERVICE*)pBuf;
|
||
//elementdataman* pDataMan = g_pGame.GetElementDataMan();
|
||
|
||
//// Fill equip data into NPC pack
|
||
//ASSERT(sizeof(pData.pages) / sizeof(pData.pages[0]) == NUM_NPCIVTR );
|
||
//for (int j = 0; j < NUM_NPCIVTR; j++)
|
||
//{
|
||
// unsigned int id_recipe = pData.pages[j].id_recipe;
|
||
// DATA_TYPE dt = DT_INVALID;
|
||
// RANDPROP_ESSENCE* pRecipe = (RANDPROP_ESSENCE*)pDataMan.get_data_ptr(id_recipe, ID_SPACE_RECIPE, dt);
|
||
|
||
// if (pRecipe && dt == DT_RANDPROP_ESSENCE)
|
||
// {
|
||
// FillNPCPack(j, pData.pages[j].page_title, (int*)pRecipe.equip_id, 1.0f, false);
|
||
// }
|
||
// else
|
||
// {
|
||
// // skip the invalid recipe id
|
||
// GetNPCSevPack(j).RemoveAllItems();
|
||
// GetNPCSevPack(j).SetName(_AL(""));
|
||
// }
|
||
//}
|
||
|
||
//// Clear deal pack
|
||
//m_pDealPack.RemoveAllItems();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void LogInventoryPacket(string tag, byte[] buffer, int hostId)
|
||
{
|
||
if (buffer == null)
|
||
return;
|
||
|
||
int index = 0;
|
||
if (buffer.Length < 6)
|
||
{
|
||
//LogInventoryRaw(tag, buffer);
|
||
return;
|
||
}
|
||
|
||
byte byPackage = buffer[index++];
|
||
byte ivtrSize = buffer[index++];
|
||
uint contentLength = BitConverter.ToUInt32(buffer, index);
|
||
index += 4;
|
||
|
||
int remaining = buffer.Length - index;
|
||
int contentBytes = remaining;
|
||
if (contentLength < (uint)remaining)
|
||
{
|
||
contentBytes = (int)contentLength;
|
||
}
|
||
|
||
if (contentBytes > 0)
|
||
{
|
||
byte[] content = new byte[contentBytes];
|
||
Buffer.BlockCopy(buffer, index, content, 0, contentBytes);
|
||
}
|
||
|
||
int trailing = buffer.Length - (index + contentBytes);
|
||
if (trailing > 0)
|
||
{
|
||
byte[] tail = new byte[trailing];
|
||
Buffer.BlockCopy(buffer, index + contentBytes, tail, 0, trailing);
|
||
}
|
||
}
|
||
public bool NaturallyStopMoving()
|
||
{
|
||
// if (!m_MoveCtrl.IsStop())
|
||
if (!IsPlayerMoving())
|
||
return true; // Host has been stopped
|
||
|
||
if (m_iMoveMode == (int)MoveMode.MOVE_FREEFALL || InSlidingState() || IsJumping())
|
||
return false; // Host couldn't stop naturally
|
||
|
||
if (!m_pWorkMan.IsStanding())
|
||
{
|
||
m_pWorkMan.FinishAllWork(true);
|
||
}
|
||
|
||
m_MoveCtrl.SendStopMoveCmd();
|
||
|
||
return true;
|
||
}
|
||
public A3DVECTOR3 GetDir()
|
||
{
|
||
// Return forward direction from transform
|
||
return EC_Utility.ToA3DVECTOR3(transform.forward);
|
||
}
|
||
|
||
public A3DVECTOR3 A3d_RotatePosAroundY(A3DVECTOR3 vDir, float fAngle)
|
||
{
|
||
float cos = Mathf.Cos(fAngle);
|
||
float sin = Mathf.Sin(fAngle);
|
||
|
||
A3DVECTOR3 result = new A3DVECTOR3();
|
||
result.x = vDir.x * cos + vDir.z * sin;
|
||
result.y = vDir.y;
|
||
result.z = -vDir.x * sin + vDir.z * cos;
|
||
|
||
return result;
|
||
}
|
||
|
||
bool IsTooNear(A3DVECTOR3 vTarget, ref float fNearDist)
|
||
{
|
||
// ¸ù¾Ý¿Õ¼äÇé¿ö£¬¼ÆËãÏà½ü 3D ¿Õ¼äÉÏÓ¦±£³ÖµÄ½ÏС¾àÀ룬±ÜÃâÒÆ¶¯ºÜ½üÉõÖÁÖØºÏµÄÇé¿ö
|
||
// ·µ»Ø true ±íÃ÷µ±Ç°¾àÀëСÓÚ¼ÆËã³öµÄ×îС¾àÀë
|
||
|
||
A3DVECTOR3 vPos = GetPos();
|
||
A3DVECTOR3 vMoveDir = vTarget - vPos;
|
||
float fDist = vMoveDir.Magnitude();
|
||
|
||
float fTestDist = (0.0f);
|
||
const float fTestDistH = 0.1f;
|
||
const float fMoveDistH = 0.01f; // ±£Ö¤ fTestDistH > fMoveDistH£¬Ê¹¼ì²â¸üÓÐЧ
|
||
|
||
float fDeltaXZ = vMoveDir.MagnitudeH();
|
||
if (fDeltaXZ > 0.001f && (MathF.Abs(vMoveDir.y) / fDeltaXZ) <= 50)
|
||
{
|
||
// tangent Öµ²»Ì«´ó
|
||
|
||
// ÒÔ fTestDistH Ϊˮƽ¾àÀëÒªÇ󣬼ÆËã¿Õ¼äÉÏÓ¦±£³ÖµÄ¾àÀë
|
||
fTestDist = fTestDistH * fDist / fDeltaXZ;
|
||
|
||
// ÒÔ fMoveDistH Ϊˮƽ¾àÀëÒªÇ󣬼ÆËã¿Õ¼äÉÏÓ¦ÒÆ½üµÄ¾àÀë
|
||
fNearDist = fMoveDistH * fDist / fDeltaXZ;
|
||
}
|
||
else
|
||
{
|
||
// tangent ÖµºÜ´ó£¬»ò vPos Óë vTarget ÖØºÏµÄÇé¿ö
|
||
fTestDist = 0.5f;
|
||
fNearDist = fDist > 0.01f ? 0.01f : 0.0f;
|
||
}
|
||
|
||
return (fDist <= fTestDist);
|
||
}
|
||
|
||
public bool UpdateEquipSkins()
|
||
{
|
||
int[] aNewEquips = new int[InventoryConst.IVTRSIZE_EQUIPPACK];
|
||
EC_IvtrItem pItem = null;
|
||
for (int i = 0; i < InventoryConst.IVTRSIZE_EQUIPPACK; i++)
|
||
{
|
||
// Use host player's equipment inventory (per-instance CECInventory)
|
||
var host = CECGameRun.Instance?.GetHostPlayer();
|
||
var equipInv = host?.IvtrEquipPack;
|
||
pItem = equipInv?.GetItem(i, false);
|
||
if (pItem != null)
|
||
aNewEquips[i] = pItem.m_tid;
|
||
}
|
||
ShowEquipments(aNewEquips, true, true);
|
||
|
||
return true;
|
||
}
|
||
|
||
public byte glb_BuildPVPMask(bool bForceAttack)
|
||
{
|
||
byte byMask = 0;
|
||
if (bForceAttack)
|
||
byMask |= (byte)PVPMask.GP_PVPMASK_FORCE;
|
||
else
|
||
{
|
||
CECConfigs pConfigs = EC_Game.GetConfigs();
|
||
|
||
if (pConfigs.GetGameSettings().bAtk_Player)
|
||
{
|
||
byMask |= (byte)PVPMask.GP_PVPMASK_FORCE;
|
||
|
||
if (pConfigs.GetGameSettings().bAtk_NoMafia)
|
||
byMask |= (byte)PVPMask.GP_PVPMASK_NOMAFIA;
|
||
|
||
if (pConfigs.GetGameSettings().bAtk_NoWhite)
|
||
byMask |= (byte)PVPMask.GP_PVPMASK_NOWHITE;
|
||
|
||
if (pConfigs.GetGameSettings().bAtk_NoAlliance)
|
||
byMask |= (byte)PVPMask.GP_PVPMASK_NOALLIANCE;
|
||
|
||
if (pConfigs.GetGameSettings().bAtk_NoForce)
|
||
byMask |= (byte)PVPMask.GP_PVPMASK_NOFORCE;
|
||
}
|
||
}
|
||
|
||
return byMask;
|
||
}
|
||
|
||
public bool SelectTarget(int idTarget)
|
||
{
|
||
bool bRet = false;
|
||
bool canDo = CanDo(ActionCanDo.CANDO_CHANGESELECT);
|
||
bool canselect = CanSelectTarget(idTarget);
|
||
if (canDo && canselect)
|
||
{
|
||
bRet = true;
|
||
if (idTarget == 0)
|
||
{
|
||
//BMLogger.LogError("HoangDev: HostPlayer Unsetlect npc");
|
||
UnityGameSession.c2s_CmdUnselect();
|
||
m_idSelTarget = 0;
|
||
m_idUCSelTarget = 0;
|
||
}
|
||
else
|
||
{
|
||
//BMLogger.LogError("HoangDev: HostPlayer setlect npc");
|
||
UnityGameSession.c2s_CmdSelectTarget(idTarget);
|
||
m_idSelTarget = idTarget;
|
||
m_idUCSelTarget = idTarget;
|
||
}
|
||
}
|
||
|
||
return bRet;
|
||
}
|
||
|
||
|
||
public int GetMaxLevelSofar() { return Math.Max(m_ReincarnationTome.max_level, m_BasicProps.iLevel); }
|
||
|
||
public bool CanUseProjectile(CECIvtrArrow pArrow)
|
||
{
|
||
if (pArrow == null)
|
||
return false;
|
||
|
||
CECIvtrWeapon pWeapon = (CECIvtrWeapon)m_pEquipPack.GetItem((int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WEAPON);
|
||
if (pWeapon == null)
|
||
return false;
|
||
|
||
IVTR_ESSENCE_WEAPON we = pWeapon.GetEssence();
|
||
if (we.weapon_type != (int)WeaponType.WEAPONTYPE_RANGE)
|
||
return false;
|
||
|
||
IVTR_ESSENCE_ARROW ae = pArrow.GetEssence();
|
||
if (we.require_projectile != (int)pArrow.GetDBSubType().id ||
|
||
we.weapon_level < ae.iWeaponReqLow || we.weapon_level > ae.iWeaponReqHigh)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
bool CanSelectTarget(int idTarget)
|
||
{
|
||
if (idTarget == 0 || idTarget == this.GetCharacterID())
|
||
{
|
||
// 0 means unselect
|
||
return true;
|
||
}
|
||
|
||
// Duel: always allow selecting the duel opponent so we can attack/cast (distance checked when trace runs)
|
||
if (IsInDuel() && idTarget == m_pvp.idDuelOpp)
|
||
return true;
|
||
|
||
CECObject pTarget = null;
|
||
if (GPDataTypeHelper.ISPLAYERID(idTarget))
|
||
{
|
||
EC_ElsePlayer pElsePlayer =
|
||
(EC_ManMessageMono.Instance.GetECManPlayer.GetPlayer(idTarget)) as EC_ElsePlayer;
|
||
if (pElsePlayer != null)
|
||
{
|
||
if (CanSafelySelect(pElsePlayer))
|
||
{
|
||
pTarget = pElsePlayer;
|
||
}
|
||
}
|
||
}
|
||
else if (GPDataTypeHelper.ISNPCID(idTarget))
|
||
{
|
||
CECNPC pNPC = EC_ManMessageMono.Instance.CECNPCMan.GetNPC(idTarget);
|
||
if (pNPC != null)
|
||
{
|
||
if (CanSafelySelect(pNPC) && !pNPC.IsDead())
|
||
{
|
||
pTarget = pNPC;
|
||
}
|
||
}
|
||
}
|
||
|
||
return pTarget ? pTarget.IsSelectable() : false;
|
||
}
|
||
float SafelySelectDistance()
|
||
{
|
||
// ·þÎñÆ÷¶Ô SelectTarget ÓжîÍâ¾àÀëÏÞÖÆ£¬Èýά¾àÀë 150.0¡¢Ë®Æ½¾àÀë 125.0 ÒÔÉϵ쬶¼»áÎÞ·¨Ñ¡ÖÐ
|
||
// »ùÓÚÒÔÉÏÔÒò£¬¿Í»§¶ËÑ¡Ôñ¶ÔÏó¡¢»òÕß¶ÔÒѾѡÔñµÄ¶ÔÏ󣬶¼È·±£ÆäÔÚ´ËÏÞÖÆ·¶Î§ÄÚ£¬¼´Ñ¡ÔñʱʹÓýÏС¾àÀë¼ì²â
|
||
return 100.0f;
|
||
}
|
||
|
||
public bool CanSafelySelectWith(float fDistanceToHostPlayer)
|
||
{
|
||
return fDistanceToHostPlayer <= SafelySelectDistance();
|
||
}
|
||
|
||
bool CanSafelySelect(EC_ElsePlayer pElsePlayer)
|
||
{
|
||
// IsSkeletonReady() Ϊ true ʱ, GetDistToHost() ²ÅΪÓÐЧÊý¾Ý
|
||
// !IsSkeletonReady() ʱ£¬Ò²ÔÊÐíʹÓã¬Ä¿µÄÊDZÜÃâδ¿¼Âǵ½µÄÒâÍâÇé¿ö
|
||
// ÏÂͬ
|
||
return pElsePlayer && ( /*!IsSkeletonReady() || */CanSafelySelectWith(pElsePlayer.GetDistToHost()));
|
||
}
|
||
|
||
bool CanSafelySelect(CECNPC pNPC)
|
||
{
|
||
return pNPC && ( /*!IsSkeletonReady() ||*/ CanSafelySelectWith(pNPC.GetDistToHost()));
|
||
}
|
||
|
||
// Check whether host can do a behavior / 检查宿主是否可以执行某个行为
|
||
public bool CanDo(int iThing)
|
||
{
|
||
bool bRet = true;
|
||
|
||
switch (iThing)
|
||
{
|
||
case ActionCanDo.CANDO_SITDOWN:
|
||
if (IsDead() || IsAboutToDie() || IsJumping() || IsTrading() || IsUsingTrashBox() ||
|
||
IsRooting() || IsReviving() || IsTalkingWithNPC() || IsChangingFace() ||
|
||
!m_GndInfo.bOnGround || GetBoothState() != 0 || m_iBuddyId != 0 || IsOperatingPet() != 0 || IsRebuildingPet() ||
|
||
IsUsingItem() || IsRidingOnPet() || GetShapeType() == (int)PlayerModelType.PLAYERMODEL_DUMMYTYPE2 || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_MOVETO:
|
||
if (IsDead() || IsSitting() || IsTrading() || IsUsingTrashBox() || IsRooting() ||
|
||
IsReviving() || IsTalkingWithNPC() || IsChangingFace() || IsUsingItem() ||
|
||
GetBoothState() != 0 || m_bHangerOn || IsOperatingPet() != 0 || IsRebuildingPet() || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_MELEE:
|
||
if (IsDead() || IsSitting() || m_idSelTarget == 0 || m_idSelTarget == m_PlayerInfo.cid ||
|
||
IsJumping() || GPDataTypeHelper.ISMATTERID(m_idSelTarget) || IsTrading() || IsReviving() ||
|
||
IsUsingTrashBox() || IsTalkingWithNPC() || IsChangingFace() || CannotAttack() ||
|
||
GetBoothState() != 0 || m_iBuddyId != 0 || IsRidingOnPet() || IsOperatingPet() != 0 || IsRebuildingPet() ||
|
||
IsUsingItem() || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_ASSISTSEL:
|
||
if (IsDead() || !GPDataTypeHelper.ISPLAYERID(m_idSelTarget) || m_idSelTarget == m_PlayerInfo.cid ||
|
||
m_pTeam == null || m_pTeam.GetMemberByID(m_idSelTarget) == null || m_iBuddyId != 0 || IsPassiveMove() ||
|
||
m_playerLimits[(int)PLAYER_LIMIT.PLAYER_LIMIT_NOCHANGESELECT])
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_FLY:
|
||
if (IsDead() || IsRooting() || IsSitting() || IsTrading() || IsReviving() ||
|
||
IsUsingTrashBox() || IsTalkingWithNPC() || IsChangingFace() || GetBoothState() != 0 ||
|
||
IsFlashMoving() || (m_pWorkMan != null && m_pWorkMan.HasWorkRunningOnPriority(CECHPWorkMan.Work_priority.PRIORITY_2)) ||
|
||
m_bHangerOn || IsOperatingPet() != 0 || IsRebuildingPet() ||
|
||
IsUsingItem() || IsRidingOnPet() || GetShapeType() == (int)PlayerModelType.PLAYERMODEL_DUMMYTYPE2 || IsPassiveMove() ||
|
||
m_playerLimits[(int)PLAYER_LIMIT.PLAYER_LIMIT_NOFLY] /*|| m_BattleInfo.IsChariotWar()*/)
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_PICKUP:
|
||
case ActionCanDo.CANDO_GATHER:
|
||
if (IsDead() || IsAboutToDie() || IsSitting() || IsTrading() || IsUsingTrashBox() ||
|
||
IsReviving() || IsTalkingWithNPC() || IsChangingFace() || GetBoothState() != 0 ||
|
||
GetBuddyState() == 1 || IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_TRADE:
|
||
if (IsDead() || IsAboutToDie() || IsSitting() || IsJumping() || IsMeleeing() ||
|
||
IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() || IsChangingFace() ||
|
||
IsSpellingMagic() || GetBoothState() != 0 || m_iBuddyId != 0 || IsOperatingPet() != 0 || IsRebuildingPet() ||
|
||
IsUsingItem() || IsInvisible() || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_PLAYPOSE:
|
||
if (IsDead() || IsAboutToDie() || IsSitting() || IsJumping() || IsMeleeing() ||
|
||
IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() || IsChangingFace() ||
|
||
IsSpellingMagic() || IsShapeChanged() || IsReviving() || m_iMoveEnv != (int)MoveEnvironment.MOVEENV_GROUND ||
|
||
GetBoothState() != 0 || m_iBuddyId != 0 || IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() ||
|
||
IsRidingOnPet() || GetShapeType() == (int)PlayerModelType.PLAYERMODEL_DUMMYTYPE2 || IsPassiveMove() /*|| m_BattleInfo.IsChariotWar()*/)
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_SPELLMAGIC:
|
||
if (IsDead() || GPDataTypeHelper.ISMATTERID(m_idSelTarget) || IsAboutToDie() || IsSitting() ||
|
||
IsJumping() || IsFlashMoving() || IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() ||
|
||
IsChangingFace() || CannotAttack() || IsReviving() || GetBoothState() != 0 ||
|
||
m_iBuddyId != 0 || IsRidingOnPet() || IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_SUMMONPET:
|
||
if (IsDead() || GPDataTypeHelper.ISMATTERID(m_idSelTarget) || IsAboutToDie() || IsSitting() ||
|
||
IsJumping() || IsFlashMoving() || IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() ||
|
||
IsChangingFace() || CannotAttack() || IsReviving() || GetBoothState() != 0 ||
|
||
IsInvisible() || IsGMInvisible() || IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || IsPassiveMove()
|
||
/*|| m_BattleInfo.IsChariotWar()*/)
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_REBUILDPET:
|
||
if (IsDead() || GPDataTypeHelper.ISMATTERID(m_idSelTarget) || IsAboutToDie() || IsSitting() ||
|
||
IsJumping() || IsFlashMoving() || IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() ||
|
||
IsChangingFace() || CannotAttack() || IsReviving() || GetBoothState() != 0 ||
|
||
m_iBuddyId != 0 || IsInvisible() || IsGMInvisible() || IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || IsPassiveMove() ||
|
||
IsPlayerMoving() /*|| m_BattleInfo.IsChariotWar()*/)
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_USEITEM:
|
||
if (IsAboutToDie() || IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() ||
|
||
IsChangingFace() || GetBoothState() != 0 || IsPassiveMove() /*|| m_BattleInfo.IsChariotWar()*/)
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_JUMP:
|
||
if (IsDead() ||
|
||
m_iJumpCount >= MAX_JUMP_COUNT ||
|
||
// cannot jump more than one time if shape mode is type2
|
||
(IsJumping() && (GetShapeType() == (int)PlayerModelType.PLAYERMODEL_DUMMYTYPE2)) ||
|
||
IsJumpInWater() || m_iMoveEnv == (int)MoveEnvironment.MOVEENV_AIR || IsSitting() ||
|
||
IsMeleeing() || IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() ||
|
||
IsChangingFace() || IsReviving() || IsSpellingMagic() || IsPicking() ||
|
||
IsGathering() || IsRooting() || GetBoothState() != 0 || m_bHangerOn || (IsJumping() && IsRidingOnPet()) ||
|
||
IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || IsPassiveMove() /*|| m_BattleInfo.IsChariotWar()*/)
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_FOLLOW:
|
||
if (IsDead() || IsAboutToDie() || IsSitting() || IsMeleeing() || IsReviving() ||
|
||
IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() || IsChangingFace() ||
|
||
IsSpellingMagic() || GetBoothState() != 0 || m_bHangerOn || IsOperatingPet() != 0 || IsRebuildingPet() ||
|
||
IsUsingItem() || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_BOOTH:
|
||
if (IsDead() || IsAboutToDie() || IsPlayerMoving() || IsSitting() || IsReviving() ||
|
||
IsMeleeing() || IsJumping() || IsTrading() || IsUsingTrashBox() ||
|
||
IsTalkingWithNPC() || IsChangingFace() || IsSpellingMagic() || IsFlying() ||
|
||
IsUnderWater() || m_iBuddyId != 0 || IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || IsRidingOnPet() || IsInvisible() ||
|
||
IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_FLASHMOVE:
|
||
if (IsDead() || IsAboutToDie() || IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() ||
|
||
IsJumping() || IsFlashMoving() || IsFalling() || IsChangingFace() || GetBoothState() != 0 || IsTakingOff() ||
|
||
(m_pWorkMan != null && m_pWorkMan.HasWorkRunningOnPriority(CECHPWorkMan.Work_priority.PRIORITY_2)) ||
|
||
m_iBuddyId != 0 || IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_BINDBUDDY:
|
||
if (IsDead() || IsAboutToDie() || IsJumping() || IsSitting() ||
|
||
IsMeleeing() || IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() ||
|
||
IsChangingFace() || IsReviving() || IsSpellingMagic() || IsPicking() ||
|
||
IsGathering() || IsRooting() || GetBoothState() != 0 ||
|
||
(m_pWorkMan != null && !m_pWorkMan.IsStanding()) || m_iBuddyId != 0 ||
|
||
IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || GetShapeType() == (int)PlayerModelType.PLAYERMODEL_DUMMYTYPE2 || IsPassiveMove() ||
|
||
m_playerLimits[(int)PLAYER_LIMIT.PLAYER_LIMIT_NOBIND])
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_DUEL:
|
||
if (IsDead() || IsAboutToDie() || IsSitting() || IsFighting() || IsTrading() ||
|
||
IsReviving() || IsUsingTrashBox() || IsTalkingWithNPC() || IsChangingFace() ||
|
||
GetBoothState() != 0 || m_iBuddyId != 0 || m_pvp.iDuelState != (int)DuelState.DUEL_ST_NONE ||
|
||
IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_CHANGESELECT:
|
||
if (m_playerLimits[(int)PLAYER_LIMIT.PLAYER_LIMIT_NOCHANGESELECT])
|
||
bRet = false;
|
||
break;
|
||
|
||
case ActionCanDo.CANDO_SWITCH_PARALLEL_WORLD:
|
||
if (IsDead() || IsAboutToDie() || IsJumping() || IsFighting() ||
|
||
IsMeleeing() || IsTrading() || IsUsingTrashBox() || IsTalkingWithNPC() ||
|
||
IsChangingFace() || IsReviving() || IsSpellingMagic() || IsPicking() ||
|
||
IsGathering() || IsRooting() || GetBoothState() != 0 ||
|
||
m_iBuddyId != 0 || IsOperatingPet() != 0 || IsRebuildingPet() || IsUsingItem() ||
|
||
GetShapeType() == (int)PlayerModelType.PLAYERMODEL_DUMMYTYPE2 || IsPassiveMove())
|
||
bRet = false;
|
||
break;
|
||
}
|
||
|
||
return bRet;
|
||
}
|
||
|
||
public bool GetPushDir(ref Vector3 vPushDir, uint dwMask, float deltaTime)
|
||
{
|
||
vPushDir = Vector3.zero;
|
||
if (joystick.Horizontal == 0 && joystick.Vertical == 0)
|
||
{
|
||
if (isPressMoveUp)
|
||
{
|
||
vPushDir = Vector3.up;
|
||
}
|
||
else if (isPressMoveDown)
|
||
{
|
||
vPushDir = Vector3.down;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
if (m_iMoveEnv == Move_environment.MOVEENV_WATER || m_iMoveEnv == Move_environment.MOVEENV_AIR)
|
||
{
|
||
float angle = Vector2.Angle(new Vector2(joystick.Horizontal, joystick.Vertical), Vector2.up);
|
||
angle *= joystick.Horizontal < 0 ? 1 : -1;
|
||
Vector2 v2Cam = new Vector2(mainCam.transform.forward.x, mainCam.transform.forward.z);
|
||
v2Cam = Quaternion.Euler(0, 0, angle) * v2Cam;
|
||
v2Cam.Normalize();
|
||
vPushDir.x = v2Cam.x;
|
||
vPushDir.y = ((transform.position + Vector3.up * m_CDRInfo.vExtent.y) - mainCam.transform.position).normalized.y;
|
||
if (isPressMoveUp)
|
||
{
|
||
vPushDir.y = Math.Abs(vPushDir.y) * Time.deltaTime;
|
||
}
|
||
else if (isPressMoveDown)
|
||
{
|
||
vPushDir.y = -Math.Abs(vPushDir.y) * Time.deltaTime;
|
||
}
|
||
vPushDir.z = v2Cam.y;
|
||
}
|
||
else
|
||
{
|
||
float angle = Vector2.Angle(new Vector2(joystick.Horizontal, joystick.Vertical), Vector2.up);
|
||
angle *= joystick.Horizontal < 0 ? 1 : -1;
|
||
Vector2 v2Cam = new Vector2(mainCam.transform.forward.x, mainCam.transform.forward.z);
|
||
v2Cam = Quaternion.Euler(0, 0, angle) * v2Cam;
|
||
v2Cam.Normalize();
|
||
vPushDir.x = v2Cam.x;
|
||
vPushDir.y = 0;
|
||
vPushDir.z = v2Cam.y;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// Is under water
|
||
bool CanTakeOffWater()
|
||
{
|
||
// TO DO: fix later
|
||
//A3DVECTOR3 vPos = GetPos();
|
||
//if (vPos.y < EC_Game.GetGameRun().GetWorld().GetWaterHeight(vPos) - m_MoveConst.fShoreDepth)
|
||
// return false;
|
||
//else
|
||
// return true;
|
||
|
||
A3DVECTOR3 vPos = GetPos();
|
||
float h0 = 0f;
|
||
int countHits0 = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vPos) + Vector3.up * 500f, Vector3.down, hits, 1000f, 1 << 8);
|
||
if (countHits0 > 0)
|
||
{
|
||
h0 = hits[0].point.y;
|
||
}
|
||
if (vPos.y < h0 - m_MoveConst.fShoreDepth)
|
||
{
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
bool IsUsingItem()
|
||
{
|
||
return m_pWorkMan.IsUsingItem();
|
||
}
|
||
|
||
bool IsPassiveMove()
|
||
{
|
||
return m_pWorkMan.IsPassiveMoving();
|
||
}
|
||
|
||
// Is about to die / 是否即将死亡
|
||
bool IsAboutToDie()
|
||
{
|
||
return m_bAboutToDie;
|
||
}
|
||
|
||
// Is rebuilding pet / 是否正在重建宠物
|
||
bool IsRebuildingPet()
|
||
{
|
||
return m_bInRebuildPet;
|
||
}
|
||
|
||
// Is riding on pet / 是否骑乘宠物
|
||
bool IsRidingOnPet()
|
||
{
|
||
return m_RidingPet.id != 0;
|
||
}
|
||
|
||
// Is flash moving / 是否在闪移
|
||
bool IsFlashMoving()
|
||
{
|
||
if (m_pWorkMan == null) return false;
|
||
return m_pWorkMan.IsFlashMoving();
|
||
}
|
||
|
||
// Get buddy state / 获取伙伴状态
|
||
// return value: 0 = no buddy, 1 = has buddy, 2 = hanger on
|
||
int GetBuddyState()
|
||
{
|
||
if (m_bHangerOn) return 2;
|
||
if (m_iBuddyId != 0) return 1;
|
||
return 0;
|
||
}
|
||
|
||
// Is invisible / 是否隐身
|
||
bool IsInvisible()
|
||
{
|
||
return (m_dwStates & (uint)PlayerNPCState.GP_STATE_INVISIBLE) != 0;
|
||
}
|
||
|
||
// Is GM invisible / 是否GM隐身
|
||
bool IsGMInvisible()
|
||
{
|
||
// GMF_INVISIBLE would be a constant, using bit check
|
||
// GMF_INVISIBLE 将是一个常量,使用位检查
|
||
return (m_dwGMFlags & 0x01) != 0; // Assuming GMF_INVISIBLE = 0x01
|
||
}
|
||
|
||
// Is shape changed / 是否形状已改变
|
||
bool IsShapeChanged()
|
||
{
|
||
return m_iShape != 0;
|
||
}
|
||
|
||
// Is taking off / 是否正在起飞
|
||
bool IsTakingOff()
|
||
{
|
||
if (m_pWorkMan == null) return false;
|
||
return m_pWorkMan.IsFlyingOff();
|
||
}
|
||
|
||
// Is flying / 是否在飞行
|
||
bool IsFlying()
|
||
{
|
||
return (m_dwStates & (uint)PlayerNPCState.GP_STATE_FLY) != 0;
|
||
}
|
||
|
||
//public void SetGroundInfoClient()
|
||
//{
|
||
// isGrounded = GroundCheck(out lastGroundHit);
|
||
// m_GndInfo.bOnGround = isGrounded;
|
||
//}
|
||
|
||
public void SetRotationHP(Vector3 dir)
|
||
{
|
||
transform.rotation = Quaternion.LookRotation(dir);
|
||
}
|
||
|
||
public void SetRotationHPWithTime(Vector3 dir, float time)
|
||
{
|
||
StartCoroutine(RotateToDir(transform, dir, time));
|
||
}
|
||
|
||
IEnumerator RotateToDir(Transform target, Vector3 dir, float duration)
|
||
{
|
||
Quaternion startRot = target.rotation;
|
||
Quaternion endRot = Quaternion.LookRotation(dir);
|
||
float t = 0f;
|
||
|
||
while (t < duration)
|
||
{
|
||
t += Time.deltaTime;
|
||
target.rotation = Quaternion.Slerp(startRot, endRot, t / duration);
|
||
yield return null;
|
||
}
|
||
|
||
target.rotation = endRot;
|
||
}
|
||
|
||
void SetJumpInWater(bool b)
|
||
{
|
||
m_bJumpInWater = b;
|
||
}
|
||
|
||
// Is host in sliding state (in the state, host is sliding on slope) ?
|
||
public int GetProfession()
|
||
{
|
||
return m_iProfession;
|
||
}
|
||
public void OnAllInitDataReady()
|
||
{
|
||
if (IsDead())
|
||
{
|
||
/* CECGameUIMan pGameUI = g_pGame.GetGameRun().GetUIManager().GetInGameUIMan();
|
||
pGameUI.PopupReviveDialog(true);*/
|
||
PopupManager.Instance.OnPlayerDied();
|
||
}
|
||
m_bEnterGame = true;
|
||
}
|
||
public void SetSelectedTarget(int id)
|
||
{
|
||
if (m_idSelTarget != id)
|
||
EventBus.Publish(new CECHostPlayer.TargetHUDClearEvent());
|
||
m_idSelTarget = id;
|
||
// In duel, when player selects a different target, cancel trace work so it doesn't overwrite selection on touch
|
||
if (IsInDuel() && id != 0 && id != m_pvp.idDuelOpp)
|
||
m_pWorkMan?.FinishRunningWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT);
|
||
// When selecting another player, publish NPCINFO so target HUD shows (server doesn't resend base info on select)
|
||
if (id != 0 && id != GetCharacterID() && GPDataTypeHelper.ISPLAYERID(id))
|
||
{
|
||
var elsePlayer = EC_ManMessageMono.Instance?.GetECManPlayer?.GetPlayer(id) as EC_ElsePlayer;
|
||
if (elsePlayer != null && elsePlayer.m_bBaseInfoReady)
|
||
{
|
||
string name = elsePlayer.GetName();
|
||
if (!string.IsNullOrEmpty(name))
|
||
EventBus.Publish(new CECHostPlayer.NPCINFO(name, 100, 100, id));
|
||
}
|
||
}
|
||
}
|
||
|
||
public new int GetSelectedTarget()
|
||
{
|
||
return m_idSelTarget;
|
||
}
|
||
|
||
// Auto select a attackable target / 自动选择一个可攻击的目标
|
||
// Auto select a attackable target / 自动选择一个可攻击的目标
|
||
public int AutoSelectTarget()
|
||
{
|
||
if (!CanDo(ActionCanDo.CANDO_CHANGESELECT))
|
||
return 0;
|
||
|
||
int idCurSel = (m_idSelTarget != 0 && m_idSelTarget != m_PlayerInfo.cid) ? m_idSelTarget : 0;
|
||
|
||
// Clean up invalid targets from tab selection array / 清理无效目标
|
||
if (idCurSel == 0)
|
||
{
|
||
// Rebuild selected table / 重建选中表
|
||
m_aTabSels.Clear();
|
||
}
|
||
else
|
||
{
|
||
// Remove targets that are too far / 移除距离过远的目标
|
||
for (int i = m_aTabSels.Count - 1; i >= 0; i--)
|
||
{
|
||
CECObject pObject = m_aTabSels[i];
|
||
if (pObject == null)
|
||
{
|
||
m_aTabSels.RemoveAt(i);
|
||
continue;
|
||
}
|
||
|
||
float fDistToHost = 0.0f;
|
||
if (pObject is EC_ElsePlayer pPlayer)
|
||
fDistToHost = pPlayer.GetDistToHost();
|
||
else if (pObject is CECNPC pNPC)
|
||
fDistToHost = pNPC.GetDistToHost();
|
||
|
||
if (fDistToHost > EC_RoleTypes.EC_TABSEL_DIST || !CanSafelySelectWith(fDistToHost))
|
||
{
|
||
m_aTabSels.RemoveAt(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
float fMinDist = 10000.0f;
|
||
CECObject pCand = null;
|
||
int idCandidate = 0;
|
||
|
||
// Get player candidates / 获取玩家候选列表
|
||
List<EC_ElsePlayer> aCandPlayers = new List<EC_ElsePlayer>();
|
||
EC_ManMessageMono.Instance.EC_ManPlayer.TabSelectCandidates(idCurSel, aCandPlayers);
|
||
|
||
foreach (var pPlayer in aCandPlayers)
|
||
{
|
||
if (pPlayer == null)
|
||
continue;
|
||
|
||
// Check whether this player is in selected array / 检查此玩家是否已在选中数组中
|
||
if (m_aTabSels.Contains(pPlayer))
|
||
continue; // This player has been in selected array / 此玩家已在选中数组中
|
||
|
||
// Record the nearest one as candidate / 记录最近的一个作为候选
|
||
float fDist = pPlayer.GetDistToHost();
|
||
if (fDist < fMinDist)
|
||
{
|
||
fMinDist = fDist;
|
||
pCand = pPlayer;
|
||
idCandidate = pPlayer.GetCharacterID();
|
||
}
|
||
}
|
||
|
||
// Get NPC candidates / 获取NPC候选列表
|
||
List<CECNPC> aCandNPCs = new List<CECNPC>();
|
||
EC_ManMessageMono.Instance.CECNPCMan.TabSelectCandidates(idCurSel, aCandNPCs);
|
||
|
||
foreach (var pNPC in aCandNPCs)
|
||
{
|
||
if (pNPC == null)
|
||
continue;
|
||
|
||
// Check whether this npc is in selected array / 检查此NPC是否已在选中数组中
|
||
if (m_aTabSels.Contains(pNPC))
|
||
continue; // This npc has been in selected array / 此NPC已在选中数组中
|
||
|
||
// Record the nearest one as candidate / 记录最近的一个作为候选
|
||
float fDist = pNPC.GetDistToHost();
|
||
if (fDist < fMinDist)
|
||
{
|
||
fMinDist = fDist;
|
||
pCand = pNPC;
|
||
idCandidate = pNPC.GetNPCID();
|
||
}
|
||
}
|
||
|
||
// Maximum number of candidates in tab selection array / 标签选择数组中的最大候选数
|
||
// Increased from 9 to 20 to allow more targets / 从9增加到20以允许更多目标
|
||
const int iMaxCand = 9;
|
||
int idNewSel = 0;
|
||
|
||
if (pCand != null && idCandidate != 0)
|
||
{
|
||
// Add candidate to tab selection array / 将候选添加到标签选择数组
|
||
if (m_aTabSels.Count >= iMaxCand)
|
||
{
|
||
m_aTabSels.RemoveAt(m_aTabSels.Count - 1);
|
||
m_aTabSels.Insert(0, pCand);
|
||
}
|
||
else
|
||
{
|
||
m_aTabSels.Add(pCand);
|
||
}
|
||
|
||
idNewSel = idCandidate;
|
||
}
|
||
else // No proper candidate was found / 未找到合适的候选
|
||
{
|
||
if (m_aTabSels.Count == 0)
|
||
{
|
||
idNewSel = idCurSel;
|
||
}
|
||
else // Try to select one which has been in selected array / 尝试选择已在选中数组中的一个
|
||
{
|
||
int iIndex = -1;
|
||
for (int i = 0; i < m_aTabSels.Count; i++)
|
||
{
|
||
CECObject pObject = m_aTabSels[i];
|
||
if (pObject == null)
|
||
continue;
|
||
|
||
int objId = 0;
|
||
if (pObject is EC_ElsePlayer pElsePlayer)
|
||
objId = pElsePlayer.GetCharacterID();
|
||
else if (pObject is CECNPC pNPC)
|
||
objId = pNPC.GetNPCID();
|
||
|
||
if (objId == idCurSel)
|
||
{
|
||
iIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
iIndex = (iIndex + 1) % m_aTabSels.Count;
|
||
CECObject pSelectedObj = m_aTabSels[iIndex];
|
||
if (pSelectedObj != null)
|
||
{
|
||
if (pSelectedObj is EC_ElsePlayer pSelPlayer)
|
||
idNewSel = pSelPlayer.GetCharacterID();
|
||
else if (pSelectedObj is CECNPC pSelNPC)
|
||
idNewSel = pSelNPC.GetNPCID();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Select the target if found / 如果找到目标则选择它
|
||
if (idNewSel != 0)
|
||
{
|
||
if (idNewSel != idCurSel)
|
||
{
|
||
SelectTarget(idNewSel);
|
||
}
|
||
}
|
||
|
||
return idNewSel;
|
||
}
|
||
|
||
|
||
//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;
|
||
//}
|
||
|
||
// Does host player have specified way point ?
|
||
bool HasWayPoint(uint? wID)
|
||
{
|
||
if (wID == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
for (int i = 0; i < m_aWayPoints.Count; i++)
|
||
{
|
||
if (m_aWayPoints[i] == wID)
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// Get faction role
|
||
public int GetFRoleID()
|
||
{
|
||
return m_idFRole;
|
||
}
|
||
|
||
public bool InSlidingState()
|
||
{
|
||
if (m_iMoveMode != (int)MoveMode.MOVE_SLIDE)
|
||
return false;
|
||
|
||
var work = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_STAND);
|
||
var standWork = work as CECHPWorkStand;
|
||
|
||
if (standWork != null)
|
||
{
|
||
if (standWork.GetStopSlideFlag())
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public FACTION_FORTRESS_CONFIG GetFactionFortressConfig()
|
||
{
|
||
elementdataman pDataMan = EC_Game.GetElementDataMan();
|
||
DATA_TYPE dt = DATA_TYPE.DT_INVALID;
|
||
var ob = pDataMan.get_data_ptr(854, ID_SPACE.ID_SPACE_CONFIG, ref dt);
|
||
FACTION_FORTRESS_CONFIG pConfig = new FACTION_FORTRESS_CONFIG();
|
||
if (ob == null || dt != DATA_TYPE.DT_FACTION_FORTRESS_CONFIG)
|
||
{
|
||
// ûÓлùµØÅäÖñí
|
||
pConfig.require_level = int.MinValue;
|
||
}
|
||
else
|
||
{
|
||
pConfig = (FACTION_FORTRESS_CONFIG)ob;
|
||
}
|
||
|
||
return pConfig;
|
||
}
|
||
|
||
public bool IsInFortressWar()
|
||
{
|
||
bool bInWar = false;
|
||
if (IsInFortress())
|
||
{
|
||
int serverTime = EC_Game.GetServerGMTTime();
|
||
if (m_fortressEnter.end_time > serverTime)
|
||
bInWar = true;
|
||
}
|
||
|
||
return bInWar;
|
||
}
|
||
|
||
public bool IsFactionMember(int idTargetFaction)
|
||
{
|
||
// µ±Ç°ÊôÓÚij°ïÅÉ£¬ÇÒ¶Ô·½ÊôÓÚͬһ°ïÅÉʱ·µ»Ø true
|
||
return GetFactionID() != 0 && GetFactionID() == idTargetFaction;
|
||
}
|
||
|
||
public bool IsFactionAllianceMember(int idTargetFaction)
|
||
{
|
||
// µ±Ç°ÊôÓÚij°ïÅÉ£¬¶Ô·½ÊôÓÚͬһ°ïÅÉ¡¢»òÕßÊôÓÚͬÃ˰ïÅÉ
|
||
return GetFactionID() != 0
|
||
&& idTargetFaction != 0
|
||
&& (GetFactionID() == idTargetFaction || EC_Game.GetFactionMan().IsFactionAlliance(idTargetFaction));
|
||
}
|
||
|
||
public int GetCountry()
|
||
{
|
||
return m_idCountry;
|
||
}
|
||
|
||
// Get battle info.
|
||
//public BATTLEINFO GetBattleInfo() { return m_BattleInfo; }
|
||
|
||
//public bool IsInCountryWar() { return IsInBattle() && GetBattleInfo().IsCountryWar(); }
|
||
|
||
// End NPC service
|
||
public void EndNPCService()
|
||
{
|
||
m_idSevNPC = 0;
|
||
m_bTalkWithNPC = false;
|
||
m_iBoothState = 0;
|
||
m_bIsInKingService = false;
|
||
//m_pOffShopCtrl.SetNPCSevFlag(COfflineShopCtrl::NPCSEV_NULL);
|
||
}
|
||
|
||
void UpdateGFXs(float dwDeltaTime)
|
||
{
|
||
// if (m_pLevelUpGFX)
|
||
// m_pLevelUpGFX.SetParentTM(GetAbsoluteTM());
|
||
|
||
// Update hover GFX / 更新悬停特效
|
||
if (m_pHoverGFX) // && m_idCurHover != m_idSelTarget)
|
||
{
|
||
if (!IsChangingFace() && (GPDataTypeHelper.ISPLAYERID(m_idCurHover) || GPDataTypeHelper.ISNPCID(m_idCurHover)))
|
||
{
|
||
CECObject pObject = EC_ManMessageMono.Instance?.GetObject(m_idCurHover, 1);
|
||
if (pObject)
|
||
{
|
||
// Start GFX if stopped / 如果停止则启动特效
|
||
if (m_pHoverGFX.GetState() == GFX_STATE.ST_STOP)
|
||
m_pHoverGFX.Play();
|
||
|
||
// Update parent transform every frame to follow target / 每帧更新父变换以跟随目标
|
||
if (m_pHoverGFX.transform.parent != pObject.transform)
|
||
{
|
||
m_pHoverGFX.transform.parent = pObject.transform;
|
||
m_pHoverGFX.transform.localPosition = Vector3.zero;
|
||
}
|
||
}
|
||
else
|
||
m_pHoverGFX.Stop(true);
|
||
}
|
||
else
|
||
m_pHoverGFX.Stop(true);
|
||
}
|
||
|
||
// Update selected GFX / 更新选中特效
|
||
if (m_pSelectedGFX)
|
||
{
|
||
if (!IsChangingFace() && (GPDataTypeHelper.ISPLAYERID(m_idSelTarget) || GPDataTypeHelper.ISNPCID(m_idSelTarget)))
|
||
{
|
||
CECObject pObject = EC_ManMessageMono.Instance?.GetObject(m_idSelTarget, 1);
|
||
if (pObject)
|
||
{
|
||
// Start GFX if stopped / 如果停止则启动特效
|
||
if (m_pSelectedGFX.GetState() == GFX_STATE.ST_STOP)
|
||
m_pSelectedGFX.Play();
|
||
|
||
// Update parent transform every frame to follow target / 每帧更新父变换以跟随目标
|
||
if (m_pSelectedGFX.transform.parent != pObject.transform)
|
||
{
|
||
m_pSelectedGFX.transform.parent = pObject.transform;
|
||
m_pSelectedGFX.transform.localPosition = Vector3.zero;
|
||
}
|
||
}
|
||
else
|
||
m_pSelectedGFX.Stop(true);
|
||
}
|
||
else
|
||
m_pSelectedGFX.Stop(true);
|
||
}
|
||
|
||
// if (m_pFloatDust)
|
||
// {
|
||
// A3DTerrainWater* pWater = g_pGame.GetGameRun().GetWorld().GetTerrainWater();
|
||
//
|
||
// if (pWater.IsUnderWater(m_CameraCoord.GetPos()))
|
||
// {
|
||
// if (m_pFloatDust.GetState() == ST_STOP)
|
||
// {
|
||
// m_pFloatDust.Start(true);
|
||
// m_pFloatDust.TickAnimation(2000);
|
||
// }
|
||
//
|
||
// m_pFloatDust.SetParentTM(GetAbsoluteTM());
|
||
// }
|
||
// else if (m_pFloatDust.GetState() != ST_STOP)
|
||
// m_pFloatDust.Stop();
|
||
// }
|
||
// UpdateMonsterSpiritGfx(dwDeltaTime);
|
||
}
|
||
public byte GetRealmLevel() { return m_RealmLevel; }
|
||
|
||
// Level up
|
||
public void LevelUp()
|
||
{
|
||
// CECGameSession *pSession = g_pGame.GetGameSession();
|
||
//
|
||
// m_BasicProps.iLevel++;
|
||
// g_pGame.GetGameRun().AddFixedMessage(FIXMSG_LEVELUP, m_BasicProps.iLevel);
|
||
//
|
||
// // Get all extend properties
|
||
// pSession.c2s_CmdGetExtProps();
|
||
|
||
// if (m_pLevelUpGFX)
|
||
// m_pLevelUpGFX.Start(true);
|
||
PlayGfx(EC_Resource.res_GFXFile((int)GfxResourceType.RES_GFX_LEVELUP), null, 1f, 1); //PLAYERMODEL_TYPEALL
|
||
|
||
// // Popup notify bubble text
|
||
// BubbleText(BUBBLE_LEVELUP, 0);
|
||
//
|
||
// // Notify my friends that my level changed
|
||
// ACHAR szInfo[40];
|
||
// a_sprintf(szInfo, _AL("L%d"), m_BasicProps.iLevel);
|
||
//
|
||
// for (int i=0; i < m_pFriendMan.GetGroupNum(); i++)
|
||
// {
|
||
// CECFriendMan::GROUP* pGroup = m_pFriendMan.GetGroupByIndex(i);
|
||
// for (int j=0; j < pGroup.aFriends.GetSize(); j++)
|
||
// {
|
||
// CECFriendMan::FRIEND* pFriend = pGroup.aFriends[j];
|
||
// if (pFriend.IsGameOnline())
|
||
// {
|
||
// pSession.SendPrivateChatData(pFriend.GetName(),
|
||
// szInfo, GNET::CHANNEL_USERINFO, pFriend.id);
|
||
// }
|
||
// }
|
||
// }
|
||
//
|
||
// if (GetBasicProps().iLevel==30)
|
||
// {
|
||
// CECGameUIMan* pGameUI = g_pGame.GetGameRun().GetUIManager().GetInGameUIMan();
|
||
// pGameUI.AddChatMessage(pGameUI.GetStringFromTable(9638), GP_CHAT_SYSTEM);
|
||
// }
|
||
// if (GetBasicProps().iLevel>31)
|
||
// {
|
||
// CECGameUIMan* pGameUI = g_pGame.GetGameRun().GetUIManager().GetInGameUIMan();
|
||
// ((CDlgOnlineAward*)pGameUI.GetDialog("Win_AddExp2")).RestartWhenLevelup();
|
||
// }
|
||
}
|
||
|
||
// Estimate mouse cursor
|
||
private void EstimateCursor()
|
||
{
|
||
m_cursorUpdateTimer += Time.deltaTime;
|
||
if (m_cursorUpdateTimer < CURSOR_UPDATE_INTERVAL)
|
||
return;
|
||
m_cursorUpdateTimer = 0f;
|
||
|
||
// Early exit checks
|
||
if (IsChangingFace() || mainCam == null || m_cachedMouse == null)
|
||
{
|
||
m_idCurHover = 0;
|
||
return;
|
||
}
|
||
|
||
// Get mouse position using cached device
|
||
Vector2 mousePosition = m_cachedMouse.position.ReadValue();
|
||
|
||
// Early exit if mouse hasn't moved significantly (2 pixel threshold)
|
||
if (Vector2.Distance(mousePosition, m_lastMousePosition) < 2f)
|
||
return;
|
||
|
||
m_lastMousePosition = mousePosition;
|
||
m_idCurHover = 0;
|
||
|
||
CursorType cursorType = CursorType.RES_CUR_NORMAL;
|
||
|
||
// Check modifier keys using cached keyboard
|
||
bool isShiftPressed = m_cachedKeyboard != null &&
|
||
(m_cachedKeyboard.leftShiftKey.isPressed || m_cachedKeyboard.rightShiftKey.isPressed);
|
||
|
||
Ray ray = mainCam.ScreenPointToRay(mousePosition);
|
||
RaycastHit hit;
|
||
|
||
// You can add a layer mask here to only raycast against specific layers
|
||
// LayerMask interactableMask = LayerMask.GetMask("NPC", "Player", "Item");
|
||
// if (Physics.Raycast(ray, out hit, 100f, interactableMask))
|
||
|
||
if (Physics.Raycast(ray, out hit, 1000f)) // Reduced from 1000f to 100f for better performance
|
||
{
|
||
// Try to get CECObject component (cached lookup)
|
||
CECObject hitObject = hit.collider.GetComponent<CECObject>();
|
||
|
||
if (hitObject != null)
|
||
{
|
||
int idHitObject = CECObject.GetObjectID(hitObject);
|
||
|
||
if (idHitObject != 0)
|
||
{
|
||
bool bForceAttack = isShiftPressed;
|
||
|
||
// Check object type and set appropriate cursor
|
||
if (GPDataTypeHelper.ISNPCID(idHitObject))
|
||
{
|
||
// NPC handling
|
||
CECNPC pNPC = EC_ManMessageMono.Instance?.CECNPCMan?.GetNPC(idHitObject);
|
||
if (pNPC != null && !pNPC.IsDead())
|
||
{
|
||
m_idCurHover = idHitObject;
|
||
|
||
if (m_idSelTarget == idHitObject && AttackableJudge(idHitObject, bForceAttack) == 1)
|
||
{
|
||
cursorType = CursorType.RES_CUR_ATTACK;
|
||
}
|
||
else if (pNPC.IsServerNPC())
|
||
{
|
||
if (!IsInBattle() || InSameBattleCamp(pNPC))
|
||
{
|
||
cursorType = CursorType.RES_CUR_TALK;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (GPDataTypeHelper.ISPLAYERID(idHitObject))
|
||
{
|
||
// Player handling
|
||
EC_ElsePlayer pPlayer =
|
||
EC_ManMessageMono.Instance?.GetECManPlayer?.GetPlayer(idHitObject) as EC_ElsePlayer;
|
||
if (pPlayer != null)
|
||
{
|
||
m_idCurHover = idHitObject;
|
||
|
||
if (m_idSelTarget == idHitObject && AttackableJudge(idHitObject, bForceAttack) == 1)
|
||
{
|
||
cursorType = CursorType.RES_CUR_ATTACK;
|
||
}
|
||
}
|
||
}
|
||
else if (GPDataTypeHelper.ISMATTERID(idHitObject))
|
||
{
|
||
//todo
|
||
// BMLogger.LogError($"[EstimateCursor]- GPDataTypeHelper.ISMATTERID: {idHitObject}");
|
||
// Matter/item handling (uncomment when CECMatter is implemented)
|
||
// CECMatter pMatter = GetMatterManager()?.GetMatter(idHitObject);
|
||
// if (pMatter != null)
|
||
// {
|
||
// if (!pMatter.IsMine())
|
||
// cursorType = CursorType.Pickup;
|
||
// else if (CanGatherMatter(pMatter))
|
||
// cursorType = pMatter.IsMonsterSpiritMine() ? CursorType.Swallow : CursorType.Dig;
|
||
//
|
||
// if (cursorType != CursorType.Normal)
|
||
// m_idCurHover = idHitObject;
|
||
// }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Apply cursor change
|
||
EC_Game.ChangeCursor((int)cursorType);
|
||
}
|
||
|
||
bool IsJumpInWater()
|
||
{
|
||
return m_bJumpInWater;
|
||
}
|
||
|
||
// Is under water
|
||
bool IsUnderWater()
|
||
{
|
||
return m_iMoveEnv == Move_environment.MOVEENV_WATER ? true : false;
|
||
}
|
||
|
||
// Can jump or take off in water ?
|
||
public bool IsSitting()
|
||
{
|
||
return (m_dwStates & PlayerNPCState.GP_STATE_SITDOWN) != 0 ? true : false;
|
||
}
|
||
|
||
// Is host player open trash box ?
|
||
bool IsUsingTrashBox()
|
||
{
|
||
return m_bUsingTrashBox;
|
||
}
|
||
|
||
// Is host player talking with NPC ?
|
||
public bool IsTalkingWithNPC()
|
||
{
|
||
return m_bTalkWithNPC;
|
||
}
|
||
|
||
// Is reviving
|
||
bool IsReviving()
|
||
{
|
||
return m_pWorkMan.IsReviving();
|
||
}
|
||
|
||
// Is spelling magic
|
||
public bool IsSpellingMagic()
|
||
{
|
||
if (m_pWorkMan == null) return false;
|
||
return m_pWorkMan.IsSpellingMagic();
|
||
}
|
||
|
||
// Is picking up something
|
||
bool IsPicking()
|
||
{
|
||
return false;
|
||
// TODO: fix later
|
||
//CECHPWork pWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_PICKUP);
|
||
//if (pWork != null)
|
||
//{
|
||
// return !(pWork as CECHPWorkPick).IsGather();
|
||
//}
|
||
//else
|
||
// return false;
|
||
}
|
||
|
||
// Is gathering resources
|
||
public bool IsGathering()
|
||
{
|
||
if (m_pWorkMan == null) return false;
|
||
CECHPWork pWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_PICKUP);
|
||
if (pWork != null)
|
||
return ((EC_HPWorkPick)pWork).IsGather();
|
||
return false;
|
||
}
|
||
// Is using item ?
|
||
|
||
// Estimate move environment
|
||
void EstimateMoveEnv(A3DVECTOR3 vPos)
|
||
{
|
||
if (IsFlying())
|
||
{
|
||
m_iMoveEnv = Move_environment.MOVEENV_AIR;
|
||
return;
|
||
}
|
||
|
||
CECWorld pWorld = EC_Game.GetGameRun().GetWorld();
|
||
Vector3 vStart = EC_Utility.ToVector3(vPos);
|
||
Vector3 vGndPos0 = Vector3.zero;
|
||
Vector3 vTestPos0 = m_MoveCtrl.GetLastSevPos() + g_vAxisY * m_aabbServer.Extents.y;
|
||
VertRayTrace(vTestPos0, ref vGndPos0, ref m_GndInfo.vGndNormal, 1000f);
|
||
m_GndInfo.fGndHei = vGndPos0.y;
|
||
|
||
Vector3 vAABBGnd = Vector3.zero;
|
||
VertAABBTrace(vTestPos0, EC_Utility.ToVector3(m_aabbServer.Extents), ref vAABBGnd, ref m_GndInfo.vGndNormal,
|
||
1000f);
|
||
vAABBGnd.y -= m_aabbServer.Extents.y;
|
||
|
||
bool bIsInAir = false;
|
||
if (m_MoveCtrl.GetLastSevPos().y - vAABBGnd.y > 0.2f)
|
||
bIsInAir = true;
|
||
|
||
Vector3 vGndPos = Vector3.zero;
|
||
A3DVECTOR3 vTestPos = vPos + EC_Utility.ToA3DVECTOR3(g_vAxisY) * m_aabbServer.Extents.y;
|
||
VertRayTrace(EC_Utility.ToVector3(vTestPos), ref vGndPos, ref m_GndInfo.vGndNormal, 1000f);
|
||
m_GndInfo.fGndHei = vGndPos.y;
|
||
|
||
if (Physics.RaycastNonAlloc(EC_Utility.ToVector3(vTestPos) + Vector3.up * 500f, Vector3.down, hits, 1000f, 1 << 8) > 0)
|
||
{
|
||
m_GndInfo.fWaterHei = hits[0].point.y;
|
||
}
|
||
|
||
VertAABBTrace(EC_Utility.ToVector3(vTestPos), EC_Utility.ToVector3(m_aabbServer.Extents), ref vAABBGnd,
|
||
ref m_GndInfo.vGndNormal, 1000f);
|
||
vAABBGnd.y -= m_aabbServer.Extents.y;
|
||
|
||
int iNewEnv = Move_environment.MOVEENV_GROUND;
|
||
if (CheckWaterMoveEnv(vPos, m_GndInfo.fWaterHei, vAABBGnd.y))
|
||
{
|
||
iNewEnv = Move_environment.MOVEENV_WATER;
|
||
}
|
||
|
||
if (iNewEnv == Move_environment.MOVEENV_GROUND && GetPos().y - vAABBGnd.y < 0.2f && bIsInAir &&
|
||
EC_Utility.ToVector3(GetPos()) != m_MoveCtrl.GetLastSevPos())
|
||
{
|
||
m_MoveCtrl.SendMoveCmd(GetPos(), 2, EC_Utility.ToA3DVECTOR3(g_vAxisY), m_CDRInfo.vAbsVelocity,
|
||
m_iMoveMode, true);
|
||
// BubbleText(BUBBLE_LEVELUP, 0);
|
||
}
|
||
|
||
if (iNewEnv == Move_environment.MOVEENV_GROUND)
|
||
{
|
||
m_GndInfo.bOnGround = true;
|
||
|
||
// if (vPos.y > m_GndInfo.fGndHei + 0.2f)
|
||
if (m_CDRInfo.vTPNormal == new A3DVECTOR3(0))
|
||
{
|
||
if (m_iMoveMode != (int)MoveMode.MOVE_FREEFALL)
|
||
m_iMoveMode = (int)MoveMode.MOVE_FREEFALL;
|
||
|
||
m_GndInfo.bOnGround = false;
|
||
if (IsJumping() && m_CDRInfo.vAbsVelocity.y < 0.0f && vPos.y - vAABBGnd.y < 0.6f)
|
||
{
|
||
PlayAction((int)PLAYER_ACTION_TYPE.ACT_JUMP_LAND, false);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (IsJumping() && m_CDRInfo.vAbsVelocity.y < 0.0f && vPos.y - vAABBGnd.y < 0.6f)
|
||
{
|
||
PlayAction((int)PLAYER_ACTION_TYPE.ACT_JUMP_LAND, false);
|
||
ResetJump();
|
||
}
|
||
|
||
// if (m_GndInfo.vGndNormal.y < EC_SLOPE_Y)
|
||
if (m_CDRInfo.vTPNormal.y < EC_SLOPE_Y)
|
||
{
|
||
if (!m_MoveCtrl.GetSlideLock())
|
||
{
|
||
if (m_iMoveMode != (int)MoveMode.MOVE_SLIDE)
|
||
{
|
||
m_iOldWalkMode = m_iMoveMode;
|
||
m_iMoveMode = (int)MoveMode.MOVE_SLIDE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
m_MoveCtrl.SetSlideLock(false);
|
||
m_iMoveMode = (int)MoveMode.MOVE_STAND;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
m_MoveCtrl.SetSlideLock(false);
|
||
if (m_iMoveMode == (int)MoveMode.MOVE_FREEFALL)
|
||
{
|
||
m_iMoveMode = (int)MoveMode.MOVE_STAND;
|
||
}
|
||
else if (m_iMoveMode == (int)MoveMode.MOVE_SLIDE)
|
||
m_iMoveMode = m_iOldWalkMode;
|
||
}
|
||
}
|
||
}
|
||
else if (iNewEnv == Move_environment.MOVEENV_WATER)
|
||
{
|
||
m_CDRInfo.fYVel = 0.0f;
|
||
|
||
if (m_iMoveMode == (int)MoveMode.MOVE_SLIDE)
|
||
{
|
||
if (m_pWorkMan.IsMoving())
|
||
m_iMoveMode = (int)MoveMode.MOVE_MOVE;
|
||
else
|
||
m_iMoveMode = (int)MoveMode.MOVE_STAND;
|
||
}
|
||
}
|
||
|
||
m_iMoveEnv = iNewEnv;
|
||
}
|
||
|
||
// ÏòÏ Trace µØÐκͽ¨Öþ£¬²¢·µ»ØµÚÒ»¸öÅöײµãµÄÇé¿ö
|
||
bool VertRayTrace(Vector3 vPos, ref Vector3 vHitPos, ref A3DVECTOR3 vHitNormal, float DeltaY)
|
||
{
|
||
Vector3 vTerrainPos = Vector3.zero;
|
||
Vector3 vTerrainNormal = Vector3.zero;
|
||
Vector3 vBuildingPos = Vector3.zero;
|
||
Vector3 vBuildingNormal = Vector3.zero;
|
||
|
||
LayerMask layerMaskTerrain = 1 << 6;
|
||
LayerMask layerMaskBush = 1 << 7;
|
||
if (Physics.RaycastNonAlloc(vPos, (Vector3.down), hits, 1000f, layerMaskTerrain) > 0 /*&& hits[0].distance > 0.0009f*/)
|
||
{
|
||
vTerrainPos = hits[0].point;
|
||
vTerrainNormal = hits[0].normal;
|
||
}
|
||
|
||
if (Physics.RaycastNonAlloc(vPos, (Vector3.down), hits, DeltaY, layerMaskBush) > 0 /*&& hits[0].distance > 0.0009f*/)
|
||
{
|
||
if (vBuildingPos.y > vTerrainPos.y)
|
||
{
|
||
// Ó뽨Öþ·¢ÉúÁËÅöײ
|
||
vHitPos = vBuildingPos;
|
||
vHitNormal = EC_Utility.ToA3DVECTOR3(vBuildingNormal);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
if (vTerrainPos.y > vPos.y || (vTerrainPos.y <= vPos.y && vTerrainPos.y >= vPos.y - DeltaY))
|
||
{
|
||
vHitPos = vTerrainPos;
|
||
vHitNormal = EC_Utility.ToA3DVECTOR3(vTerrainNormal);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void VertAABBTrace(Vector3 vCenter, Vector3 vExt, ref Vector3 vHitPos, ref A3DVECTOR3 vHitNormal,
|
||
float DeltaY /* =100.0f */)
|
||
{
|
||
vHitPos = vCenter;
|
||
vHitPos.y -= DeltaY;
|
||
|
||
LayerMask layerMask = 1 << 6 | 1 << 7;
|
||
hits = new RaycastHit[5];
|
||
int count = Physics.BoxCastNonAlloc(vCenter, vExt, (Vector3.down).normalized,
|
||
hits, transform.rotation, DeltaY, layerMask);
|
||
if (count == 0 || (count > 0 && hits[0].distance < 0.0009f))
|
||
{
|
||
vHitPos = vCenter;
|
||
vHitNormal = EC_Utility.ToA3DVECTOR3(Vector3.up);
|
||
}
|
||
else
|
||
{
|
||
if (Math.Abs(hits[0].distance - 0f) <= float.Epsilon)
|
||
{
|
||
// halfBox with y = 0.05f? I need Y box check too small.
|
||
count = Physics.BoxCastNonAlloc(vCenter, new Vector3(vExt.x, 0.05f, vExt.z), (Vector3.down).normalized,
|
||
hits, transform.rotation, DeltaY, layerMask);
|
||
if (count == 0 || ( count > 0 && hits[0].distance < 0.0009f))
|
||
{
|
||
vHitPos = vCenter;
|
||
vHitNormal = EC_Utility.ToA3DVECTOR3(Vector3.up);
|
||
}
|
||
else
|
||
{
|
||
vHitPos = hits[0].point;
|
||
vHitPos.y += vExt.y;
|
||
vHitNormal = EC_Utility.ToA3DVECTOR3(hits[0].normal);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
vHitPos = hits[0].point;
|
||
vHitPos.y += vExt.y;
|
||
vHitNormal = EC_Utility.ToA3DVECTOR3(hits[0].normal);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Get cool time
|
||
public virtual int GetCoolTime(int iIndex, out int piMax /* NULL */)
|
||
{
|
||
piMax = 1;
|
||
if (iIndex >= 0 && iIndex < (int)CoolTimeIndex.GP_CT_MAX)
|
||
{
|
||
if (piMax > 0)
|
||
piMax = m_aCoolTimes[iIndex].iMaxTime;
|
||
return m_aCoolTimes[iIndex].iCurTime;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
// Play a pose
|
||
public bool CmdStartPose(int iPose)
|
||
{
|
||
// first of all see if we need to cancel sitdown work.
|
||
if (m_pWorkMan.IsSitting())
|
||
{
|
||
UnityGameSession.c2s_CmdStandUp();
|
||
return false;
|
||
}
|
||
|
||
if (!CanDo(ActionCanDo.CANDO_PLAYPOSE))
|
||
return false;
|
||
|
||
if (!m_pWorkMan.IsStanding())
|
||
return false;
|
||
|
||
if (iPose == (int)RoleExpression.ROLEEXP_SITDOWN)
|
||
{
|
||
// UnityGameSession.c2s_CmdSessionEmote(iPose);
|
||
}
|
||
else if (iPose == (int)RoleExpression.ROLEEXP_KISS)
|
||
{
|
||
// if (GPDataTypeHelper.ISPLAYERID(m_idSelTarget))
|
||
// UnityGameSession.c2s_CmdConEmoteRequest(RoleExpression.ROLEEXP_KISS, m_idSelTarget);
|
||
}
|
||
else
|
||
UnityGameSession.c2s_CmdEmoteAction((uint)iPose);
|
||
|
||
return true;
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
public struct EXPToUpLevel
|
||
{
|
||
public int NeededExp;
|
||
|
||
public EXPToUpLevel(int neededExp)
|
||
{
|
||
NeededExp = neededExp;
|
||
}
|
||
}
|
||
|
||
public enum MOVE_DIR
|
||
{
|
||
MD_FORWARD = 0x01,
|
||
MD_RIGHT = 0x02,
|
||
MD_BACK = 0x04,
|
||
MD_LEFT = 0x08,
|
||
MD_ABSUP = 0x10,
|
||
MD_ABSDOWN = 0x20,
|
||
MD_ALL = 0x3f,
|
||
};
|
||
|
||
public struct NPCINFO
|
||
{
|
||
public string Name; // Movement properties
|
||
public int CurrentHealth;
|
||
public int MaxHealth; // Attacking properties
|
||
public int IDNPC; // Attacking properties
|
||
|
||
public NPCINFO(string name, int currentHealth, int maxHealth, int idnpc)
|
||
{
|
||
Name = name;
|
||
CurrentHealth = currentHealth;
|
||
MaxHealth = maxHealth;
|
||
IDNPC = idnpc;
|
||
}
|
||
}
|
||
|
||
/// <summary>Fired when selected target HUD should be hidden (deselect or switch).</summary>
|
||
public struct TargetHUDClearEvent { }
|
||
|
||
public struct GNDINFO
|
||
{
|
||
public float fGndHei; // Ground height
|
||
public float fWaterHei; // Water height
|
||
public A3DVECTOR3 vGndNormal; // Terrain normal
|
||
public bool bOnGround; // On ground flag
|
||
};
|
||
|
||
public struct InfoHostPlayer
|
||
{
|
||
public string NameHostPlayer;
|
||
|
||
public InfoHostPlayer(string name)
|
||
{
|
||
NameHostPlayer = name;
|
||
}
|
||
}
|
||
public struct REINCARNATION_TOME
|
||
{
|
||
public int tome_exp;
|
||
public char tome_active; // 1¼¤»î0δ¼¤»î
|
||
public int max_level; // ÀúÊ·×î¸ßµÈ¼¶
|
||
cmd_reincarnation_tome_info._entry[] reincarnations;
|
||
static int max_exp;
|
||
};
|
||
public struct cmd_reincarnation_tome_info
|
||
{
|
||
public int tome_exp;
|
||
public char tome_active; // 1¼¤»î0δ¼¤»î
|
||
public int count;
|
||
public struct _entry
|
||
{
|
||
int level;
|
||
int timestamp;
|
||
int exp;
|
||
}
|
||
public _entry[] records;
|
||
};
|
||
// Behavior id used by CanDo()
|
||
public static class ActionCanDo
|
||
|
||
{
|
||
public const int CANDO_SITDOWN = 0,
|
||
CANDO_MOVETO = 1,
|
||
CANDO_MELEE = 2,
|
||
CANDO_ASSISTSEL = 3,
|
||
CANDO_FLY = 4,
|
||
CANDO_PICKUP = 5,
|
||
CANDO_TRADE = 6,
|
||
CANDO_PLAYPOSE = 7,
|
||
CANDO_SPELLMAGIC = 8,
|
||
CANDO_USEITEM = 9,
|
||
CANDO_JUMP = 10,
|
||
CANDO_FOLLOW = 11,
|
||
CANDO_GATHER = 12,
|
||
CANDO_BOOTH = 13,
|
||
CANDO_FLASHMOVE = 14,
|
||
CANDO_BINDBUDDY = 15,
|
||
CANDO_DUEL = 16,
|
||
CANDO_SUMMONPET = 17,
|
||
CANDO_CHANGESELECT = 18,
|
||
CANDO_REBUILDPET = 19,
|
||
CANDO_SWITCH_PARALLEL_WORLD = 20;
|
||
}
|
||
public bool FindMineTool(int tidMine, ref int piPack, ref int piIndex, ref int pidTool)
|
||
{
|
||
if (tidMine == 0)
|
||
return false;
|
||
|
||
DATA_TYPE DataType = DATA_TYPE.DT_INVALID;
|
||
object pDataPtr = ElementDataManProvider.GetElementDataMan()
|
||
.get_data_ptr((uint)tidMine, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
|
||
|
||
if (DataType != DATA_TYPE.DT_MINE_ESSENCE)
|
||
{
|
||
//ASSERT(DataType != DATA_TYPE.DT_MINE_ESSENCE);
|
||
return false;
|
||
}
|
||
|
||
MINE_ESSENCE pData = (MINE_ESSENCE)pDataPtr;
|
||
int idTool = (int)pData.id_equipment_required;
|
||
bool bRet = true;
|
||
|
||
if (idTool != 0)
|
||
{
|
||
int iIndex = IvtrPack.FindItem(idTool);
|
||
if (iIndex >= 0)
|
||
{
|
||
piPack = EC_Inventory.Inventory_type.IVTRTYPE_PACK;
|
||
piIndex = iIndex;
|
||
pidTool = idTool;
|
||
}
|
||
else if ((iIndex = IvtrTaskPack.FindItem(idTool)) >= 0)
|
||
{
|
||
piPack = EC_Inventory.Inventory_type.IVTRTYPE_TASKPACK;
|
||
piIndex = iIndex;
|
||
pidTool = idTool;
|
||
}
|
||
else
|
||
bRet = false;
|
||
}
|
||
else
|
||
{
|
||
piPack = 0;
|
||
piIndex = 0;
|
||
pidTool = 0;
|
||
}
|
||
|
||
return bRet;
|
||
}
|
||
|
||
public CECCounter GetGatherCnt() { return m_GatherCnt; }
|
||
|
||
public void UpdateTimers(float dwDeltaTime)
|
||
{
|
||
// Get real time tick of this frame
|
||
var iRealTime = EC_Game.GetRealTickTime();
|
||
|
||
// Update flysword time
|
||
/*if (IsFlying() && GetRushFlyFlag())
|
||
{
|
||
CECIvtrItem pItem = m_pEquipPack.GetItem(EQUIPIVTR_FLYSWORD);
|
||
Debug.Assert(pItem != null);
|
||
|
||
if (pItem.GetClassID() == CECIvtrItem.ICID_FLYSWORD)
|
||
{
|
||
CECIvtrFlySword pFlySword = (CECIvtrFlySword)pItem;
|
||
pFlySword.TimePass(dwDeltaTime);
|
||
}
|
||
}*/
|
||
|
||
int i;
|
||
|
||
// Update skills
|
||
for (i = 0; i < m_aPtSkills.Count; i++)
|
||
m_aPtSkills[i].Tick(dwDeltaTime);
|
||
|
||
for (i = 0; i < m_aPsSkills.Count; i++)
|
||
m_aPsSkills[i].Tick(dwDeltaTime);
|
||
|
||
for (i = 0; i < m_aGoblinSkills.Count; i++)
|
||
m_aGoblinSkills[i].Tick(dwDeltaTime);
|
||
|
||
for (i = 0; i < m_aEquipSkills.Count; i++)
|
||
m_aEquipSkills[i].Tick(dwDeltaTime);
|
||
|
||
if (m_pTargetItemSkill != null)
|
||
m_pTargetItemSkill.Tick(dwDeltaTime);
|
||
|
||
//CECComboSkillState.Instance().Tick();
|
||
|
||
// Update cool times
|
||
for (i = 0; i < (int)CoolTimeIndex.GP_CT_MAX; i++)
|
||
m_aCoolTimes[i].Update(dwDeltaTime);
|
||
|
||
foreach (var kvp in m_skillCoolTime)
|
||
{
|
||
kvp.Value.Update(dwDeltaTime);
|
||
}
|
||
|
||
// Gather time counter
|
||
if (m_GatherCnt.IncCounter(iRealTime * 1000))
|
||
m_GatherCnt.Reset(true);
|
||
|
||
// Incant time counter
|
||
if (m_IncantCnt.IncCounter(dwDeltaTime * 1000))
|
||
m_IncantCnt.Reset(true);
|
||
|
||
m_PetOptCnt.IncCounter(iRealTime);
|
||
// Bind command cool counter
|
||
/* if (m_BindCmdCoolCnt.IncCounter(dwDeltaTime))
|
||
m_BindCmdCoolCnt.Reset(true);
|
||
|
||
// Auto fashion time counter
|
||
if (m_bAutoFashion && GetBoothState() != 2 && !IsShapeChanged())
|
||
{
|
||
if (m_AutoFashionCnt.IncCounter(dwDeltaTime))
|
||
{
|
||
if (!CheckAutoFashionCondition())
|
||
{
|
||
while (!EquipFashionBySuitID((m_iCurFashionSuitID + 1) % GetMaxFashionSuitNum()))
|
||
m_iCurFashionSuitID++;
|
||
|
||
m_AutoFashionCnt.Reset();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Auto convert Yinpiao
|
||
if (m_AutoYinpiao.open)
|
||
{
|
||
if (m_AutoYinpiao.cnt.IncCounter(dwDeltaTime))
|
||
{
|
||
ExchangeYinpiao();
|
||
m_AutoYinpiao.cnt.Reset();
|
||
}
|
||
}
|
||
|
||
// Control the dialog of the target item
|
||
m_TargetItemDlgCtrl.Update(dwDeltaTime);
|
||
|
||
// For some reasons on server, sometimes friend list couldn't be got
|
||
// successfully. Try to get it again every 20s if this case really happen
|
||
if (m_pFriendMan != null && !m_pFriendMan.CheckInit())
|
||
{
|
||
m_iGetFriendCnt -= dwDeltaTime;
|
||
if (m_iGetFriendCnt < 0)
|
||
{
|
||
EC_Game.GetGameSession().friend_GetList();
|
||
m_iGetFriendCnt = 60000;
|
||
}
|
||
}
|
||
|
||
// Duel stopping time counter
|
||
if (m_pvp.iDuelState == DUEL_ST_PREPARE)
|
||
{
|
||
m_pvp.iDuelTimeCnt -= dwDeltaTime;
|
||
if (m_pvp.iDuelTimeCnt < 0)
|
||
m_pvp.iDuelTimeCnt = 0;
|
||
}
|
||
else if (m_pvp.iDuelState == DUEL_ST_INDUEL)
|
||
{
|
||
m_pvp.iDuelTimeCnt += dwDeltaTime;
|
||
}
|
||
else if (m_pvp.iDuelState == DUEL_ST_STOPPING)
|
||
{
|
||
m_pvp.iDuelTimeCnt -= dwDeltaTime;
|
||
if (m_pvp.iDuelTimeCnt < 0)
|
||
{
|
||
m_pvp.iDuelTimeCnt = 0;
|
||
m_pvp.iDuelState = DUEL_ST_NONE;
|
||
m_pvp.idDuelOpp = 0;
|
||
}
|
||
}
|
||
|
||
// Update pariah time counter
|
||
if (m_dwPariahTime > 0)
|
||
{
|
||
if (m_dwPariahTime > (uint)dwDeltaTime)
|
||
m_dwPariahTime -= (uint)dwDeltaTime;
|
||
else
|
||
m_dwPariahTime = 0;
|
||
}
|
||
|
||
// Update pet operation time counter
|
||
m_PetOptCnt.IncCounter(dwDeltaTime);
|
||
|
||
// Update battle result time counter
|
||
if (IsInBattle() && !IsInFortress() && m_BattleInfo.iResult != 0 && m_BattleInfo.iResultCnt != 0)
|
||
{
|
||
// iResultCnt is time counter (likely in milliseconds as int), dwDeltaTime is in seconds (float)
|
||
// iResultCnt是时间计数器(可能是毫秒为int),dwDeltaTime是秒数(float)
|
||
// Convert seconds to milliseconds and subtract / 将秒转换为毫秒并减去
|
||
int deltaTimeMs = (int)(dwDeltaTime * 1000f);
|
||
if ((m_BattleInfo.iResultCnt -= deltaTimeMs) < 0)
|
||
m_BattleInfo.iResultCnt = 0;
|
||
}
|
||
|
||
// Update pet corral
|
||
if (m_pPetCorral != null)
|
||
m_pPetCorral.Tick((uint)dwDeltaTime);
|
||
|
||
// Update the related people
|
||
var keysToRemove = new List<int>();
|
||
foreach (var kvp in m_RelatedPlayer)
|
||
{
|
||
m_RelatedPlayer[kvp.Key] -= dwDeltaTime;
|
||
if (m_RelatedPlayer[kvp.Key] <= 0)
|
||
keysToRemove.Add(kvp.Key);
|
||
}
|
||
foreach (var key in keysToRemove)
|
||
{
|
||
m_RelatedPlayer.Remove(key);
|
||
}*/
|
||
}
|
||
public struct COOLTIME
|
||
{
|
||
public int iCurTime;
|
||
public int iMaxTime;
|
||
|
||
public void Update(float dwDeltaTime)
|
||
{
|
||
if (iCurTime > 0)
|
||
{
|
||
iCurTime -= (int)(dwDeltaTime * 1000);
|
||
Mathf.Clamp(iCurTime, 0, float.MaxValue);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Start / Stop flying
|
||
public bool CmdFly(bool bForceFly)
|
||
{
|
||
//if (m_pActionSwitcher)
|
||
// m_pActionSwitcher.PostMessge(CECActionSwitcherBase::MSG_FLY);
|
||
|
||
// first of all see if we need to cancel sitdown work.
|
||
|
||
|
||
if (!CanDo(ActionCanDo.CANDO_FLY))
|
||
return false;
|
||
|
||
EC_IvtrItem pItem = IvtrEquipPack.GetItem(InventoryConst.EQUIPIVTR_FLYSWORD);
|
||
if (pItem == null)
|
||
return false;
|
||
|
||
if (pItem is EC_IvtrEquip)
|
||
{
|
||
if ((pItem as EC_IvtrEquip).IsDestroying())
|
||
return false;
|
||
}
|
||
|
||
if (!IsFlying())
|
||
{
|
||
// TODO: Maybe we should let server tell us whether we can fly or not
|
||
bool bCanFly = true;
|
||
|
||
if (m_iMoveEnv == (int)MoveEnvironment.MOVEENV_AIR)
|
||
bCanFly = false;
|
||
else if (m_iMoveEnv == (int)MoveEnvironment.MOVEENV_WATER && !CanTakeOffWater())
|
||
bCanFly = false;
|
||
|
||
if (!bCanFly)
|
||
return false;
|
||
}
|
||
|
||
UnityGameSession.c2s_SendCmdUseItem(InventoryConst.IVTRTYPE_EQUIPPACK, InventoryConst.EQUIPIVTR_FLYSWORD, pItem.GetTemplateID(), 1);
|
||
return true;
|
||
}
|
||
|
||
public CECCounter GetIncantCnt() { return m_IncantCnt; }
|
||
|
||
// Get key object(NPC..) coordinates
|
||
public A3DVECTOR3 GetObjectCoordinates(int idTarget, out List<OBJECT_COORD> TargetCoord, ref bool bInTable)
|
||
{
|
||
|
||
TargetCoord = new List<OBJECT_COORD>();
|
||
|
||
A3DVECTOR3 vDestPos = new A3DVECTOR3(0, 0, 0);
|
||
|
||
bInTable = false;
|
||
// Get object coordinates from CECGame::m_CoordTab
|
||
string szText = idTarget.ToString();
|
||
List<OBJECT_COORD> originalCoords = new List<OBJECT_COORD>();
|
||
int iCount = EC_Game.GetObjectCoord(szText, out originalCoords);
|
||
|
||
if (iCount == 0)
|
||
{
|
||
return vDestPos;
|
||
}
|
||
|
||
float fMinDist = 99999999.0f;
|
||
|
||
// Get Current map name, such as 'a32' etc.
|
||
int idInstance = CECGameRun.Instance.GetWorld().GetInstanceID();
|
||
CECInstance pInstance = CECGameRun.Instance.GetInstance(idInstance);
|
||
string strCurMap = pInstance.GetPath();
|
||
// �ȼ��ͬһ��ͼ���Ƿ���Ҫ���ҵ���Ʒ
|
||
bool bHasObjectInCurrentInstance = originalCoords.Any(coord => coord.strMap == strCurMap);
|
||
|
||
// Iterate over original list and build filtered TargetCoord list
|
||
for (int i = 0; i < iCount; i++)
|
||
{
|
||
OBJECT_COORD objCoord = originalCoords[i];
|
||
if (strCurMap == objCoord.strMap)
|
||
{
|
||
TargetCoord.Add(objCoord);
|
||
|
||
// Check if this is the nearest target
|
||
float tempDist = (objCoord.vPos - GetPos()).Magnitude();
|
||
if (tempDist < fMinDist)
|
||
{
|
||
fMinDist = tempDist;
|
||
bInTable = true;
|
||
vDestPos = objCoord.vPos;
|
||
}
|
||
}
|
||
// ��ǰ��ͼ��û��Ŀ��Ļ�����Ŀ�����ڵ�ͼ�ڵ�ǰ��ͼ�����
|
||
else if (!bHasObjectInCurrentInstance)
|
||
{
|
||
|
||
// find the entrance of instance
|
||
List<OBJECT_COORD> instCoord = new List<OBJECT_COORD>();
|
||
int iCount2 = EC_Game.GetObjectCoord(objCoord.strMap, out instCoord);
|
||
for (int j = 0; j < iCount2; ++j)
|
||
{
|
||
if (instCoord[j].strMap == strCurMap)
|
||
{
|
||
TargetCoord.Add(instCoord[j]);
|
||
|
||
// Check if this is the nearest target
|
||
float tempDist = (instCoord[j].vPos - GetPos()).Magnitude();
|
||
|
||
if (tempDist < fMinDist)
|
||
{
|
||
fMinDist = tempDist;
|
||
bInTable = true;
|
||
vDestPos = instCoord[j].vPos;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return vDestPos;
|
||
}
|
||
public int GetRealmLayer() { return m_RealmLevel / 100; }
|
||
public int GetRealmSubLevel() { return m_RealmLevel % 100; }
|
||
public static int GetRealmLayer(int realmLevel) { return realmLevel > 0 ? (realmLevel + 9) / 10 : 0; }
|
||
public static int GetRealmSubLevel(int realmLevel) { return realmLevel > 0 ? (realmLevel % 10 > 0 ? realmLevel % 10 : 10) : 0; }
|
||
|
||
// <summary>
|
||
// Calculate distance to an object and optionally retrieve the object reference
|
||
// 计算到对象的距离,并可选地获取对象引用
|
||
// </summary>
|
||
// <param name="idObject">Target object ID / 目标对象ID</param>
|
||
// <param name="pfDist">Output distance / 输出距离</param>
|
||
// <param name="ppObject">Output object reference (optional) / 输出对象引用(可选)</param>
|
||
// <returns>True if calculation succeeded / 计算成功返回true</returns>
|
||
public bool CalcDist(int idObject, out float pfDist, out CECObject ppObject)
|
||
{
|
||
pfDist = 0.0f;
|
||
ppObject = null;
|
||
|
||
if (idObject == 0 || idObject == m_PlayerInfo.cid)
|
||
return false;
|
||
|
||
CECWorld pWorld = CECGameRun.Instance.GetWorld();
|
||
if (pWorld == null)
|
||
return false;
|
||
|
||
CECObject pObject = pWorld.GetObject(idObject, 1);
|
||
if (ppObject == null)
|
||
return false;
|
||
|
||
ppObject = pObject;
|
||
float fDist = 0.0f;
|
||
|
||
if (GPDataTypeHelper.ISNPCID(idObject))
|
||
{
|
||
CECNPC pNPC = pObject as CECNPC;
|
||
if (pNPC == null)
|
||
return false;
|
||
fDist = pNPC.CalcDist(GetPos(), true);
|
||
}
|
||
else if (GPDataTypeHelper.ISMATTERID(idObject))
|
||
{
|
||
Debug.Assert(pObject.GetClassID() == Class_ID.OCID_MATTER);
|
||
CECMatter pMatter = pObject as CECMatter;
|
||
if (pMatter == null)
|
||
return false;
|
||
A3DVECTOR3 vDelta = pMatter.GetPos() - GetPos();
|
||
fDist = A3d_Magnitude(vDelta);
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
|
||
pfDist = fDist;
|
||
return true;
|
||
}
|
||
|
||
// Helper method overload without object output
|
||
public bool CalcDist(int idObject, out float pfDist)
|
||
{
|
||
CECObject pObject;
|
||
return CalcDist(idObject, out pfDist, out pObject);
|
||
}
|
||
|
||
private void UpdatePosWing()
|
||
{
|
||
if(m_HH_chibang == null)
|
||
{
|
||
m_HH_chibang = FindChildRecursive(m_pPlayerModel.transform, "HH_chibang");
|
||
}
|
||
if(m_HH_feijian == null)
|
||
{
|
||
m_HH_feijian = FindChildRecursive(m_pPlayerModel.transform, "HH_feijian");
|
||
}
|
||
if(m_Wing != null)
|
||
{
|
||
if (m_wingType == enumWingType.WINGTYPE_WING)
|
||
{
|
||
if(m_CC_chibang == null)
|
||
{
|
||
m_CC_chibang = FindChildRecursive(m_Wing, "CC_chibang");
|
||
}
|
||
m_delta_CC_to_HH = (m_Wing.position - m_CC_chibang.position);
|
||
m_Wing.position = m_HH_chibang.position + m_delta_CC_to_HH;
|
||
}
|
||
else if (m_wingType == enumWingType.WINGTYPE_FLYSWORD)
|
||
{
|
||
if (m_CC_feijian == null)
|
||
{
|
||
m_CC_feijian = FindChildRecursive(m_Wing, "CC_feijian");
|
||
}
|
||
//m_delta_CC_to_HH = (m_Wing.position - m_CC_feijian.position);
|
||
//m_Wing.position = m_HH_feijian.position + m_delta_CC_to_HH;
|
||
m_Wing.position = m_HH_feijian.position;
|
||
}
|
||
//else
|
||
//{
|
||
|
||
//}
|
||
}
|
||
|
||
//if (m_Wing2 != null)
|
||
//{
|
||
// if (m_wingType == enumWingType.WINGTYPE_WING)
|
||
// {
|
||
// m_Wing.transform.position = m_HH_chibang.transform.position;
|
||
// }
|
||
// else if (m_wingType == enumWingType.WINGTYPE_FLYSWORD)
|
||
// {
|
||
// m_Wing.transform.position = m_HH_feijian.transform.position;
|
||
// }
|
||
// else
|
||
// {
|
||
|
||
// }
|
||
//}
|
||
}
|
||
|
||
//// ID checking helper methods
|
||
//private bool ISNPCID(int id) => ((id & 0x80000000) != 0) && ((id & 0x40000000) == 0);
|
||
//private bool ISPLAYERID(int id) => id != 0 && (id & 0x80000000) == 0;
|
||
//private bool ISMATTERID(int id) => ((id) & 0xC0000000) == 0xC0000000;
|
||
|
||
// Release object
|
||
public void Release()
|
||
{
|
||
// TODO: Release all objects created by player, such as inventory, skills, etc.
|
||
// CECInstanceReenter::Instance().Clear();
|
||
// CECShoppingItemsMover::Instance().Clear();
|
||
// CECFashionShopManager::Instance().Clear();
|
||
// CECShoppingManager::Instance().Clear();
|
||
// CECUseUniversalTokenCommandManager::Instance().Clear();
|
||
// CECUniversalTokenHTTPOSNavigatorTicketHandler::Instance().Clear();
|
||
// RandMallShoppingManager::Instance().Release();
|
||
// CECFactionPVPModel::Instance().Clear();
|
||
// CECHostSkillModel::Instance().Release();
|
||
// CECComboSkillState::Instance().Release();
|
||
// CECPlayerLevelRankRealmChangeCheck::Instance().Release();
|
||
// CECHostFashionEquipFromStorageSystem::Instance().Clear();
|
||
//
|
||
// m_pSaveLifeTrigger = NULL;
|
||
// CECQuickBuyPopManager::Instance().ClearPolicies();
|
||
//
|
||
// // Ïú»ÙPlayerWrapper
|
||
// CECAutoPolicy::GetInstance().OnLeaveWorld();
|
||
//
|
||
// // Save favorite auction list first
|
||
// SaveFavorAucItems();
|
||
//
|
||
// // Release duel images
|
||
// ReleaseDuelImages();
|
||
//
|
||
// // Release sounds
|
||
// g_pGame->GetGameRun()->ReleaseSoundTable();
|
||
// m_pCurMoveSnd = NULL;
|
||
//
|
||
// // Release friend manger
|
||
// if (m_pFriendMan)
|
||
// {
|
||
// delete m_pFriendMan;
|
||
// m_pFriendMan = NULL;
|
||
// }
|
||
//
|
||
// // Release pet corral
|
||
// if (m_pPetCorral)
|
||
// {
|
||
// delete m_pPetCorral;
|
||
// m_pPetCorral = NULL;
|
||
// }
|
||
//
|
||
// if (m_pPetWords)
|
||
// {
|
||
// delete m_pPetWords;
|
||
// m_pPetWords = NULL;
|
||
// }
|
||
//
|
||
// if (m_pForceMgr)
|
||
// {
|
||
// delete m_pForceMgr;
|
||
// m_pForceMgr = NULL;
|
||
// }
|
||
//
|
||
// if (m_pOnlineAwardCtrl)
|
||
// {
|
||
// delete m_pOnlineAwardCtrl;
|
||
// m_pOnlineAwardCtrl = NULL;
|
||
// }
|
||
//
|
||
// if (m_pOffShopCtrl)
|
||
// {
|
||
// delete m_pOffShopCtrl;
|
||
// m_pOffShopCtrl = NULL;
|
||
// }
|
||
//
|
||
// if (m_pAutoTeam)
|
||
// {
|
||
// delete m_pAutoTeam;
|
||
// m_pAutoTeam = NULL;
|
||
// }
|
||
//
|
||
// if (m_pChariot)
|
||
// {
|
||
// delete m_pChariot;
|
||
// m_pChariot = NULL;
|
||
// }
|
||
//
|
||
// int i;
|
||
//
|
||
// // Release all shortcuts
|
||
// for (i=0; i < NUM_HOSTSCSETS1; i++)
|
||
// A3DRELEASE(m_aSCSets1[i]);
|
||
//
|
||
// for (i=0; i < NUM_HOSTSCSETS2; i++)
|
||
// A3DRELEASE(m_aSCSets2[i]);
|
||
//
|
||
// for (i=0; i < NUM_SYSMODSETS; i++)
|
||
// A3DRELEASE(m_aSCSetSysMod[i]);
|
||
//
|
||
// // Release all inventories
|
||
// A3DRELEASE(m_pPack);
|
||
// A3DRELEASE(m_pEquipPack);
|
||
// A3DRELEASE(m_pTrashBoxPack);
|
||
// A3DRELEASE(m_pTrashBoxPack2);
|
||
// A3DRELEASE(m_pTrashBoxPack3);
|
||
// A3DRELEASE(m_pAccountBoxPack);
|
||
// A3DRELEASE(m_pGeneralCardPack);
|
||
// A3DRELEASE(m_pTaskPack);
|
||
// A3DRELEASE(m_pDealPack);
|
||
// A3DRELEASE(m_pEPDealPack);
|
||
// A3DRELEASE(m_pTaskInterface);
|
||
// A3DRELEASE(m_pSpritePortrait);
|
||
// A3DRELEASE(m_pBuyPack);
|
||
// A3DRELEASE(m_pSellPack);
|
||
// A3DRELEASE(m_pBoothSPack);
|
||
// A3DRELEASE(m_pBoothBPack);
|
||
// A3DRELEASE(m_pEPBoothSPack);
|
||
// A3DRELEASE(m_pEPBoothBPack);
|
||
// A3DRELEASE(m_pEPEquipPack);
|
||
// A3DRELEASE(m_pClientGenCardPack);
|
||
//
|
||
// for (i=0; i < NUM_NPCIVTR; i++)
|
||
// {
|
||
// A3DRELEASE(m_aNPCPacks[i]);
|
||
// }
|
||
//
|
||
// // Release all skills
|
||
// ReleaseSkills();
|
||
//
|
||
// // Clear current combo skill
|
||
// ClearComboSkill();
|
||
//
|
||
// if (m_pWorkMan)
|
||
// {
|
||
// delete m_pWorkMan;
|
||
// m_pWorkMan = NULL;
|
||
// }
|
||
//
|
||
// m_CameraCtrl.Release();
|
||
//
|
||
// m_aTeamInvs.RemoveAll();
|
||
//
|
||
// g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pMoveTargetGFX);
|
||
// g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pSelectedGFX);
|
||
// g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pHoverGFX);
|
||
// g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pFloatDust);
|
||
//
|
||
// m_pMoveTargetGFX = NULL;
|
||
// m_pSelectedGFX = NULL;
|
||
// m_pHoverGFX = NULL;
|
||
// m_pFloatDust = NULL;
|
||
//
|
||
// // Clear tab select table
|
||
// m_aTabSels.RemoveAll(false);
|
||
//
|
||
// m_aForceInfo.RemoveAll();
|
||
//
|
||
// if (m_pActionSwitcher)
|
||
// {
|
||
// delete m_pActionSwitcher;
|
||
// m_pActionSwitcher = NULL;
|
||
// }
|
||
//
|
||
// CECQShopConfig::Instance().ClearBuyedItem();
|
||
//
|
||
// A3DRELEASE(m_pNavigatePlayer);
|
||
//
|
||
// CECPlayer::Release();
|
||
}
|
||
}
|
||
public sealed class CECHPTraceSpellMatcher : CECHPWorkMatcher
|
||
{
|
||
public override bool Match(CECHPWork pWork, int priority, bool isDelayWork)
|
||
{
|
||
if (pWork == null) return false;
|
||
if (pWork.GetWorkID() != Host_work_ID.WORK_TRACEOBJECT) return false;
|
||
|
||
// dynamic_cast<CECHPWorkTrace*>(pWork) trong C#
|
||
if (pWork is not CECHPWorkTrace trace) return false;
|
||
|
||
return trace.GetTraceReason() == Trace_reason.TRACE_SPELL;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|