Fix bug about elseplayer cast skill.

This commit is contained in:
Chomper9981
2026-03-16 14:46:16 +07:00
parent 6effe9c706
commit db65447df2
17 changed files with 128 additions and 109 deletions
@@ -4,6 +4,11 @@ using UnityEngine;
public static class GameConstants
{
public static int NUM_MAGICCLASS = 5;
public static int ARMOR_RUIN_SPEED = -25;
public static int WEAPON_RUIN_SPEED = -2;
public static float PLAYER_PRICE_SCALE = 1.0f;
public static int ENDURANCE_SCALE = 100;
}
public struct ROLEBASICPROP
@@ -2,10 +2,6 @@ namespace BrewMonster.Scripts
{
public class InventoryConst
{
// Equipment endurance scale
public const int ENDURANCE_SCALE = 100;
// NUM_MAGICCLASS
public const int NUM_MAGICCLASS = 5;
// Index of item in equipment inventory
public const int EQUIPIVTR_WEAPON = 0;
public const int EQUIPIVTR_HEAD = 1;
@@ -316,8 +316,8 @@ namespace BrewMonster.Network
}
public static void update_require_data(ref prerequisition require)
{
require.durability *= BrewMonster.Scripts.InventoryConst.ENDURANCE_SCALE;
require.max_durability *= BrewMonster.Scripts.InventoryConst.ENDURANCE_SCALE;
require.durability *= GameConstants.ENDURANCE_SCALE;
require.max_durability *= GameConstants.ENDURANCE_SCALE;
}
public static void set_to_classid(DATA_TYPE type, byte[] data, int major_type)
{
@@ -201,8 +201,8 @@ namespace PerfectWorld.Scripts.Managers
StrengthReq = m_pDBEssence.require_strength;
AgilityReq = m_pDBEssence.require_agility;
ReputationReq = m_pDBEssence.require_reputation;
CurEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
MaxEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
CurEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
MaxEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
}
// Get item icon file name
public override string GetIconFile()
@@ -207,8 +207,8 @@ namespace PerfectWorld.Scripts.Managers
StrengthReq = m_pDBEssence.require_strength;
AgilityReq = m_pDBEssence.require_agility;
ReputationReq = m_pDBEssence.require_reputation;
CurEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
MaxEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
CurEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
MaxEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
}
// Get item icon file name
public override string GetIconFile()
@@ -174,8 +174,8 @@ namespace PerfectWorld.Scripts.Managers
StrengthReq = 0;
AgilityReq = 0;
ReputationReq = m_pDBEssence.require_reputation;
CurEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
MaxEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
CurEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
MaxEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
}
public override string GetIconFile()
{
@@ -14,7 +14,6 @@ using System.Text.RegularExpressions;
using System.Reflection;
using BrewMonster.Scripts.Managers;
using BrewMonster.Scripts;
namespace PerfectWorld.Scripts.Managers
{
/// <summary>
@@ -161,9 +160,6 @@ namespace PerfectWorld.Scripts.Managers
// Scale Types
public const int SCALE_SELL = 1;
// Endurance Scale
public const int ENDURANCE_SCALE = 100;
#endregion
#region Public Fields
@@ -925,7 +921,7 @@ namespace PerfectWorld.Scripts.Managers
/// </summary>
public static int VisualizeEndurance(int v)
{
return (v + ENDURANCE_SCALE - 1) / ENDURANCE_SCALE;
return (v + GameConstants.ENDURANCE_SCALE - 1) / GameConstants.ENDURANCE_SCALE;
}
/// <summary>
@@ -115,8 +115,8 @@ namespace BrewMonster.Scripts.Managers
magic_damage = dr.ReadInt();
defense = dr.ReadInt();
armor = dr.ReadInt();
resistance = new int[InventoryConst.NUM_MAGICCLASS];
for (int i = 0; i < InventoryConst.NUM_MAGICCLASS; i++)
resistance = new int[GameConstants.NUM_MAGICCLASS];
for (int i = 0; i < GameConstants.NUM_MAGICCLASS; i++)
{
resistance[i] = dr.ReadInt();
}
@@ -132,13 +132,13 @@ namespace BrewMonster.Scripts.Managers
public int[] resistance;
public IVTR_ESSENCE_ARMOR(byte[] data)
{
resistance = new int[InventoryConst.NUM_MAGICCLASS];
resistance = new int[GameConstants.NUM_MAGICCLASS];
CECDataReader dr = new(data, data.Length);
defense = dr.ReadInt();
armor = dr.ReadInt();
mp_enhance = dr.ReadInt();
hp_enhance = dr.ReadInt();
for (int i = 0; i < InventoryConst.NUM_MAGICCLASS; i++)
for (int i = 0; i < GameConstants.NUM_MAGICCLASS; i++)
{
resistance[i] = dr.ReadInt();
}
@@ -231,8 +231,7 @@ namespace BrewMonster.Scripts
{
// Constants
public const int EC_MAXNOPKLEVEL = 0; // The maximum no PK level
public const float EC_TABSEL_DIST = 60.0f; // Distance of TAB selection
public const int NUM_MAGICCLASS = 5;
public const float EC_TABSEL_DIST = 60.0f;
public const int NUM_ESBYTE = (int)(ExtendState.NUM_EXTSTATE + 31) >> 5;
// Helper methods for bit manipulation (equivalent to the C++ inline functions)
@@ -2202,7 +2202,45 @@ namespace BrewMonster
}
}
}
protected void OnMsgEnchantResult(ECMSG msg)
{
// 从消息中获取cmd_enchant_result结构 // Get cmd_enchant_result structure from message
cmd_enchant_result pCmd = GPDataTypeHelper.FromBytes<cmd_enchant_result>((byte[])msg.dwParam1);
// 初始化掩码为全1 // Initialize mask to all 1s
uint mask = 0xFFFFFFFF;
// 如果目标不是主机玩家且当前不是主机玩家,则过滤会引起气泡文本的修饰符
// We should filter out these things that will cause bubble texts
CECHostPlayer pHost = EC_ManMessageMono.Instance.GetECManPlayer.GetHostPlayer();
if (pCmd.target != pHost.GetCharacterID() && !IsHostPlayer())
{
mask &= (uint)(MOD.MOD_PHYSIC_ATTACK_RUNE | MOD.MOD_MAGIC_ATTACK_RUNE |
MOD.MOD_CRITICAL_STRIKE | MOD.MOD_ENCHANT_FAILED);
}
// 获取修饰符 // Get modifier
uint dwModifier = (uint)pCmd.attack_flag;
// 获取技能类型 // Get skill type
int nDamage = -2; // 默认为-2,不会引起受伤动作 // Default to -2, will not cause wounded action
if (ElementSkill.GetType((uint)pCmd.skill) == (byte)skill_type.TYPE_ATTACK)
{
// 只有攻击技能会引起受伤动作,伤害值为-1 // Only attack skill will cause wounded action, when damage is -1
nDamage = -1;
}
else
{
// 其他技能不会引起受伤动作,伤害值设为-2 // Other skills will not cause wounded action, so we set damage to -2
nDamage = -2;
}
// 播放攻击效果 // Play attack effect
int attackTime = 0;
PlayAttackEffect(pCmd.target, pCmd.skill, pCmd.level, nDamage,
dwModifier & mask, 0, ref attackTime, pCmd.section);
}
// 判断是否在飞行 / Check if flying
public bool IsFlying()
{
+1 -1
View File
@@ -469,7 +469,7 @@ public class CECNPC : CECObject
{
return m_pNPCModelPolicy.PlayAttackAction(nAttackSpeed, attackevent);
}
void NPCTurnFaceTo(int idTarget, float dwTime = 0.3f)
public void NPCTurnFaceTo(int idTarget, float dwTime = 0.3f)
{
if (IsDirFixed())
{
@@ -1539,6 +1539,18 @@ namespace CSNetwork.GPDataType
public byte section;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_host_skill_attacked
{
public int idAttacker;
public int idSkill;
public int iDamage;
public byte cEquipment; // The equipment which is mangled, λιDzӦñɫ
public int attack_flag; //ǸùǷйŻͷŻػ
public byte speed; //ٶ speed * 50 ms
public byte section; // skill section
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_matter_info_list
{
public ushort count;
@@ -2889,5 +2901,6 @@ namespace CSNetwork.GPDataType
{
public int id;
};
}
@@ -1114,6 +1114,9 @@ namespace CSNetwork
case CommandID.HOST_SKILL_ATTACK_RESULT:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.HOST_SKILL_ATTACKED:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLATTACKED, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.CHANGE_FACE_START:
case CommandID.CHANGE_FACE_END:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CHANGEFACE, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
@@ -484,6 +484,7 @@ namespace BrewMonster
case EC_MsgDef.MSG_PM_PLAYEREQUIPDATA: OnMsgPlayerEquipData(Msg); break;
case EC_MsgDef.MSG_PM_PLAYERATKRESULT: OnMsgPlayerAtkResult(Msg); break;
case EC_MsgDef.MSG_PM_CASTSKILL: OnMsgPlayerCastSkill(Msg); break;
case EC_MsgDef.MSG_PM_ENCHANTRESULT: OnMsgEnchantResult(Msg); break;
case EC_MsgDef.MSG_PM_PLAYERDOEMOTE: OnMsgPlayerDoEmote(Msg); break;
case EC_MsgDef.MSG_PM_PLAYERSKILLRESULT: OnMsgPlayerSkillResult(Msg); break;
case EC_MsgDef.MSG_PM_PLAYERGATHER: OnMsgPlayerGather(Msg); break;
@@ -630,53 +631,15 @@ namespace BrewMonster
// Face to target
TurnFaceTo(pCmd.target_id);
// Check if this is a skill attack or melee attack
// For skill attacks, we need to get the skill ID from the current skill
int idSkill = 0;
int skillLevel = 0;
int nSection = 0;
// If we have a current skill being cast, use it for the attack effect
if (m_pCurSkill != null)
{
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);
PlayAttackEffect(pCmd.target_id, 0, 0, -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))
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));
}
}
else
{
// For skill attacks, the attack effect will use m_pCurSkill to get skill info
// The GFX system (CECAttacksMan) will handle spawning effects at hook positions
// We keep m_pCurSkill until the next cast or interruption to allow
// multiple attack results for the same skill cast (multi-hit skills)
// The skill will be cleared when:
// 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();
@@ -847,7 +810,7 @@ namespace BrewMonster
}
}
}
private async void LoadAppearGfx()
{
if (!m_pAppearGFX && m_iAppearFlag == (int)PlayerAppearFlag.APPEAR_ENTERWORLD)
+48 -5
View File
@@ -8,7 +8,7 @@ using System;
using UnityEngine;
using static BrewMonster.Scripts.CECHPWork;
using static CECPlayerWrapper;
using PerfectWorld.Scripts.Managers;
namespace BrewMonster
{
public partial class CECHostPlayer
@@ -40,10 +40,10 @@ namespace BrewMonster
if (pCmd.iDamage != 0 && (pCmd.cEquipment & 0x7f) != 0x7f)
{
/* char cEquip = (char)(pCmd.cEquipment & 0x7f);
CECIvtrEquip pEquip = (CECIvtrEquip)m_pEquipPack.GetItem(cEquip);
if (pEquip)
pEquip.AddCurEndurance(ARMOR_RUIN_SPEED);*/
char cEquip = (char)(pCmd.cEquipment & 0x7f);
EC_IvtrEquip pEquip = (EC_IvtrEquip)m_pEquipPack.GetItem(cEquip);
if (pEquip != null)
pEquip.AddCurEndurance(GameConstants.ARMOR_RUIN_SPEED);
}
// The host player is attacked, we should make an effect here
@@ -204,6 +204,49 @@ namespace BrewMonster
PlayAttackEffect(pCmd.idTarget, pCmd.idSkill, 0, pCmd.iDamage, (uint)pCmd.attack_flag,
pCmd.attack_speed * 50, ref refFake, pCmd.section);
}
void OnMsgHstSkillAttacked(ECMSG Msg)
{
cmd_host_skill_attacked pCmd = GPDataTypeHelper.FromBytes<cmd_host_skill_attacked>((byte[])Msg.dwParam1);
if (pCmd.iDamage != 0 && (pCmd.cEquipment & 0x7f) != 0x7f)
{
EC_IvtrEquip pEquip = (EC_IvtrEquip)m_pEquipPack.GetItem(pCmd.cEquipment & 0x7f);
if (pEquip != null)
pEquip.AddCurEndurance(GameConstants.ARMOR_RUIN_SPEED);
}
// The host player is attacked, we should make an effect here
if( GPDataTypeHelper.ISPLAYERID(pCmd.idAttacker) )
{
EC_ElsePlayer pAttacker = EC_ManMessageMono.Instance.EC_ManPlayer.GetElsePlayer(pCmd.idAttacker);
if (pAttacker)
{
if( !pAttacker.IsDead() )
{
// Face to target
pAttacker.TurnFaceTo(GetPlayerInfo().cid);
}
int refFake = 0;
pAttacker.PlayAttackEffect(GetCharacterID(), pCmd.idSkill, 0, pCmd.iDamage, (uint)pCmd.attack_flag, pCmd.speed * 50,ref refFake,pCmd.section);
pAttacker.EnterFightState();
}
}
else if( GPDataTypeHelper.ISNPCID(pCmd.idAttacker) )
{
CECNPC pAttacker = EC_ManMessageMono.Instance.CECNPCMan.GetNPC(pCmd.idAttacker);
if (pAttacker)
{
if( !pAttacker.IsDead())
{
// Face to target
pAttacker.NPCTurnFaceTo(GetPlayerInfo().cid);
}
pAttacker.PlayAttackEffect(GetCharacterID(), pCmd.idSkill, 0, pCmd.iDamage, (uint)pCmd.attack_flag, pCmd.speed,pCmd.section);
}
}
CECAutoPolicy.GetInstance().SendEvent_BeHurt(pCmd.idAttacker);
}
private void OnMsgPlayerCastSkill(ECMSG Msg)
{
+1 -39
View File
@@ -763,45 +763,7 @@ namespace BrewMonster
UnityGameSession.c2s_CmdGetItemInfo(Inventory_type.IVTRTYPE_PACK, pCmd.equip_idx);
}
private void OnMsgEnchantResult(ECMSG msg)
{
// 从消息中获取cmd_enchant_result结构 // Get cmd_enchant_result structure from message
cmd_enchant_result pCmd = GPDataTypeHelper.FromBytes<cmd_enchant_result>((byte[])msg.dwParam1);
// 初始化掩码为全1 // Initialize mask to all 1s
uint mask = 0xFFFFFFFF;
// 如果目标不是主机玩家且当前不是主机玩家,则过滤会引起气泡文本的修饰符
// We should filter out these things that will cause bubble texts
CECHostPlayer pHost = EC_ManMessageMono.Instance.GetECManPlayer.GetHostPlayer();
if (pCmd.target != pHost.GetCharacterID() && !IsHostPlayer())
{
mask &= (uint)(MOD.MOD_PHYSIC_ATTACK_RUNE | MOD.MOD_MAGIC_ATTACK_RUNE |
MOD.MOD_CRITICAL_STRIKE | MOD.MOD_ENCHANT_FAILED);
}
// 获取修饰符 // Get modifier
uint dwModifier = (uint)pCmd.attack_flag;
// 获取技能类型 // Get skill type
int nDamage = -2; // 默认为-2,不会引起受伤动作 // Default to -2, will not cause wounded action
if (ElementSkill.GetType((uint)pCmd.skill) == (byte)skill_type.TYPE_ATTACK)
{
// 只有攻击技能会引起受伤动作,伤害值为-1 // Only attack skill will cause wounded action, when damage is -1
nDamage = -1;
}
else
{
// 其他技能不会引起受伤动作,伤害值设为-2 // Other skills will not cause wounded action, so we set damage to -2
nDamage = -2;
}
// 播放攻击效果 // Play attack effect
int attackTime = 0;
PlayAttackEffect(pCmd.target, pCmd.skill, pCmd.level, nDamage,
dwModifier & mask, 0, ref attackTime, pCmd.section);
}
public void OnMsgHstClearTessera(ECMSG Msg)
{
+1
View File
@@ -553,6 +553,7 @@ namespace BrewMonster
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_HST_SKILLATTACKED: OnMsgHstSkillAttacked(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;