done cast skill move linear
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:60c9fc32910746f134c7ade2390c674afb51eae2d258e8292b0bb9e817d02732
|
||||
size 282839
|
||||
oid sha256:53692fc411388ad4af78104667731d6952578dbdd4a549d5706ecb7b64986d63
|
||||
size 284380
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:486a1814b7c89098f245397c87a04b43c8b9d7b906d4a7de20930badea5292fb
|
||||
size 107133
|
||||
oid sha256:8c97935b0995b6688065daf1645d0f26e8b964dd5bf92b6cd66c37c5f97b5586
|
||||
size 104077
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace BrewMonster.Scripts
|
||||
|
||||
if (_loadedPrefabAssets.ContainsKey(assetPath))
|
||||
{
|
||||
BMLogger.Log($"AddressableManager: Asset already loaded: {assetPath} is valid: (${_loadedPrefabAssets[assetPath].Result != null})");
|
||||
//BMLogger.Log($"AddressableManager: Asset already loaded: {assetPath} is valid: (${_loadedPrefabAssets[assetPath].Result != null})");
|
||||
return _loadedPrefabAssets[assetPath].Result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +1,74 @@
|
||||
#define ENALBE_LOGGING
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class BMLogger
|
||||
{
|
||||
public static void Log(string message)
|
||||
{
|
||||
#if ENALBE_LOGGING
|
||||
Debug.Log(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogError(string message)
|
||||
{
|
||||
#if ENALBE_LOGGING
|
||||
Debug.LogError(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
#if ENALBE_LOGGING
|
||||
Debug.LogWarning(message);
|
||||
#endif
|
||||
}
|
||||
public static void LogMono(object source, string message)
|
||||
{
|
||||
#if ENALBE_LOGGING && UNITY_EDITOR
|
||||
if (DebugRegistry.IsEnabled(source))
|
||||
UnityEngine.Debug.LogError($"[{source}] {message}");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
#define ENALBE_LOGGING
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class BMLogger
|
||||
{
|
||||
// File logging callback - set by SessionFileLogger
|
||||
private static Action<string> s_FileLogCallback = null;
|
||||
|
||||
public static void SetFileLogCallback(Action<string> callback)
|
||||
{
|
||||
s_FileLogCallback = callback;
|
||||
}
|
||||
|
||||
public static void ClearFileLogCallback()
|
||||
{
|
||||
s_FileLogCallback = null;
|
||||
}
|
||||
|
||||
private static void WriteToFile(string message)
|
||||
{
|
||||
if (s_FileLogCallback != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
s_FileLogCallback(message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"BMLogger: Failed to write to file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Log(string message)
|
||||
{
|
||||
#if ENALBE_LOGGING
|
||||
Debug.Log(message);
|
||||
WriteToFile(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogError(string message)
|
||||
{
|
||||
#if ENALBE_LOGGING
|
||||
Debug.LogError(message);
|
||||
WriteToFile(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
#if ENALBE_LOGGING
|
||||
Debug.LogWarning(message);
|
||||
WriteToFile(message);
|
||||
#endif
|
||||
}
|
||||
public static void LogMono(object source, string message)
|
||||
{
|
||||
#if ENALBE_LOGGING && UNITY_EDITOR
|
||||
if (DebugRegistry.IsEnabled(source))
|
||||
{
|
||||
string fullMessage = $"[{source}] {message}";
|
||||
UnityEngine.Debug.LogError(fullMessage);
|
||||
WriteToFile(fullMessage);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,10 @@ namespace BrewMonster
|
||||
_instance = this as T;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
_instance = null;
|
||||
}
|
||||
/// <summary>Override this method to initialize the singleton</summary>
|
||||
protected virtual void Initialize()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles file logging for each Unity play session.
|
||||
/// Overwrites the same log file when play mode starts, with timestamp in filename to detect updates.
|
||||
/// Intercepts BMLogger calls and writes them to the session log file.
|
||||
///
|
||||
/// Usage:
|
||||
/// - Automatically initializes when play mode starts
|
||||
/// - Call BMLogger.LogError("HIHIHIHI") and it will appear in the log file
|
||||
/// - Each play session overwrites the log file (old files are cleaned up)
|
||||
/// - Filename includes timestamp so you can see when it was last updated
|
||||
/// - Log files are saved in the "Logs" directory in your project root
|
||||
/// </summary>
|
||||
public class SessionFileLogger : MonoBehaviour
|
||||
{
|
||||
private static SessionFileLogger s_Instance = null;
|
||||
private string m_LogFilePath = null;
|
||||
private StreamWriter m_LogWriter = null;
|
||||
private readonly object m_LockObject = new object();
|
||||
|
||||
[Header("Log Settings")]
|
||||
[Tooltip("Directory where log files will be saved (relative to project root)")]
|
||||
public string logDirectory = "Logs";
|
||||
|
||||
[Tooltip("Log file name prefix")]
|
||||
public string logFileNamePrefix = "SessionLog";
|
||||
|
||||
/// <summary>
|
||||
/// Auto-initializes when play mode starts.
|
||||
/// Creates the SessionFileLogger GameObject automatically.
|
||||
/// This ensures a fresh log file is created for each play session.
|
||||
/// </summary>
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void AutoInitialize()
|
||||
{
|
||||
// Clean up any existing instance (in case of play mode restart)
|
||||
if (s_Instance != null)
|
||||
{
|
||||
if (s_Instance.m_LogWriter != null)
|
||||
{
|
||||
s_Instance.Cleanup();
|
||||
}
|
||||
if (s_Instance.gameObject != null)
|
||||
{
|
||||
DestroyImmediate(s_Instance.gameObject);
|
||||
}
|
||||
s_Instance = null;
|
||||
}
|
||||
|
||||
// Create new instance for this play session
|
||||
GameObject loggerObj = new GameObject("SessionFileLogger");
|
||||
s_Instance = loggerObj.AddComponent<SessionFileLogger>();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Ensure only one instance exists
|
||||
if (s_Instance != null && s_Instance != this)
|
||||
{
|
||||
Destroy(this);
|
||||
return;
|
||||
}
|
||||
|
||||
s_Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
InitializeLogFile();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (s_Instance == this)
|
||||
{
|
||||
Cleanup();
|
||||
s_Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new log file for the current play session.
|
||||
/// Overwrites old log files and creates a new one with timestamp in filename.
|
||||
/// </summary>
|
||||
private void InitializeLogFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get project root directory
|
||||
string projectRoot = Application.dataPath.Replace("/Assets", "").Replace("\\Assets", "");
|
||||
string logDir = Path.Combine(projectRoot, logDirectory);
|
||||
|
||||
// Ensure directory exists
|
||||
if (!Directory.Exists(logDir))
|
||||
{
|
||||
Directory.CreateDirectory(logDir);
|
||||
}
|
||||
|
||||
// Clean up old log files with the same prefix (keep only the latest)
|
||||
CleanupOldLogFiles(logDir);
|
||||
|
||||
// Generate log file path with timestamp (so you can see when it was updated)
|
||||
string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||
string fileName = $"{logFileNamePrefix}_{timestamp}.txt";
|
||||
m_LogFilePath = Path.Combine(logDir, fileName);
|
||||
|
||||
// Create/overwrite the log file
|
||||
m_LogWriter = new StreamWriter(m_LogFilePath, false);
|
||||
m_LogWriter.WriteLine($"=== Play Session Log ===");
|
||||
m_LogWriter.WriteLine($"Started: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
m_LogWriter.WriteLine($"Project: {Application.productName}");
|
||||
m_LogWriter.WriteLine($"Unity Version: {Application.unityVersion}");
|
||||
m_LogWriter.WriteLine($"==========================================");
|
||||
m_LogWriter.WriteLine();
|
||||
m_LogWriter.Flush();
|
||||
|
||||
// Set up BMLogger callback
|
||||
BMLogger.SetFileLogCallback(WriteLogMessage);
|
||||
|
||||
Debug.Log($"[SessionFileLogger] Log file initialized: {m_LogFilePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[SessionFileLogger] Failed to initialize log file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes old log files with the same prefix to keep only the current session file.
|
||||
/// </summary>
|
||||
private void CleanupOldLogFiles(string logDir)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(logDir))
|
||||
return;
|
||||
|
||||
string searchPattern = $"{logFileNamePrefix}_*.txt";
|
||||
string[] oldFiles = Directory.GetFiles(logDir, searchPattern);
|
||||
|
||||
foreach (string oldFile in oldFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(oldFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[SessionFileLogger] Failed to delete old log file {oldFile}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[SessionFileLogger] Error cleaning up old log files: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a log message to the file.
|
||||
/// Called by BMLogger when Log, LogError, or LogWarning is called.
|
||||
/// </summary>
|
||||
private void WriteLogMessage(string message)
|
||||
{
|
||||
if (m_LogWriter == null || string.IsNullOrEmpty(m_LogFilePath))
|
||||
return;
|
||||
|
||||
lock (m_LockObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
|
||||
m_LogWriter.WriteLine($"[{timestamp}] {message}");
|
||||
m_LogWriter.Flush();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[SessionFileLogger] Failed to write log message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up the log file and removes BMLogger callback.
|
||||
/// </summary>
|
||||
private void Cleanup()
|
||||
{
|
||||
lock (m_LockObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_LogWriter != null)
|
||||
{
|
||||
m_LogWriter.WriteLine();
|
||||
m_LogWriter.WriteLine($"==========================================");
|
||||
m_LogWriter.WriteLine($"Session ended: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
m_LogWriter.Flush();
|
||||
m_LogWriter.Close();
|
||||
m_LogWriter = null;
|
||||
}
|
||||
|
||||
BMLogger.ClearFileLogCallback();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[SessionFileLogger] Error during cleanup: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current log file path (for debugging/inspection).
|
||||
/// </summary>
|
||||
public static string GetLogFilePath()
|
||||
{
|
||||
return s_Instance != null ? s_Instance.m_LogFilePath : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9118777afdffebc47861ed63bbb15c9a
|
||||
@@ -60,8 +60,6 @@ namespace BrewMonster
|
||||
bool bReverse
|
||||
)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddSkillGfxEvent: host={nHostID}, target={nTargetID}, fly={szFlyGfx ?? "NULL"}, hit={szHitGfx ?? "NULL"}, flyTime={dwFlyTimeSpan}, mode={FlyMode}, count={nFlyGfxCount}, interval={dwInterval}");
|
||||
|
||||
bool bRet = true, bCluster;
|
||||
uint dwDelayTime;
|
||||
|
||||
@@ -76,12 +74,9 @@ namespace BrewMonster
|
||||
bCluster = true;
|
||||
}
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddSkillGfxEvent: Creating {nFlyGfxCount} event(s), cluster={bCluster}, delay={dwDelayTime}");
|
||||
|
||||
for (int i = 0; i < nFlyGfxCount; i++)
|
||||
{
|
||||
string value = bOnlyOneHit && i != nFlyGfxCount - 1 ? "" : szHitGfx;
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddSkillGfxEvent: Creating event {i + 1}/{nFlyGfxCount}, hitGfx={value ?? "NULL"}");
|
||||
|
||||
if (!AddOneSkillGfxEvent(
|
||||
pComposer,
|
||||
@@ -101,13 +96,8 @@ namespace BrewMonster
|
||||
bReverse
|
||||
))
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddSkillGfxEvent: AddOneSkillGfxEvent FAILED for event {i + 1}");
|
||||
bRet = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddSkillGfxEvent: AddOneSkillGfxEvent SUCCESS for event {i + 1}");
|
||||
}
|
||||
|
||||
dwDelayTime += dwInterval;
|
||||
}
|
||||
@@ -131,7 +121,6 @@ namespace BrewMonster
|
||||
bool bIsGoblinSkill,
|
||||
bool bReverse)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddOneSkillGfxEvent: host={nHostID}, target={nTargetID}, fly={szFlyGfx ?? "NULL"}, hit={szHitGfx ?? "NULL"}, mode={mode}, flyTime={dwFlyTimeSpan}, delay={dwDelayTime}");
|
||||
|
||||
// Validate host ID
|
||||
// 验证施法者ID
|
||||
@@ -143,13 +132,8 @@ namespace BrewMonster
|
||||
|
||||
// Validate target ID - allow 0 for area skills, but warn about suspiciously large negative values
|
||||
// 验证目标ID - 允许0用于区域技能,但对可疑的大负值发出警告
|
||||
if (nTargetID < -1000000000) // Suspiciously large negative value (likely uninitialized)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddOneSkillGfxEvent: WARNING - Suspicious target ID ({nTargetID}), this may be uninitialized. Event will be created but may fail target lookup.");
|
||||
}
|
||||
|
||||
A3DSkillGfxEvent pEvent = SkillGfxMan.InstanceSub.GetEmptyEvent(mode);
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddOneSkillGfxEvent: Event created from pool, type={pEvent.GetType().Name}");
|
||||
|
||||
pEvent.SetComposer(pComposer);
|
||||
pEvent.SetHostID(nHostID);
|
||||
@@ -183,7 +167,6 @@ namespace BrewMonster
|
||||
pEvent.Tick(0);
|
||||
#endif
|
||||
PushEvent(pEvent);
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddOneSkillGfxEvent: Event pushed to SkillGfxMan, active events={SkillGfxMan.InstanceSub.m_EventLst.Count}");
|
||||
return true;
|
||||
}
|
||||
public virtual bool GetPropertyById(long nId, ref ECMODEL_GFX_PROPERTY pProperty) => false;
|
||||
|
||||
@@ -17,12 +17,22 @@ namespace BrewMonster
|
||||
{
|
||||
public class CECAttacksMan : MonoSingleton<CECAttacksMan>
|
||||
{
|
||||
private readonly LinkedList<CECAttackEvent> m_targets = new LinkedList<CECAttackEvent>();
|
||||
private LinkedList<CECAttackEvent> m_targets = new LinkedList<CECAttackEvent>();
|
||||
public CECMultiSectionSkillMan m_pMultiSkillGfxComposerMan;
|
||||
protected A3DSkillGfxComposerMan m_pSkillGfxComposerMan;
|
||||
#if UNITY_EDITOR
|
||||
public List<CECAttackEvent> m_AttackList = new List<CECAttackEvent>();
|
||||
#endif
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
}
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
m_targets = null;
|
||||
SkillGfxMan.InstanceSub = null;
|
||||
base.OnDestroy();
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
StartLoad();
|
||||
@@ -154,7 +164,9 @@ namespace BrewMonster
|
||||
//BMLogger.LogError("HoangDev: Update CECAttackEvent node.Value.m_bFinished: " + node.Value.m_bFinished);
|
||||
if (node.Value.m_bFinished)
|
||||
m_targets.Remove(node);
|
||||
else node.Value.Tick(dwDeltaTime);
|
||||
else {
|
||||
node.Value.Tick(dwDeltaTime);
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
|
||||
@@ -631,14 +643,10 @@ namespace BrewMonster
|
||||
{
|
||||
bool bCastInTargets = false;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Composer.Play: host={nHostID}, castTarget={nCastTargetID}, targets={(targets?.Count ?? 0)}, isGoblin={bIsGoblinSkill}");
|
||||
|
||||
// Determine GFX names from loaded prefabs / 从已加载的预制体确定GFX名称
|
||||
string szFly = flyGFX != null ? flyGfxName : null;
|
||||
string szHit = hitGFX != null ? hitGfxName : null;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Composer.Play: flyGFX={(flyGFX != null ? "LOADED" : "NULL")}, hitGFX={(hitGFX != null ? "LOADED" : "NULL")}, szFly={szFly ?? "NULL"}, szHit={szHit ?? "NULL"}");
|
||||
|
||||
// TODO Phase 2: Optimization checks / 第二阶段:优化检查
|
||||
// if (!CECOptimize.Instance.GetGFX().CanShowFly(nHostID)) szFly = null;
|
||||
// if (!CECOptimize.Instance.GetGFX().CanShowHit(nHostID)) szHit = null;
|
||||
@@ -668,7 +676,6 @@ namespace BrewMonster
|
||||
|
||||
int originalCount = targets.Count;
|
||||
targets = validTargets;
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Composer.Play: Processing {targets.Count} valid targets (filtered from {originalCount})");
|
||||
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
@@ -676,7 +683,6 @@ namespace BrewMonster
|
||||
|
||||
if (nCastTargetID == tar.idTarget)
|
||||
bCastInTargets = true;
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Composer.Play: Calling AddOneTarget for target {i}/{targets.Count}, id={tar.idTarget}");
|
||||
AddOneTarget(nCastTargetID, nHostID, szFly, szHit, tar, i == 0, bIsGoblinSkill);
|
||||
}
|
||||
}
|
||||
@@ -695,7 +701,6 @@ namespace BrewMonster
|
||||
tar.idTarget = nCastTargetID;
|
||||
tar.dwModifier = 0;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Composer.Play: Cast target not in targets list, adding separately");
|
||||
AddOneTarget(nCastTargetID, nHostID, szFly, szHit, tar, false, bIsGoblinSkill);
|
||||
}
|
||||
|
||||
@@ -710,12 +715,32 @@ namespace BrewMonster
|
||||
if (GPDataTypeHelper.ISNPCID(idTarget))
|
||||
{
|
||||
var npc = EC_ManMessageMono.Instance?.CECNPCMan?.GetNPCFromAll(idTarget);
|
||||
return npc != null && npc.gameObject != null;
|
||||
// Use Unity's == null check which properly handles destroyed objects
|
||||
// Unity destroyed objects pass != null but throw exceptions when accessed
|
||||
if (npc == null) return false;
|
||||
try
|
||||
{
|
||||
return npc.gameObject != null;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// Object was destroyed - return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (GPDataTypeHelper.ISPLAYERID(idTarget))
|
||||
{
|
||||
var player = EC_ManMessageMono.Instance?.GetECManPlayer?.GetPlayer(idTarget);
|
||||
return player != null && player.gameObject != null;
|
||||
if (player == null) return false;
|
||||
try
|
||||
{
|
||||
return player.gameObject != null;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// Object was destroyed - return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -732,9 +757,6 @@ namespace BrewMonster
|
||||
float fScale;
|
||||
bool bReverse;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddOneTarget: castTarget={nCastTargetID}, host={nHostID}, target={tar.idTarget}, fly={szFly ?? "NULL"}, hit={szHit ?? "NULL"}, first={bFirst}");
|
||||
|
||||
// 根据目标模式决定Host和Target的映射 / Determine Host and Target mapping based on target mode
|
||||
switch (m_TargetMode)
|
||||
{
|
||||
case GfxTargetMode.enumTargetToHost:
|
||||
@@ -753,8 +775,6 @@ namespace BrewMonster
|
||||
break;
|
||||
}
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddOneTarget: After mapping - _Host={_Host}, _Target={_Target}, reverse={bReverse}, moveMode={m_MoveMode}, flyTime={m_dwFlyTime}");
|
||||
|
||||
// 计算缩放 / Calculate scale
|
||||
/* if (m_bRelScl)
|
||||
fScale = SkillGfxMan.InstanceSub.GetTargetScale(_Target) / m_fDefTarScl * m_fHitGfxScale;
|
||||
@@ -781,8 +801,6 @@ namespace BrewMonster
|
||||
return;
|
||||
}
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] AddOneTarget: Calling m_pSkillGfxMan.AddSkillGfxEvent(host={_Host}, target={_Target}, fly={szFly ?? "NULL"}, hit={szHit ?? "NULL"}, flyTime={m_dwFlyTime}, moveMode={m_MoveMode})");
|
||||
|
||||
// 调用GFX管理器添加技能特效事件 / Call GFX manager to add skill GFX event
|
||||
m_pSkillGfxMan.AddSkillGfxEvent(
|
||||
this,
|
||||
@@ -829,6 +847,9 @@ public class CECAttackEvent
|
||||
public int m_nSkillLevel;
|
||||
public int m_nSkillSection;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
int debugCounter = 0; // Debug counter to track Tick calls
|
||||
#endif
|
||||
public CECAttackEvent() { }
|
||||
|
||||
public CECAttackEvent(CECAttacksMan? pManager, int idHost, int idCastTarget, int idTarget,
|
||||
@@ -844,6 +865,7 @@ public class CECAttackEvent
|
||||
m_timeToBeFired = (uint)nTimeToBeFired;
|
||||
m_timeToDoDamage = (uint)nTimeToDoDamage;
|
||||
m_bFinished = false;
|
||||
debugCounter = UnityEngine.Random.Range(0, 1000);
|
||||
|
||||
AddTarget(idTarget, dwModifier, nDamage);
|
||||
}
|
||||
@@ -895,13 +917,8 @@ public class CECAttackEvent
|
||||
public void SetSkillSection(int nSection) { m_nSkillSection = nSection; }
|
||||
bool DoFire()
|
||||
{
|
||||
float vFlyScale = 1.0f;
|
||||
float vHitScale = 1.0f;
|
||||
|
||||
m_bDoFired = true;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] DoFire: skill={m_idSkill}, host={m_idHost}, castTarget={m_idCastTarget}, targets={m_targets.Count}, section={m_nSkillSection}");
|
||||
|
||||
if (GPDataTypeHelper.ISPLAYERID(m_idHost))
|
||||
{
|
||||
|
||||
@@ -916,10 +933,8 @@ public class CECAttackEvent
|
||||
if (pMan != null)
|
||||
{
|
||||
bool isGoblin = ElementSkill.IsGoblinSkill((uint)m_idSkill);
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] DoFire: Multi-section skill, calling pMan.Play()");
|
||||
pMan.Play(m_idSkill, m_nSkillSection, m_idHost, m_idCastTarget, m_targets, isGoblin);
|
||||
pComposer = pMan.GetSkillGfxComposer(m_idSkill, m_nSkillSection);
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] DoFire: Multi-section composer={(pComposer != null ? "FOUND" : "NULL")}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -932,18 +947,15 @@ public class CECAttackEvent
|
||||
|
||||
// Get the composer manager
|
||||
var composerMan = m_pManager.GetSkillGfxComposerMan();
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] DoFire: composerMan={(composerMan != null ? "FOUND" : "NULL")}");
|
||||
|
||||
if (composerMan != null)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] DoFire: Calling composerMan.Play(skill={m_idSkill}, host={m_idHost}, castTarget={m_idCastTarget}, targets={m_targets.Count}, isGoblin={isGoblin})");
|
||||
if (isGoblin)
|
||||
composerMan.Play(m_idSkill, m_idHost, m_idCastTarget, m_targets, true);
|
||||
else
|
||||
composerMan.Play(m_idSkill, m_idHost, m_idCastTarget, m_targets);
|
||||
|
||||
pComposer = composerMan.GetSkillGfxComposer(m_idSkill);
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] DoFire: Composer lookup result={(pComposer != null ? "FOUND" : "NULL")}, flyTime={(pComposer != null ? pComposer.m_dwFlyTime : 0)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1484,4 +1496,4 @@ public enum GfxSkillValType
|
||||
enumGfxSkillInt,
|
||||
enumGfxSkillFloat,
|
||||
enumGfxSkillValTypeNum
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,12 +14,14 @@ using UnityEngine;
|
||||
|
||||
public class CECNPCMan : IMsgHandler
|
||||
{
|
||||
private Dictionary<int, CECNPC> m_NPCTab = new Dictionary<int, CECNPC>(512);
|
||||
private Dictionary<int, int> m_UkNPCTab = new Dictionary<int, int>(32);
|
||||
private Dictionary<int, CECNPC> m_NPCTab ;
|
||||
private Dictionary<int, int> m_UkNPCTab ;
|
||||
|
||||
List<CECNPC> m_aDisappearNPCs = new List<CECNPC>(32);
|
||||
List<CECNPC> m_aDisappearNPCs ;
|
||||
public int HandlerId => (int)MANAGER_INDEX.MAN_NPC;
|
||||
|
||||
// Static counter to track calls - resets to 0 when new instance is created (play mode starts)
|
||||
|
||||
// List of NPCs to remove. It's needed in every tick.
|
||||
// Having this as a global variable is more efficient than creating a new list every tick.
|
||||
CECNPC[] aRemove = new CECNPC[64];
|
||||
@@ -29,6 +31,187 @@ public class CECNPCMan : IMsgHandler
|
||||
|
||||
public CECNPCMan()
|
||||
{
|
||||
//
|
||||
m_NPCTab = new Dictionary<int, CECNPC>(512);
|
||||
m_UkNPCTab = new Dictionary<int, int>(32);
|
||||
m_aDisappearNPCs = new List<CECNPC>(32);
|
||||
// Reset debug counter when new instance is created (play mode starts)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates an NPC in the m_NPCTab dictionary with comprehensive logging.
|
||||
/// </summary>
|
||||
/// <param name="nid">NPC ID</param>
|
||||
/// <param name="npc">NPC object to add</param>
|
||||
/// <param name="reason">Reason for adding (for logging purposes)</param>
|
||||
/// <returns>True if added successfully, false if npc is null or invalid</returns>
|
||||
private bool AddNPCToTable(int nid, CECNPC npc, string reason = "Unknown")
|
||||
{
|
||||
string stackTrace = System.Environment.StackTrace.Split('\n')[1].Trim();
|
||||
int countBefore = m_NPCTab.Count;
|
||||
bool keyExists = m_NPCTab.ContainsKey(nid);
|
||||
CECNPC oldValue = keyExists ? m_NPCTab[nid] : null;
|
||||
|
||||
|
||||
// Validate input
|
||||
if (npc == null)
|
||||
{
|
||||
BMLogger.LogError($"[DICT_TRACE] AddNPCToTable: FAILED - npc is NULL for nid={nid}, reason={reason}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if old value exists and is different
|
||||
if (keyExists && oldValue != null)
|
||||
{
|
||||
bool oldIsDestroyed = false;
|
||||
try
|
||||
{
|
||||
oldIsDestroyed = oldValue.gameObject == null;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
oldIsDestroyed = true;
|
||||
}
|
||||
|
||||
if (oldValue != npc)
|
||||
{
|
||||
BMLogger.LogError($"[DICT_TRACE] AddNPCToTable: REPLACING existing NPC - nid={nid}, oldValue={(oldValue != null ? oldValue.name : "NULL")}, oldIsDestroyed={oldIsDestroyed}, newValue={npc.name}");
|
||||
}
|
||||
}
|
||||
|
||||
// Check new value state
|
||||
bool newIsNull = npc == null;
|
||||
bool newGameObjectIsNull = false;
|
||||
string newNPCName = "NULL";
|
||||
try
|
||||
{
|
||||
newGameObjectIsNull = npc.gameObject == null;
|
||||
newNPCName = npc.name;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
newGameObjectIsNull = true;
|
||||
BMLogger.LogError($"[DICT_TRACE] AddNPCToTable: Exception checking new npc.gameObject for nid={nid}: {ex.Message}");
|
||||
}
|
||||
|
||||
// Add to dictionary
|
||||
m_NPCTab[nid] = npc;
|
||||
int countAfter = m_NPCTab.Count;
|
||||
|
||||
// Verify the value was set correctly
|
||||
bool verifySuccess = m_NPCTab.TryGetValue(nid, out var verifyNPC);
|
||||
bool verifyIsNull = verifyNPC == null;
|
||||
bool verifyGameObjectIsNull = false;
|
||||
try
|
||||
{
|
||||
if (verifyNPC != null)
|
||||
verifyGameObjectIsNull = verifyNPC.gameObject == null;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
verifyGameObjectIsNull = true;
|
||||
BMLogger.LogError($"[DICT_TRACE] AddNPCToTable: Exception verifying npc.gameObject for nid={nid}: {ex.Message}");
|
||||
}
|
||||
|
||||
if (verifyIsNull || verifyGameObjectIsNull)
|
||||
{
|
||||
BMLogger.LogError($"[DICT_TRACE] AddNPCToTable: WARNING - Value is NULL or destroyed immediately after setting! nid={nid}, reason={reason}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an NPC from the m_NPCTab dictionary with comprehensive logging.
|
||||
/// </summary>
|
||||
/// <param name="nid">NPC ID to remove</param>
|
||||
/// <param name="reason">Reason for removal (for logging purposes)</param>
|
||||
/// <returns>True if removed successfully, false if key didn't exist</returns>
|
||||
private bool RemoveNPCFromTable(int nid, string reason = "Unknown")
|
||||
{
|
||||
string stackTrace = System.Environment.StackTrace.Split('\n')[1].Trim();
|
||||
int countBefore = m_NPCTab.Count;
|
||||
bool keyExists = m_NPCTab.ContainsKey(nid);
|
||||
CECNPC valueBeforeRemove = null;
|
||||
bool valueIsNull = false;
|
||||
bool gameObjectIsNull = false;
|
||||
string npcName = "NULL";
|
||||
|
||||
if (!keyExists)
|
||||
{
|
||||
BMLogger.LogError($"[DICT_TRACE] RemoveNPCFromTable: KEY NOT FOUND - nid={nid}, reason={reason}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check value state before removal
|
||||
valueBeforeRemove = m_NPCTab[nid];
|
||||
valueIsNull = valueBeforeRemove == null;
|
||||
|
||||
if (!valueIsNull)
|
||||
{
|
||||
try
|
||||
{
|
||||
gameObjectIsNull = valueBeforeRemove.gameObject == null;
|
||||
npcName = valueBeforeRemove.name;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
gameObjectIsNull = true;
|
||||
BMLogger.LogError($"[DICT_TRACE] RemoveNPCFromTable: Exception accessing value before remove for nid={nid}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remove from dictionary
|
||||
bool removed = m_NPCTab.Remove(nid);
|
||||
int countAfter = m_NPCTab.Count;
|
||||
|
||||
// Verify removal
|
||||
bool keyStillExists = m_NPCTab.ContainsKey(nid);
|
||||
|
||||
|
||||
if (keyStillExists)
|
||||
{
|
||||
BMLogger.LogError($"[DICT_TRACE] RemoveNPCFromTable: ERROR - Key still exists after removal! nid={nid}, reason={reason}");
|
||||
}
|
||||
|
||||
if (valueIsNull || gameObjectIsNull)
|
||||
{
|
||||
BMLogger.LogError($"[DICT_TRACE] RemoveNPCFromTable: REMOVED NULL/DESTROYED VALUE - nid={nid}, valueIsNull={valueIsNull}, gameObjectIsNull={gameObjectIsNull}, reason={reason}. This explains why key existed but value was null!");
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up destroyed objects from dictionaries. Call this when play mode starts to remove stale references.
|
||||
/// </summary>
|
||||
public void CleanupDestroyedObjects()
|
||||
{
|
||||
// Clean up destroyed NPCs from main table
|
||||
var keysToRemove = new List<int>();
|
||||
int nullCount = 0;
|
||||
foreach (var kvp in m_NPCTab)
|
||||
{
|
||||
if (kvp.Value == null)
|
||||
{
|
||||
nullCount++;
|
||||
keysToRemove.Add(kvp.Key);
|
||||
}
|
||||
else if (kvp.Value.gameObject == null)
|
||||
{
|
||||
nullCount++;
|
||||
keysToRemove.Add(kvp.Key);
|
||||
}
|
||||
}
|
||||
foreach (var key in keysToRemove)
|
||||
{
|
||||
RemoveNPCFromTable(key, "CleanupDestroyedObjects - null/destroyed entry");
|
||||
}
|
||||
|
||||
// Clean up destroyed NPCs from disappear table
|
||||
int beforeDisappearCount = m_aDisappearNPCs.Count;
|
||||
m_aDisappearNPCs.RemoveAll(npc => npc == null || npc.gameObject == null);
|
||||
}
|
||||
public bool ProcessMessage(ECMSG Msg)
|
||||
{
|
||||
@@ -94,9 +277,14 @@ public class CECNPCMan : IMsgHandler
|
||||
public void Tick()
|
||||
{
|
||||
iRemoveCnt = 0;
|
||||
|
||||
|
||||
// Tick all NPCs
|
||||
foreach (var pNPC in m_NPCTab.Values)
|
||||
{
|
||||
if (pNPC == null || pNPC.gameObject == null)
|
||||
continue; // Skip destroyed objects
|
||||
|
||||
if (pNPC.ShouldDisappear())
|
||||
{
|
||||
if (iRemoveCnt < SIZE_REMOVETAB)
|
||||
@@ -109,7 +297,13 @@ public class CECNPCMan : IMsgHandler
|
||||
}
|
||||
|
||||
for (int i = 0; i < iRemoveCnt; i++)
|
||||
NPCLeave(aRemove[i].GetNPCID());
|
||||
{
|
||||
if (aRemove[i] != null)
|
||||
{
|
||||
int nid = aRemove[i].GetNPCID();
|
||||
NPCLeave(nid);
|
||||
}
|
||||
}
|
||||
|
||||
// Tick all NPCs who are in disappear table
|
||||
iRemoveCnt = 0;
|
||||
@@ -136,7 +330,11 @@ public class CECNPCMan : IMsgHandler
|
||||
|
||||
for (int i = 0; i < iRemoveCnt; i++)
|
||||
{
|
||||
ReleaseNPC(aRemove[i]);
|
||||
if (aRemove[i] != null)
|
||||
{
|
||||
int nid = aRemove[i].GetNPCID();
|
||||
ReleaseNPC(aRemove[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Update NPCs in various ranges (Active, visible, mini-map etc.)
|
||||
@@ -148,7 +346,6 @@ public class CECNPCMan : IMsgHandler
|
||||
}
|
||||
private void OnMsgNPCDisappear(ECMSG Msg)
|
||||
{
|
||||
BMLogger.Log("HoangDev : OnMsgNPCDisappear ");
|
||||
|
||||
var pCmd = GPDataTypeHelper.FromBytes<cmd_object_disappear>((byte[])Msg.dwParam1);
|
||||
NPCDisappear(pCmd.id);
|
||||
@@ -173,13 +370,20 @@ public class CECNPCMan : IMsgHandler
|
||||
NPCLeave(nid, true, false);
|
||||
m_aDisappearNPCs.Add(pNPC);
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[NPC_REMOVAL_TRACE] NPCDisappear: NPC {nid} NOT FOUND in table");
|
||||
}
|
||||
}
|
||||
void NPCLeave(int nid, bool bUpdateMMArray = true, bool bRelease = true)
|
||||
{
|
||||
// Release NPC
|
||||
CECNPC pNPC = GetNPC(nid);
|
||||
if (!pNPC)
|
||||
{
|
||||
BMLogger.LogError($"[NPC_REMOVAL_TRACE] NPCLeave: NPC {nid} NOT FOUND in table, cannot remove");
|
||||
return;
|
||||
}
|
||||
|
||||
/*if (bUpdateMMArray)
|
||||
RemoveNPCFromMiniMap(pNPC);*/
|
||||
@@ -191,16 +395,16 @@ public class CECNPCMan : IMsgHandler
|
||||
hostplayer.SelectTarget(0);
|
||||
|
||||
// Remove it from active NPC table
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.NPCLeave: Removing NPC from m_NPCTab - nid={nid}, table size before={m_NPCTab.Count}");
|
||||
bool removed = m_NPCTab.Remove(nid);
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.NPCLeave: NPC removed={(removed ? "SUCCESS" : "FAILED (not in table)" )}, table size after={m_NPCTab.Count}");
|
||||
bool removed = RemoveNPCFromTable(nid, $"NPCLeave - bRelease={bRelease}");
|
||||
|
||||
// Forbid reloading npc's resources
|
||||
//QueueNPCUndoLoad(nid, pNPC->GetBornStamp());
|
||||
|
||||
// Release NPC resource
|
||||
if (bRelease)
|
||||
{
|
||||
ReleaseNPC(pNPC);
|
||||
}
|
||||
else
|
||||
{
|
||||
CECHostPlayer pHost = hostplayer;
|
||||
@@ -215,13 +419,21 @@ public class CECNPCMan : IMsgHandler
|
||||
{
|
||||
if (pNPC)
|
||||
{
|
||||
int nid = pNPC.GetNPCID();
|
||||
|
||||
// Remove tab-selected array
|
||||
CECHostPlayer pHost = CECGameRun.Instance.GetHostPlayer();
|
||||
if (pHost)
|
||||
pHost.RemoveObjectFromTabSels(pNPC);
|
||||
|
||||
pNPC.Release();
|
||||
|
||||
pNPC.DestroySelf();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[NPC_REMOVAL_TRACE] ReleaseNPC: pNPC is NULL, cannot release");
|
||||
}
|
||||
}
|
||||
private bool TransmitMessage(ECMSG Msg)
|
||||
@@ -323,16 +535,19 @@ public class CECNPCMan : IMsgHandler
|
||||
// if (!bDelay)
|
||||
// NPCDisappear(nid);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pNPC == null)
|
||||
BMLogger.LogError($"[NPC_REMOVAL_TRACE] OnMsgNPCDied: NPC {nid} NOT FOUND in table");
|
||||
else if (pNPC.IsAboutToDie())
|
||||
BMLogger.LogError($"[NPC_REMOVAL_TRACE] OnMsgNPCDied: NPC {nid} already about to die, skipping");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool OnMsgNPCStopMove(ECMSG msg)
|
||||
{
|
||||
cmd_object_stop_move pCmd = EC_Utility.ByteArrayToStructure<cmd_object_stop_move>((byte[])msg.dwParam1);
|
||||
if (-2041571143 == pCmd.id)
|
||||
{
|
||||
BMLogger.Log("HoangDev: OnMsgNPCStopMove NPCID: " + pCmd.id);
|
||||
}
|
||||
CECNPC pNPC = SeekOutNPC(pCmd.id);
|
||||
if (pNPC)
|
||||
pNPC.StopMoveTo(pCmd);
|
||||
@@ -377,17 +592,14 @@ public class CECNPCMan : IMsgHandler
|
||||
private bool OnMsgNPCInfo(ECMSG msg)
|
||||
{
|
||||
int commandId = Convert.ToInt32(msg.dwParam2);
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.OnMsgNPCInfo: Received NPC info message, commandID={commandId}");
|
||||
|
||||
switch (commandId)
|
||||
{
|
||||
case CommandID.NPC_INFO_LIST:
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.OnMsgNPCInfo: Processing NPC_INFO_LIST");
|
||||
// msg.dwParam1 chính là buffer chứa placeholder data (không có header cmd_npc_info_list)
|
||||
cmd_npc_info_list pCmd = MemoryMarshal.Read<cmd_npc_info_list>(((byte[])msg.dwParam1).AsSpan());
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.OnMsgNPCInfo: NPC_INFO_LIST contains {pCmd.count} NPC(s)");
|
||||
|
||||
int offset = Marshal.OffsetOf<cmd_npc_info_list>("placeholder").ToInt32();
|
||||
byte[] buffer = (byte[])msg.dwParam1;
|
||||
@@ -398,7 +610,6 @@ public class CECNPCMan : IMsgHandler
|
||||
// giống const info_npc& Info = *(const info_npc*)pDataBuf;
|
||||
info_npc info = MemoryMarshal.Read<info_npc>(pDataBuf);
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.OnMsgNPCInfo: Processing NPC {i + 1}/{pCmd.count} - nid={info.nid}, tid={info.tid}");
|
||||
|
||||
int iSize = info_npc.HEADER_SIZE;
|
||||
if ((info.state & PlayerNPCState.GP_STATE_EXTEND_PROPERTY) != 0)
|
||||
@@ -429,20 +640,16 @@ public class CECNPCMan : IMsgHandler
|
||||
|
||||
case CommandID.NPC_ENTER_SLICE:
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.OnMsgNPCInfo: Processing NPC_ENTER_SLICE");
|
||||
var buffer = (byte[])msg.dwParam1;
|
||||
info_npc info = MemoryMarshal.Read<info_npc>(buffer.AsSpan(0, info_npc.HEADER_SIZE));
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.OnMsgNPCInfo: NPC_ENTER_SLICE - nid={info.nid}, tid={info.tid}");
|
||||
NPCEnter(info, false, buffer, info_npc.HEADER_SIZE);
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandID.NPC_ENTER_WORLD:
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.OnMsgNPCInfo: Processing NPC_ENTER_WORLD");
|
||||
var buffer = (byte[])msg.dwParam1;
|
||||
info_npc info = MemoryMarshal.Read<info_npc>(buffer.AsSpan(0, info_npc.HEADER_SIZE));
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.OnMsgNPCInfo: NPC_ENTER_WORLD - nid={info.nid}, tid={info.tid}");
|
||||
NPCEnter(info, true, buffer, info_npc.HEADER_SIZE);
|
||||
break;
|
||||
}
|
||||
@@ -483,7 +690,7 @@ public class CECNPCMan : IMsgHandler
|
||||
var npc = GetNPC(Info.nid);
|
||||
if (npc != null)
|
||||
{
|
||||
m_NPCTab.Remove(Info.nid);
|
||||
RemoveNPCFromTable(Info.nid, "NPCEnter - replacing existing NPC");
|
||||
GameObject.Destroy(npc.gameObject);
|
||||
}
|
||||
|
||||
@@ -506,9 +713,7 @@ public class CECNPCMan : IMsgHandler
|
||||
}
|
||||
|
||||
// Thêm NPC vào bảng
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.NPCEnter: Adding NPC to m_NPCTab - nid={Info.nid}, tid={Info.tid}, npc={(npc != null ? "created" : "NULL")}");
|
||||
m_NPCTab[Info.nid] = npc;
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.NPCEnter: NPC added successfully. m_NPCTab[Info.nid] = npc {m_NPCTab[Info.nid].name} NPC(s)");
|
||||
AddNPCToTable(Info.nid, npc, $"NPCEnter - nid={Info.nid}, tid={Info.tid}, bBornInSight={bBornInSight}");
|
||||
return true;
|
||||
}
|
||||
// Get NPC by id and optional bornStamp
|
||||
@@ -517,6 +722,30 @@ public class CECNPCMan : IMsgHandler
|
||||
if (!m_NPCTab.TryGetValue(nid, out var npc))
|
||||
return null;
|
||||
|
||||
// Validate that the NPC object is not destroyed (Unity destroyed objects pass != null but throw on access)
|
||||
if (npc == null)
|
||||
{
|
||||
// Clean up destroyed object from dictionary
|
||||
RemoveNPCFromTable(nid, "GetNPC - null value detected");
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (npc.gameObject == null)
|
||||
{
|
||||
// Clean up destroyed object from dictionary
|
||||
RemoveNPCFromTable(nid, "GetNPC - destroyed GameObject detected");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
BMLogger.LogError($"[DICT_TRACE] GetNPC: Exception accessing npc.gameObject for nid={nid}: {ex.Message}, removing from dictionary");
|
||||
// Clean up destroyed object from dictionary
|
||||
RemoveNPCFromTable(nid, $"GetNPC - exception accessing GameObject: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return npc;
|
||||
}
|
||||
|
||||
@@ -540,26 +769,32 @@ public class CECNPCMan : IMsgHandler
|
||||
}
|
||||
public CECNPC GetNPCFromAll(int nid)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.GetNPCFromAll: Looking for NPC nid={nid}, m_NPCTab.Count={m_NPCTab.Count}");
|
||||
|
||||
// Get stack trace to see who's calling this method
|
||||
CECNPC pNPC = GetNPC(nid);
|
||||
if (pNPC != null)
|
||||
// Check for null/destroyed object BEFORE accessing properties (Unity destroyed objects pass != null but throw on access)
|
||||
if (pNPC != null && pNPC.gameObject != null)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.GetNPCFromAll: GetNPC returned {pNPC.name}");
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.GetNPCFromAll: NPC {nid} FOUND in m_NPCTab");
|
||||
return pNPC;
|
||||
}
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.GetNPCFromAll: NPC {nid} NOT FOUND in m_NPCTab! Available NPC IDs: {string.Join(", ", m_NPCTab.Keys)}");
|
||||
|
||||
// Search from disappear array - provides grace period for GFX events (matches C++ behavior)
|
||||
for (int i = 0; i < m_aDisappearNPCs.Count; i++)
|
||||
{
|
||||
CECNPC pDisappearNPC = m_aDisappearNPCs[i];
|
||||
if (pDisappearNPC != null && pDisappearNPC.gameObject != null && pDisappearNPC.GetNPCID() == nid)
|
||||
// Use Unity's == null check which properly handles destroyed objects
|
||||
if (pDisappearNPC == null) continue;
|
||||
if (pDisappearNPC.gameObject == null) continue;
|
||||
|
||||
try
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CECNPCMan.GetNPCFromAll: NPC {nid} FOUND in m_aDisappearNPCs");
|
||||
return pDisappearNPC; // Return NPC even if removed from main table
|
||||
if (pDisappearNPC.GetNPCID() == nid)
|
||||
{
|
||||
return pDisappearNPC;
|
||||
}
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// Object was destroyed between null check and access - skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,44 +227,22 @@ namespace BrewMonster
|
||||
|
||||
// Log target existence issues with more detail
|
||||
// 记录目标存在问题的更多详细信息
|
||||
if (!m_bTargetExist && m_nTargetID != 0)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Event.Tick: WARNING - Target {m_nTargetID} does not exist (host={m_nHostID}, exist={m_bHostExist}, pos={m_vHostPos}), state={prevState}. Target may have been destroyed or ID is invalid.");
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Event.Tick: host={m_nHostID} (exist={m_bHostExist}, pos={m_vHostPos}), target={m_nTargetID} (exist={m_bTargetExist}, pos={m_vTargetPos}), state={prevState}");
|
||||
}
|
||||
|
||||
base.Tick(dwDeltaTime);
|
||||
|
||||
// Log state transitions
|
||||
if (prevState != m_enumState)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Event.Tick: State transition {prevState} → {m_enumState}, host={m_nHostID}, target={m_nTargetID}");
|
||||
}
|
||||
|
||||
// Spawn fly GFX when entering Flying state / 进入飞行状态时生成飞行特效
|
||||
if (prevState == GfxSkillEventState.enumWait && m_enumState == GfxSkillEventState.enumFlying)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Event.Tick: Transitioning to Flying, calling SpawnFlyGfx()");
|
||||
|
||||
// Register for gizmo drawing BEFORE spawning (so we capture the initial position)
|
||||
// 在生成前注册用于辅助线绘制(以便捕获初始位置)
|
||||
#if UNITY_EDITOR
|
||||
Vector3 currentPos = m_pMoveMethod.GetPos();
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Event.Tick: Registering gizmo - hostPos={m_vHostPos}, targetPos={m_vTargetPos}, currentPos={currentPos}, hostExist={m_bHostExist}, targetExist={m_bTargetExist}");
|
||||
|
||||
// Only register if positions are valid (not zero)
|
||||
// 仅在位置有效(非零)时注册
|
||||
if (m_vHostPos.sqrMagnitude > 0.01f && m_vTargetPos.sqrMagnitude > 0.01f)
|
||||
{
|
||||
SkillGfxGizmoDrawer.RegisterProjectile(m_nHostID, m_nTargetID, m_vHostPos, m_vTargetPos, m_pMoveMethod.GetMode());
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Event.Tick: Gizmo registration SKIPPED - invalid positions!");
|
||||
}
|
||||
#endif
|
||||
|
||||
SpawnFlyGfx();
|
||||
@@ -302,7 +280,6 @@ namespace BrewMonster
|
||||
/// </summary>
|
||||
protected override void HitTarget(Vector3 vTarget)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] HitTarget: Entry, host={m_nHostID}, target={m_nTargetID}, pos={vTarget}");
|
||||
base.HitTarget(vTarget);
|
||||
DestroyFlyGfx();
|
||||
SpawnHitGfx(vTarget);
|
||||
@@ -320,7 +297,6 @@ namespace BrewMonster
|
||||
/// </summary>
|
||||
private void SpawnFlyGfx()
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SpawnFlyGfx: Entry, host={m_nHostID}, target={m_nTargetID}");
|
||||
|
||||
if (m_pComposer == null)
|
||||
{
|
||||
@@ -339,17 +315,8 @@ namespace BrewMonster
|
||||
Vector3 dir = m_pMoveMethod.GetMoveDir();
|
||||
Quaternion rot = dir.sqrMagnitude > 1e-4f ? Quaternion.LookRotation(dir) : Quaternion.identity;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SpawnFlyGfx: Instantiating prefab={prefab.name} at pos={pos}, dir={dir}");
|
||||
m_flyGfxInstance = GameObject.Instantiate(prefab, pos, rot);
|
||||
|
||||
if (m_flyGfxInstance != null)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SpawnFlyGfx: SUCCESS - Fly GFX spawned at {pos}, instance={m_flyGfxInstance.name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SpawnFlyGfx: FAILED - Instantiate returned NULL!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -384,7 +351,6 @@ namespace BrewMonster
|
||||
/// </summary>
|
||||
private void SpawnHitGfx(Vector3 vTarget)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SpawnHitGfx: Entry, host={m_nHostID}, target={m_nTargetID}, pos={vTarget}");
|
||||
|
||||
if (m_pComposer == null)
|
||||
{
|
||||
@@ -407,17 +373,8 @@ namespace BrewMonster
|
||||
if (dir.sqrMagnitude > 1e-6f) rot = Quaternion.LookRotation(dir);
|
||||
}
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SpawnHitGfx: Instantiating prefab={prefab.name} at pos={vTarget}");
|
||||
m_hitGfxInstance = GameObject.Instantiate(prefab, vTarget, rot);
|
||||
|
||||
if (m_hitGfxInstance != null)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SpawnHitGfx: SUCCESS - Hit GFX spawned at {vTarget}, instance={m_hitGfxInstance.name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SpawnHitGfx: FAILED - Instantiate returned NULL!");
|
||||
}
|
||||
|
||||
GameObject.Destroy(m_hitGfxInstance, 3.0f); // auto-cleanup / 自动清理
|
||||
}
|
||||
@@ -485,17 +442,14 @@ namespace BrewMonster
|
||||
{
|
||||
vPos = Vector3.zero;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: Entry - nID={nID}, isPlayerID={GPDataTypeHelper.ISPLAYERID(nID)}, isNPCID={GPDataTypeHelper.ISNPCID(nID)}, pPlayerMan={(pPlayerMan != null ? "exists" : "NULL")}, pNPCMan={(pNPCMan != null ? "exists" : "NULL")}");
|
||||
|
||||
if (GPDataTypeHelper.ISPLAYERID(nID))
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: ID {nID} is a PLAYER ID");
|
||||
CECPlayer pPlayer = pPlayerMan?.GetPlayer(nID);
|
||||
|
||||
// Check if player exists AND GameObject is not destroyed (Unity's "fake null" handling)
|
||||
if (pPlayer != null && pPlayer.gameObject != null)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: Player {nID} found, getting position");
|
||||
{
|
||||
if (bIsGoblinSkill)
|
||||
{
|
||||
@@ -562,20 +516,14 @@ namespace BrewMonster
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: Player {nID} NOT FOUND or GameObject destroyed in pPlayerMan (pPlayerMan is {(pPlayerMan != null ? "not null" : "NULL")})");
|
||||
}
|
||||
}
|
||||
else if (GPDataTypeHelper.ISNPCID(nID))
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: ID {nID} is an NPC ID");
|
||||
CECNPC pNPC = pNPCMan?.GetNPCFromAll(nID);
|
||||
|
||||
// Check if NPC exists AND GameObject is not destroyed (Unity's "fake null" handling)
|
||||
if (pNPC != null && pNPC.gameObject != null)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: NPC {nID} found, getting position");
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
@@ -614,17 +562,8 @@ namespace BrewMonster
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: NPC {nID} NOT FOUND or GameObject destroyed in pNPCMan (pNPCMan is {(pNPCMan != null ? "not null" : "NULL")})");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: ID {nID} is NEITHER a player ID nor an NPC ID! This is likely an invalid ID.");
|
||||
}
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] _get_pos_by_id: Returning FALSE for ID {nID}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -779,10 +718,9 @@ namespace BrewMonster
|
||||
private const int DEFAULT_EVENT_BUF_SIZE = 10; // 默认事件缓冲区大小 / Default event buffer size
|
||||
|
||||
public LinkedList<CECSkillGfxEvent> m_EventLst; // 活动事件列表 / Active event list
|
||||
protected LinkedList<CECSkillGfxEvent>[] m_FreeLst; // 空闲事件列表(按移动模式分类) / Free event lists (categorized by move mode)
|
||||
|
||||
protected EC_ManPlayer m_pPlayerMan; // 玩家管理器 / Player manager
|
||||
protected CECNPCMan m_pNPCMan; // NPC管理器 / NPC manager
|
||||
protected LinkedList<CECSkillGfxEvent>[] m_FreeLst;
|
||||
protected EC_ManPlayer m_pPlayerMan;
|
||||
protected CECNPCMan m_pNPCMan;
|
||||
|
||||
public SkillGfxMan(CECGameRun pGameRun)
|
||||
{
|
||||
@@ -809,10 +747,7 @@ namespace BrewMonster
|
||||
m_pNPCMan = EC_ManMessageMono.Instance?.CECNPCMan;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get empty event from pool or create new one
|
||||
/// 从池中获取空事件或创建新事件
|
||||
/// </summary>
|
||||
|
||||
public A3DSkillGfxEvent GetEmptyEvent(GfxMoveMode mode)
|
||||
{
|
||||
int modeIndex = (int)mode;
|
||||
@@ -884,11 +819,6 @@ namespace BrewMonster
|
||||
/// </summary>
|
||||
public bool Tick(uint dwDeltaTime)
|
||||
{
|
||||
if (m_EventLst.Count > 0)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] SkillGfxMan.Tick: Processing {m_EventLst.Count} active event(s), deltaTime={dwDeltaTime}ms");
|
||||
}
|
||||
|
||||
var node = m_EventLst.First;
|
||||
|
||||
while (node != null)
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace BrewMonster
|
||||
/// </summary>
|
||||
public static void RegisterProjectile(long hostID, long targetID, Vector3 startPos, Vector3 targetPos, GfxMoveMode moveMode)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Gizmo: Registering projectile host={hostID}, target={targetID}, start={startPos}, target={targetPos}, mode={moveMode}");
|
||||
|
||||
var gizmo = new GizmoData
|
||||
{
|
||||
@@ -47,7 +46,6 @@ namespace BrewMonster
|
||||
moveMode = moveMode
|
||||
};
|
||||
m_GizmoList.Add(gizmo);
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] Gizmo: Registered! Total gizmos={m_GizmoList.Count}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -794,8 +794,9 @@ public class CECNPC : CECObject
|
||||
|
||||
// No delay die, enter disappear process immediately
|
||||
if (!bDelay)
|
||||
{
|
||||
Disappear();
|
||||
|
||||
}
|
||||
|
||||
StartWork((int)WorkType.WT_NORMAL, (int)WorkID.WORK_DEAD, m_dwStates);
|
||||
|
||||
@@ -803,7 +804,6 @@ public class CECNPC : CECObject
|
||||
}
|
||||
public void Disappear()
|
||||
{
|
||||
BMLogger.Log("CECNPC::Disappear");
|
||||
FadeOut();
|
||||
m_DisappearCnt.SetCounter(1);
|
||||
PlayModelAction((int)NPCActionIndex.ACT_NPC_DISAPPEAR);
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace CSNetwork
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
BMLogger.LogError("EC_ManMessage instance is null, creating a new one. This should only happen once, if it happens more than once, there might be an issue with the lifecycle of EC_ManMessage");
|
||||
_instance = new EC_ManMessage();
|
||||
}
|
||||
return _instance;
|
||||
@@ -28,7 +29,8 @@ namespace CSNetwork
|
||||
|
||||
private static ECMSG _currentMsg;
|
||||
|
||||
private EC_ManMessage() { }
|
||||
private EC_ManMessage() {
|
||||
}
|
||||
|
||||
/// <summary>Post a message to the message queue</summary>
|
||||
public static void PostMessage(long dwMsg, int iManager, int iSubID, object p1 = null, object p2 = null, object p3 = null, object p4 = null, MsgDataBase[] pData = null)
|
||||
@@ -56,6 +58,11 @@ namespace CSNetwork
|
||||
/// <summary>Register a message handler, it should be called before the game starts</summary>
|
||||
public static void RegisterHandler(IMsgHandler handler)
|
||||
{
|
||||
if(Instance.m_MsgHandlerList.ContainsKey(handler.HandlerId))
|
||||
{
|
||||
BMLogger.LogError("Handler ID: " + handler.HandlerId + " is already registered");
|
||||
return;
|
||||
}
|
||||
Instance.m_MsgHandlerList.Add(handler.HandlerId, handler);
|
||||
}
|
||||
|
||||
@@ -81,6 +88,7 @@ namespace CSNetwork
|
||||
|
||||
public static void Dispose()
|
||||
{
|
||||
BMLogger.LogError("EC_ManMessage is being disposed, this should only happen when the game is exiting");
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,94 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using CSNetwork;
|
||||
using CSNetwork.GPDataType;
|
||||
using PerfectWorld.Scripts.Managers.BrewMonster.Managers;
|
||||
using PerfectWorld.Scripts.Managers;
|
||||
|
||||
namespace BrewMonster.Managers
|
||||
{
|
||||
[Serializable]
|
||||
public class EC_ManMessageMono : MonoBehaviour
|
||||
{
|
||||
private static EC_ManMessageMono instance;
|
||||
|
||||
public static EC_ManMessageMono Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = FindAnyObjectByType<EC_ManMessageMono>();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public EC_ManPlayer EC_ManPlayer;
|
||||
public EC_ManMatter EC_ManMatter;
|
||||
public EC_ManPlayer GetECManPlayer { get => EC_ManPlayer;}
|
||||
public EC_ManMatter GetECManMatter { get => EC_ManMatter;}
|
||||
public CECNPCMan CECNPCMan { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
//TODO: Remove later
|
||||
EC_ManPlayer = new EC_ManPlayer();
|
||||
CECNPCMan = new CECNPCMan();
|
||||
EC_ManMessage.RegisterHandler(EC_ManPlayer);
|
||||
EC_ManMessage.RegisterHandler(CECNPCMan);
|
||||
EC_ManMatter = new EC_ManMatter();
|
||||
EC_ManMessage.RegisterHandler(EC_ManMatter);
|
||||
Debug.Log($"EC_ManMessage RegisterHandlerRegisterHandlerRegisterHandler");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
EC_ManMessage.Dispose();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
EC_ManMessage.Tick();
|
||||
CECNPCMan.Tick();
|
||||
}
|
||||
|
||||
// Get object by specified ID
|
||||
// iAliveFlag: 0, both alive and dead; 1, must be alive; 2, must be dead
|
||||
public CECObject GetObject(long idObject, int iAliveFlag)
|
||||
{
|
||||
CECObject pObject = null;
|
||||
|
||||
if (GPDataTypeHelper.ISNPCID((int)idObject))
|
||||
{
|
||||
if (!(pObject = CECNPCMan.GetNPC((int)idObject)))
|
||||
return null;
|
||||
|
||||
if ((iAliveFlag == 1 && (pObject as CECNPC).IsDead()) ||
|
||||
(iAliveFlag == 2 && !(pObject as CECNPC).IsDead()))
|
||||
return null;
|
||||
}
|
||||
else if (GPDataTypeHelper.ISPLAYERID((int)idObject))
|
||||
{
|
||||
if (!(pObject = EC_ManPlayer.GetPlayer((int)idObject)))
|
||||
return null;
|
||||
|
||||
if ((iAliveFlag == 1 && (pObject as CECPlayer).IsDead()) ||
|
||||
(iAliveFlag == 2 && !(pObject as CECPlayer).IsDead()))
|
||||
return null;
|
||||
}
|
||||
else if (GPDataTypeHelper.ISMATTERID((int)idObject))
|
||||
{
|
||||
pObject = EC_ManMatter.GetMatter((int)idObject);
|
||||
}
|
||||
|
||||
return pObject;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using CSNetwork;
|
||||
using CSNetwork.GPDataType;
|
||||
using PerfectWorld.Scripts.Managers.BrewMonster.Managers;
|
||||
using PerfectWorld.Scripts.Managers;
|
||||
|
||||
namespace BrewMonster.Managers
|
||||
{
|
||||
[Serializable]
|
||||
public class EC_ManMessageMono : MonoBehaviour
|
||||
{
|
||||
private static EC_ManMessageMono instance;
|
||||
|
||||
public static EC_ManMessageMono Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = FindAnyObjectByType<EC_ManMessageMono>();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public EC_ManPlayer EC_ManPlayer;
|
||||
public EC_ManMatter EC_ManMatter;
|
||||
public EC_ManPlayer GetECManPlayer { get => EC_ManPlayer;}
|
||||
public EC_ManMatter GetECManMatter { get => EC_ManMatter;}
|
||||
public CECNPCMan CECNPCMan { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
//TODO: Remove later
|
||||
EC_ManPlayer = new EC_ManPlayer();
|
||||
CECNPCMan = new CECNPCMan();
|
||||
// Clean up any destroyed objects that might persist from previous play session
|
||||
CECNPCMan.CleanupDestroyedObjects();
|
||||
EC_ManMessage.RegisterHandler(EC_ManPlayer);
|
||||
EC_ManMessage.RegisterHandler(CECNPCMan);
|
||||
EC_ManMatter = new EC_ManMatter();
|
||||
EC_ManMessage.RegisterHandler(EC_ManMatter);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Dispose();
|
||||
EC_ManMessage.Dispose();
|
||||
}
|
||||
private void Dispose()
|
||||
{
|
||||
instance = null;
|
||||
}
|
||||
private void Update()
|
||||
{
|
||||
EC_ManMessage.Tick();
|
||||
CECNPCMan.Tick();
|
||||
}
|
||||
|
||||
// Get object by specified ID
|
||||
// iAliveFlag: 0, both alive and dead; 1, must be alive; 2, must be dead
|
||||
public CECObject GetObject(long idObject, int iAliveFlag)
|
||||
{
|
||||
CECObject pObject = null;
|
||||
|
||||
if (GPDataTypeHelper.ISNPCID((int)idObject))
|
||||
{
|
||||
if (!(pObject = CECNPCMan.GetNPC((int)idObject)))
|
||||
return null;
|
||||
|
||||
if ((iAliveFlag == 1 && (pObject as CECNPC).IsDead()) ||
|
||||
(iAliveFlag == 2 && !(pObject as CECNPC).IsDead()))
|
||||
return null;
|
||||
}
|
||||
else if (GPDataTypeHelper.ISPLAYERID((int)idObject))
|
||||
{
|
||||
if (!(pObject = EC_ManPlayer.GetPlayer((int)idObject)))
|
||||
return null;
|
||||
|
||||
if ((iAliveFlag == 1 && (pObject as CECPlayer).IsDead()) ||
|
||||
(iAliveFlag == 2 && !(pObject as CECPlayer).IsDead()))
|
||||
return null;
|
||||
}
|
||||
else if (GPDataTypeHelper.ISMATTERID((int)idObject))
|
||||
{
|
||||
pObject = EC_ManMatter.GetMatter((int)idObject);
|
||||
}
|
||||
|
||||
return pObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,9 @@ namespace BrewMonster
|
||||
|
||||
public void ShowLoadingScene(bool active)
|
||||
{
|
||||
goContent.SetActive(active);
|
||||
#if !UNITY_EDITOR
|
||||
goContent.SetActive(active);
|
||||
#endif
|
||||
}
|
||||
|
||||
async UniTaskVoid ObserveLoadingAsync(CancellationToken token)
|
||||
|
||||
@@ -4,6 +4,10 @@ namespace BrewMonster
|
||||
{
|
||||
public class ObjectSpawner : MonoSingleton<ObjectSpawner>
|
||||
{
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
}
|
||||
public GameObject InstantiateObject(GameObject prefab, Transform parent =null, bool setThisAsParent = true)
|
||||
{
|
||||
if (prefab == null)
|
||||
|
||||
@@ -78,7 +78,6 @@ namespace BrewMonster
|
||||
List<TARGET_DATA> Targets,
|
||||
bool bIsGoblinSkill = false)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] ComposerMan.Play: skill={nSkillID}, host={nHostID}, castTarget={nCastTargetID}, targets={(Targets?.Count ?? 0)}, isGoblin={bIsGoblinSkill}");
|
||||
|
||||
if (!m_ComposerMap.TryGetValue(nSkillID, out var composer) || composer == null)
|
||||
{
|
||||
@@ -86,14 +85,12 @@ namespace BrewMonster
|
||||
return;
|
||||
}
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] ComposerMan.Play: Composer FOUND, calling composer.Play()");
|
||||
|
||||
// Forward to composer (stubbed for now if Play not implemented)
|
||||
try
|
||||
{
|
||||
// Provide a minimal default fly time estimation behavior when composer has no fly time configured
|
||||
// Real effect triggering should be implemented inside composer.Play
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] ComposerMan.Play: nCastTargetID={nCastTargetID}; Targets.count={Targets.Count}");
|
||||
composer.Play(nHostID, nCastTargetID, Targets, bIsGoblinSkill);
|
||||
}
|
||||
catch (System.MissingMethodException)
|
||||
|
||||
@@ -20,39 +20,31 @@ namespace BrewMonster
|
||||
/// </summary>
|
||||
public override void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.StartMove: Entry - host={vHost}, target={vTarget}, area={m_bArea}, maxFlyTime={m_dwMaxFlyTime}");
|
||||
|
||||
if (m_bArea)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.StartMove: Area skill - calculating range and random offset");
|
||||
CalcRange((vTarget - vHost).normalized);
|
||||
m_vPos = vHost + GetRandOff();
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.StartMove: Area skill - startPos={m_vPos} (host={vHost} + offset)");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vPos = vHost;
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.StartMove: Non-area skill - startPos={m_vPos} (same as host)");
|
||||
}
|
||||
|
||||
m_vMoveDir = vTarget - m_vPos;
|
||||
float fDist = Normalize(ref m_vMoveDir);
|
||||
float fMax = _fly_speed * m_dwMaxFlyTime;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.StartMove: Distance={fDist}, maxDistance={fMax} (speed={_fly_speed} * time={m_dwMaxFlyTime}), moveDir={m_vMoveDir}");
|
||||
|
||||
if (fMax >= fDist)
|
||||
{
|
||||
m_fSpeed = _fly_speed;
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.StartMove: Using default speed={m_fSpeed} (distance fits in time)");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fSpeed = fDist / m_dwMaxFlyTime;
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.StartMove: Using calculated speed={m_fSpeed} (distance={fDist} / time={m_dwMaxFlyTime})");
|
||||
}
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.StartMove: Complete - pos={m_vPos}, dir={m_vMoveDir}, speed={m_fSpeed}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,18 +58,15 @@ namespace BrewMonster
|
||||
float fDist = Normalize(ref vFlyDir);
|
||||
float fFlyDist = m_fSpeed * dwDeltaTime;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.TickMove: Entry - oldPos={oldPos}, targetPos={vTargetPos}, deltaTime={dwDeltaTime}, speed={m_fSpeed}, distance={fDist}, flyDist={fFlyDist}");
|
||||
|
||||
if (fFlyDist >= fDist)
|
||||
{
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.TickMove: Target HIT! (flyDist={fFlyDist} >= distance={fDist})");
|
||||
return true; // target hit / 命中目标
|
||||
}
|
||||
|
||||
m_vPos += vFlyDir * fFlyDist;
|
||||
m_vMoveDir = vFlyDir;
|
||||
|
||||
BMLogger.LogError($"[SKILL_GFX_DEBUG] CGfxLinearMove.TickMove: Moved - newPos={m_vPos}, moveDir={m_vMoveDir}, distanceRemaining={fDist - fFlyDist}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+360
-9
File diff suppressed because one or more lines are too long
@@ -60,8 +60,4 @@ QualitySettings:
|
||||
excludedTargetPlatforms:
|
||||
- Standalone
|
||||
m_TextureMipmapLimitGroupNames: []
|
||||
m_PerPlatformDefaultQuality:
|
||||
Android: 0
|
||||
Standalone: 0
|
||||
VisionOS: 0
|
||||
iPhone: 0
|
||||
m_PerPlatformDefaultQuality: {}
|
||||
|
||||
Reference in New Issue
Block a user