done remove buff speed gfx

This commit is contained in:
VDH
2026-03-16 16:40:04 +07:00
parent 6effe9c706
commit 69e1b36dc6
7 changed files with 256 additions and 131 deletions
@@ -50,7 +50,7 @@ ParticleSystem:
ringBufferMode: 0
ringBufferLoopRange: {x: 0, y: 1}
emitterVelocityMode: 1
looping: 1
looping: 0
prewarm: 0
playOnAwake: 1
useUnscaledTime: 0
@@ -4883,7 +4883,7 @@ ParticleSystem:
ringBufferMode: 0
ringBufferLoopRange: {x: 0, y: 1}
emitterVelocityMode: 1
looping: 1
looping: 0
prewarm: 0
playOnAwake: 1
useUnscaledTime: 0
@@ -9774,7 +9774,7 @@ ParticleSystem:
ringBufferMode: 0
ringBufferLoopRange: {x: 0, y: 1}
emitterVelocityMode: 1
looping: 1
looping: 0
prewarm: 0
playOnAwake: 1
useUnscaledTime: 0
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -146,6 +146,7 @@ namespace BrewMonster
pEvent.SetDelay(dwDelayTime);
pEvent.SetReverse(bReverse);
pEvent.SetParam(param);
BMLogger.LogError("[HoangDev] bTraceTarget=" + bTraceTarget);
pEvent.SetTraceTarget(bTraceTarget);
pEvent.SetModifier(dwModifier);
pEvent.SetIsCluster(bCluster);
@@ -503,4 +504,4 @@ namespace BrewMonster
public bool bGfxDisableCamShake;
public bool bHostECMCreatedByGfx;
};
}
}
@@ -271,7 +271,6 @@ namespace BrewMonster
if (m_enumState == GfxSkillEventState.enumHit && m_bTraceTarget)
{
UpdateHitGfxTransform();
// Check if hit GFX has been destroyed (3 second lifetime) or target is destroyed
// 检查命中特效是否已被销毁(3秒生命周期)或目标是否已销毁
if (m_hitGfxInstance == null || (!m_bTargetExist && m_nTargetID != 0))
@@ -296,7 +295,26 @@ namespace BrewMonster
protected override void HitTarget(Vector3 vTarget)
{
base.HitTarget(vTarget);
DestroyFlyGfx();
// Only destroy fly GFX if NOT tracing target
// If tracing target, fly GFX will be cleaned up when buff expires
// 只有在不跟踪目标时才销毁飞行特效
// 如果跟踪目标,飞行特效将在buff过期时清理
if (!m_bTraceTarget)
{
DestroyFlyGfx();
}
else
{
// If fly GFX exists and m_bTraceTarget is true, add to tracking list
// 如果飞行特效存在且m_bTraceTarget为true,添加到跟踪列表
if (m_flyGfxInstance != null)
{
SkillGfxMan.InstanceSub?.AddTraceTargetGfx(m_flyGfxInstance, 0); // Skill ID not available, use 0
BMLogger.Log($"[TRACE_TARGET_GFX] HitTarget: Added fly GFX to trace target list (m_bTraceTarget=true)");
}
}
SpawnHitGfx(vTarget);
// TODO Phase 2: Special hit effects (rune, critical, nullity)
@@ -333,6 +351,13 @@ namespace BrewMonster
m_flyGfxInstance = GameObject.Instantiate(prefab, pos, prefab.transform.rotation);
// If m_bTraceTarget is true, add to tracking list when spawned
// 如果m_bTraceTarget为true,在生成时添加到跟踪列表
if (m_bTraceTarget)
{
SkillGfxMan.InstanceSub?.AddTraceTargetGfx(m_flyGfxInstance, 0); // Skill ID not available, use 0
BMLogger.Log($"[TRACE_TARGET_GFX] SpawnFlyGfx: Added fly GFX to trace target list (m_bTraceTarget=true)");
}
}
/// <summary>
@@ -401,6 +426,7 @@ namespace BrewMonster
// 这与C++逻辑匹配:当投射物击中地面(无目标)时使用m_szHitGrndGfx
bool bTargetExists = m_bTargetExist && m_nTargetID != 0;
GameObject prefab = bTargetExists ? m_pComposer.GetHitGFX() : m_pComposer.GetHitGrdGFX();
BMLogger.LogError("bTargetExists : " + bTargetExists);
BMLogger.LogError("HitGfx : " + m_pComposer.hitGfxName);
if (prefab == null)
@@ -429,9 +455,21 @@ namespace BrewMonster
m_hitGfxInstance = GameObject.Instantiate(prefab, vTarget, prefab.transform.rotation);
// Destroy hit GFX after 3 seconds (unless m_bTraceTarget is true, then it follows target until destroyed)
// 3秒后销毁命中特效(除非m_bTraceTarget为true否则它会跟随目标直到被销毁)
//GameObject.Destroy(m_hitGfxInstance, 3.0f);
// If m_bTraceTarget is true, add to tracking list (don't auto-destroy)
// 如果m_bTraceTarget为true添加到跟踪列表(不自动销毁)
if (m_bTraceTarget)
{
SkillGfxMan.InstanceSub?.AddTraceTargetGfx(m_hitGfxInstance, 0); // Skill ID not available, use 0
BMLogger.Log($"[TRACE_TARGET_GFX] SpawnHitGfx: Added hit GFX to trace target list (m_bTraceTarget=true)");
}
else
{
// Destroy hit GFX after 5 seconds (unless m_bTraceTarget is true, then it follows target until destroyed)
// 5秒后销毁命中特效(除非m_bTraceTarget为true,否则它会跟随目标直到被销毁)
//HIT_GFX_MAX_TIMESPAN 5000
BMLogger.Log($"[TRACE_TARGET_GFX] SpawnHitGfx: GameObject.Destroy(m_hitGfxInstance, 5.0f);");
GameObject.Destroy(m_hitGfxInstance, 5.0f);
}
}
/// <summary>
@@ -440,10 +478,28 @@ namespace BrewMonster
/// </summary>
public new void Resume()
{
DestroyFlyGfx();
// Hit GFX is auto-destroyed by Unity's Destroy timer, don't null it here
// 命中特效由Unity的Destroy计时器自动销毁,不在此处置null
m_hitGfxInstance = null;
// Don't destroy GFX if it's in trace target list
// It will be cleaned up when buff expires
// 如果GFX在跟踪目标列表中,不要销毁它
// 它将在buff过期时清理
if (m_flyGfxInstance != null)
{
if (SkillGfxMan.InstanceSub != null && !SkillGfxMan.InstanceSub.IsTraceTargetGfx(m_flyGfxInstance))
{
DestroyFlyGfx();
}
}
if (m_hitGfxInstance != null)
{
if (SkillGfxMan.InstanceSub != null && !SkillGfxMan.InstanceSub.IsTraceTargetGfx(m_hitGfxInstance))
{
// Hit GFX is auto-destroyed by Unity's Destroy timer, don't null it here
// 命中特效由Unity的Destroy计时器自动销毁,不在此处置null
m_hitGfxInstance = null;
}
}
base.Resume();
}
@@ -878,6 +934,13 @@ namespace BrewMonster
protected EC_ManPlayer m_pPlayerMan;
protected CECNPCMan m_pNPCMan;
// Track GFX instances that have m_bTraceTarget = true
// These are typically buff-related trail effects that persist until buff expires
// 跟踪具有m_bTraceTarget = true的GFX实例
// 这些通常是持续到buff结束的buff相关轨迹效果
private List<GameObject> m_TraceTargetGfxList = new List<GameObject>();
private Dictionary<GameObject, int> m_TraceTargetGfxSkillMap = new Dictionary<GameObject, int>();
public SkillGfxMan(CECGameRun pGameRun)
{
m_EventLst = new LinkedList<CECSkillGfxEvent>();
@@ -971,6 +1034,110 @@ namespace BrewMonster
{
m_FreeLst[i].Clear();
}
// Clean up trace target GFX
// 清理跟踪目标GFX
RemoveAllTraceTargetGfx();
}
/// <summary>
/// Add a GFX instance to trace target tracking list
/// 将GFX实例添加到跟踪目标跟踪列表
/// </summary>
public void AddTraceTargetGfx(GameObject gfxInstance, int skillId)
{
if (gfxInstance == null) return;
if (!m_TraceTargetGfxList.Contains(gfxInstance))
{
m_TraceTargetGfxList.Add(gfxInstance);
m_TraceTargetGfxSkillMap[gfxInstance] = skillId;
BMLogger.Log($"[TRACE_TARGET_GFX] Added GFX for skill {skillId}, total tracked: {m_TraceTargetGfxList.Count}");
}
}
/// <summary>
/// Remove all trace target GFX (called when buff states update)
/// 移除所有跟踪目标GFX(在buff状态更新时调用)
/// </summary>
public void RemoveAllTraceTargetGfx()
{
BMLogger.Log($"[TRACE_TARGET_GFX] Removing {m_TraceTargetGfxList.Count} trace target GFX");
foreach (GameObject gfx in m_TraceTargetGfxList)
{
if (gfx != null)
{
GameObject.Destroy(gfx);
}
}
m_TraceTargetGfxList.Clear();
m_TraceTargetGfxSkillMap.Clear();
}
/// <summary>
/// Remove trace target GFX for specific skill
/// 移除特定技能的跟踪目标GFX
/// </summary>
public void RemoveTraceTargetGfxForSkill(int skillId)
{
List<GameObject> toRemove = new List<GameObject>();
foreach (var kvp in m_TraceTargetGfxSkillMap)
{
if (kvp.Value == skillId)
{
toRemove.Add(kvp.Key);
}
}
foreach (GameObject gfx in toRemove)
{
if (gfx != null)
{
GameObject.Destroy(gfx);
}
m_TraceTargetGfxList.Remove(gfx);
m_TraceTargetGfxSkillMap.Remove(gfx);
}
if (toRemove.Count > 0)
{
BMLogger.Log($"[TRACE_TARGET_GFX] Removed {toRemove.Count} GFX for skill {skillId}");
}
}
/// <summary>
/// Check if a GFX instance is tracked as trace target GFX
/// 检查GFX实例是否被跟踪为跟踪目标GFX
/// </summary>
public bool IsTraceTargetGfx(GameObject gfx)
{
return m_TraceTargetGfxList.Contains(gfx);
}
/// <summary>
/// Clean up null references (GFX destroyed elsewhere)
/// 清理空引用(在其他地方销毁的GFX)
/// </summary>
public void CleanupTraceTargetGfx()
{
m_TraceTargetGfxList.RemoveAll(gfx => gfx == null);
List<GameObject> nullKeys = new List<GameObject>();
foreach (var kvp in m_TraceTargetGfxSkillMap)
{
if (kvp.Key == null)
{
nullKeys.Add(kvp.Key);
}
}
foreach (var key in nullKeys)
{
m_TraceTargetGfxSkillMap.Remove(key);
}
}
/// <summary>
@@ -142,14 +142,22 @@ namespace PerfectWorld.Scripts.Managers
if (host != null && cid == host.GetCharacterID())
{
host.ProcessMessage(Msg);
// Call OnMsgPlayerExtState directly instead of ProcessMessage
// ProcessMessage doesn't handle MSG_PM_PLAYEREXTSTATE
// 直接调用OnMsgPlayerExtState而不是ProcessMessage
// ProcessMessage不处理MSG_PM_PLAYEREXTSTATE
host.OnMsgPlayerExtState(Msg);
}
else
{
EC_ElsePlayer elsePlayer = SeekOutElsePlayer(cid);
if (elsePlayer != null)
{
elsePlayer.ProcessMessage(Msg);
// Call OnMsgPlayerExtState directly instead of ProcessMessage
// ProcessMessage doesn't handle MSG_PM_PLAYEREXTSTATE
// 直接调用OnMsgPlayerExtState而不是ProcessMessage
// ProcessMessage不处理MSG_PM_PLAYEREXTSTATE
elsePlayer.OnMsgPlayerExtState(Msg);
}
if (Convert.ToInt32(Msg.dwParam2) == CommandID.ICON_STATE_NOTIFY && host != null && host.GetTeam() != null)
@@ -741,6 +741,15 @@ namespace BrewMonster
public virtual void SetNewExtendStates(int unknown, uint[] states, int count)
{
// TODO: Implement appearance or physics change
// Remove all trace target GFX when buff states change
// This ensures buff-related trail effects are cleaned up when buffs expire
// 当buff状态改变时移除所有跟踪目标GFX
// 这确保buff相关的轨迹效果在buff过期时被清理
if (SkillGfxMan.InstanceSub != null)
{
SkillGfxMan.InstanceSub.RemoveAllTraceTargetGfx();
}
}
public virtual void SetUpPlayer()
@@ -447,36 +447,6 @@ namespace BrewMonster
public bool ProcessMessage(ECMSG Msg)
{
// Log ALL messages for this player to debug missing attack results
// Filter out common noise messages but log important ones
int msgType = (int)Msg.dwMsg;
int param2 = Convert.ToInt32(Msg.dwParam2);
// Log skill-related messages
if (msgType == EC_MsgDef.MSG_PM_CASTSKILL || msgType == EC_MsgDef.MSG_PM_PLAYERATKRESULT)
{
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] ProcessMessage: Received message, playerID={m_PlayerInfo.cid}, " +
$"msgType={msgType}, subID={Msg.iSubID}, param2={param2}, " +
$"m_pCurSkill={(m_pCurSkill != null ? m_pCurSkill.GetSkillID().ToString() : "null")}");
}
// Log any unknown command IDs in MSG_PM_CASTSKILL to catch OBJECT_SKILL_ATTACK_RESULT
if (msgType == EC_MsgDef.MSG_PM_CASTSKILL)
{
// Check if this might be OBJECT_SKILL_ATTACK_RESULT (unknown command ID)
if (param2 != CommandID.OBJECT_CAST_SKILL &&
param2 != CommandID.OBJECT_CAST_INSTANT_SKILL &&
param2 != CommandID.OBJECT_CAST_POS_SKILL &&
param2 != CommandID.SKILL_PERFORM &&
param2 != CommandID.SKILL_INTERRUPTED &&
param2 != CommandID.PLAYER_CAST_RUNE_SKILL &&
param2 != CommandID.PLAYER_CAST_RUNE_INSTANT_SKILL)
{
BMLogger.LogWarning($"[ELSEPLAYER_SKILL_FLOW] ProcessMessage: Unknown commandID={param2} in MSG_PM_CASTSKILL! " +
$"This might be OBJECT_SKILL_ATTACK_RESULT - we need to handle it!");
}
}
switch (Msg.dwMsg)
{
case EC_MsgDef.MSG_PM_PLAYERFLY: OnMsgPlayerFly(Msg); break;
@@ -510,30 +480,21 @@ namespace BrewMonster
{
cmd_object_skill_attack_result pCmd = GPDataTypeHelper.FromBytes<cmd_object_skill_attack_result>((byte[])Msg.dwParam1);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerSkillResult: Entry, attackerID={pCmd.attacker_id}, targetID={pCmd.target_id}, " +
$"skillID={pCmd.skill_id}, damage={pCmd.damage}, speed={pCmd.speed}, attack_flag={pCmd.attack_flag}, section={pCmd.section}");
// Face to target
TurnFaceTo(pCmd.target_id);
// Call PlayAttackEffect with skill_id directly from command (like C++ does)
// Unlike OnMsgPlayerAtkResult, we get skill_id from the command structure, not from m_pCurSkill
int attackTime = int.MinValue;
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerSkillResult: Calling PlayAttackEffect: target={pCmd.target_id}, skillID={pCmd.skill_id}, " +
$"skillLevel=0, damage={pCmd.damage}, attack_flag={pCmd.attack_flag}, speed={pCmd.speed * 50}, section={pCmd.section}");
PlayAttackEffect(pCmd.target_id, pCmd.skill_id, 0, -1,
(uint)pCmd.attack_flag, pCmd.speed * 50, ref attackTime, pCmd.section);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerSkillResult: PlayAttackEffect complete, attackTime={attackTime}");
// Check skill type and enter fight state if needed (matching C++ logic)
byte skillType = ElementSkill.GetType((uint)pCmd.skill_id);
if (skillType == (byte)skill_type.TYPE_ATTACK || skillType == (byte)skill_type.TYPE_CURSE)
{
EnterFightState();
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerSkillResult: Entered fight state (skillType={skillType})");
}
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerSkillResult: Complete");
}
public void HandleRevive(short sReviveType, A3DVECTOR3 pos)
@@ -624,9 +585,6 @@ namespace BrewMonster
cmd_object_atk_result pCmd = GPDataTypeHelper.FromBytes<cmd_object_atk_result>((byte[])Msg.dwParam1);
//ASSERT(pCmd && pCmd.attacker_id == m_PlayerInfo.cid);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerAtkResult: Entry, attackerID={pCmd.attacker_id}, targetID={pCmd.target_id}, " +
$"damage={pCmd.damage}, speed={pCmd.speed}, attack_flag={pCmd.attack_flag}, m_pCurSkill={(m_pCurSkill != null ? m_pCurSkill.GetSkillID().ToString() : "null")}");
// Face to target
TurnFaceTo(pCmd.target_id);
@@ -641,24 +599,15 @@ namespace BrewMonster
{
idSkill = m_pCurSkill.GetSkillID();
skillLevel = m_pCurSkill.GetSkillLevel();
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerAtkResult: Skill attack detected, skillID={idSkill}, skillLevel={skillLevel}");
}
else
{
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerAtkResult: Melee attack (m_pCurSkill is null)");
}
// TO DO: fix later
int attackTime = int.MinValue;
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] Calling PlayAttackEffect: target={pCmd.target_id}, skillID={idSkill}, skillLevel={skillLevel}, " +
$"damage={pCmd.damage}, attack_flag={pCmd.attack_flag}, speed={pCmd.speed * 50}");
PlayAttackEffect(pCmd.target_id, idSkill, skillLevel, -1, (uint)pCmd.attack_flag, pCmd.speed * 50, ref attackTime);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] PlayAttackEffect: Complete, attackTime={attackTime}");
// Only start melee work if this is a melee attack (idSkill == 0)
if (idSkill == 0)
{
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerAtkResult: Starting melee work for target={pCmd.target_id}");
if (!m_pEPWorkMan.FindWork(CECEPWorkMan.Work_type.WT_NORMAL, CECEPWork.EP_work_ID.WORK_HACKOBJECT))
{
m_pEPWorkMan.StartNormalWork(new CECEPWorkMelee(m_pEPWorkMan, pCmd.target_id));
@@ -674,13 +623,10 @@ namespace BrewMonster
// 1. A new skill is cast (replaced in OnMsgPlayerCastSkill)
// 2. Skill is interrupted (SKILL_INTERRUPTED message)
// 3. Player stops casting (handled by server messages)
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerAtkResult: Skill attack - GFX should be triggered via CECAttacksMan, " +
$"keeping m_pCurSkill={(m_pCurSkill != null ? m_pCurSkill.GetSkillID().ToString() : "null")}");
}
// Enter fight state
EnterFightState();
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerAtkResult: Complete");
}
/// <summary>
@@ -699,7 +645,6 @@ namespace BrewMonster
private void OnMsgPlayerCastSkill(ECMSG Msg)
{
int commandID = Convert.ToInt32(Msg.dwParam2);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerCastSkill: Entry, playerID={m_PlayerInfo.cid}, commandID={commandID}");
switch (commandID)
{
@@ -712,8 +657,6 @@ namespace BrewMonster
// For else players, we mainly need the skill ID for animation purposes
int skillID = pCmd.skill;
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OBJECT_CAST_SKILL: playerID={m_PlayerInfo.cid}, skillID={skillID}, target={pCmd.target}, time={pCmd.time}");
// Store current skill target
m_idCurSkillTarget = pCmd.target;
@@ -721,9 +664,7 @@ namespace BrewMonster
TurnFaceTo(pCmd.target);
// Play skill cast animation
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] Calling PlaySkillCastAction: skillID={skillID}");
bool castActionResult = PlaySkillCastAction(skillID);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] PlaySkillCastAction result: {castActionResult}");
PlaySkillCastAction(skillID);
// Create a temporary skill object for tracking (if needed)
// Note: Else players don't maintain skill lists like host player does
@@ -732,17 +673,11 @@ namespace BrewMonster
{
// Create a temporary skill object with level 1 (we don't know the actual level)
m_pCurSkill = new CECSkill(skillID, 1);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] Created new CECSkill: skillID={skillID}, level=1");
}
else
{
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] Reusing existing m_pCurSkill: skillID={m_pCurSkill.GetSkillID()}");
}
// Enter fight state
EnterFightState();
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OBJECT_CAST_SKILL: Complete, m_pCurSkill={(m_pCurSkill != null ? m_pCurSkill.GetSkillID().ToString() : "null")}, m_idCurSkillTarget={m_idCurSkillTarget}");
break;
}
case CommandID.OBJECT_CAST_INSTANT_SKILL:
@@ -751,18 +686,15 @@ namespace BrewMonster
GPDataTypeHelper.FromBytes<cmd_object_cast_instant_skill>((byte[])Msg.dwParam1);
int skillID = pCmd.skill;
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OBJECT_CAST_INSTANT_SKILL: playerID={m_PlayerInfo.cid}, skillID={skillID}, target={pCmd.target}");
m_idCurSkillTarget = pCmd.target;
TurnFaceTo(pCmd.target);
bool instantResult = PlaySkillCastAction(skillID);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] PlaySkillCastAction (instant) result: {instantResult}");
PlaySkillCastAction(skillID);
if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
{
m_pCurSkill = new CECSkill(skillID, 1);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] Created new CECSkill (instant): skillID={skillID}");
}
EnterFightState();
@@ -774,17 +706,14 @@ namespace BrewMonster
GPDataTypeHelper.FromBytes<cmd_object_cast_pos_skill>((byte[])Msg.dwParam1);
int skillID = pCmd.skill;
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] OBJECT_CAST_POS_SKILL: playerID={m_PlayerInfo.cid}, skillID={skillID}, pos=({pCmd.pos.x:F2}, {pCmd.pos.y:F2}, {pCmd.pos.z:F2})");
// For position-based skills, target is the position, not an object
// We still play the cast animation
bool posResult = PlaySkillCastAction(skillID);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] PlaySkillCastAction (pos) result: {posResult}");
PlaySkillCastAction(skillID);
if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
{
m_pCurSkill = new CECSkill(skillID, 1);
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] Created new CECSkill (pos): skillID={skillID}");
}
EnterFightState();
@@ -796,26 +725,10 @@ namespace BrewMonster
// For else players, we keep m_pCurSkill until attack result is received
// This allows PlayAttackEffect to use the skill information
// Durative skills (channeling) will continue until interrupted
int performSkillID = m_pCurSkill != null ? m_pCurSkill.GetSkillID() : 0;
bool isDurative = m_pCurSkill != null && m_pCurSkill.IsDurative();
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] SKILL_PERFORM: playerID={m_PlayerInfo.cid}, skillID={performSkillID}, isDurative={isDurative}, " +
$"m_idCurSkillTarget={m_idCurSkillTarget}");
if (m_pCurSkill != null && m_pCurSkill.IsDurative())
{
// For durative skills, we keep the skill active
// It will be cleared when SKILL_INTERRUPTED is received
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] SKILL_PERFORM: Durative skill, keeping m_pCurSkill active");
}
else if (m_pCurSkill != null && m_idCurSkillTarget != 0)
{
// For non-durative skills with a target, if server doesn't send attack result,
// we might need to trigger GFX directly from SKILL_PERFORM
// But normally attack result should come through OnMsgPlayerAtkResult
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] SKILL_PERFORM: Non-durative skill with target - " +
$"Waiting for attack result message. If no attack result arrives, GFX will not spawn!");
BMLogger.LogWarning($"[ELSEPLAYER_SKILL_FLOW] SKILL_PERFORM: WARNING - If you don't see OnMsgPlayerAtkResult logs after this, " +
$"the server is not sending attack results for else players' skills!");
}
break;
}
@@ -825,24 +738,16 @@ namespace BrewMonster
cmd_skill_interrupted pCmd =
GPDataTypeHelper.FromBytes<cmd_skill_interrupted>((byte[])Msg.dwParam1);
int interruptedSkillID = m_pCurSkill != null ? m_pCurSkill.GetSkillID() : 0;
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] SKILL_INTERRUPTED: playerID={m_PlayerInfo.cid}, skillID={interruptedSkillID}, caster={pCmd.caster}");
if (m_pCurSkill != null)
{
StopSkillCastAction();
m_pCurSkill = null;
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] SKILL_INTERRUPTED: Cleared m_pCurSkill and stopped cast action");
}
m_idCurSkillTarget = 0;
break;
}
default:
{
BMLogger.LogWarning($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerCastSkill: Unknown commandID={commandID} - " +
$"This might be OBJECT_SKILL_ATTACK_RESULT or another skill-related message we need to handle!");
BMLogger.LogWarning($"[ELSEPLAYER_SKILL_FLOW] OnMsgPlayerCastSkill: Unknown command - " +
$"If this is OBJECT_SKILL_ATTACK_RESULT, we need to add a handler for it to trigger GFX!");
break;
}
}
File diff suppressed because one or more lines are too long