838 lines
29 KiB
C#
838 lines
29 KiB
C#
using BrewMonster;
|
|
using BrewMonster.Assets.PerfectWorld.Scripts.Common;
|
|
using BrewMonster.Managers;
|
|
using CSNetwork.GPDataType;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
public class CECAttacksMan : Singleton<CECAttacksMan>
|
|
{
|
|
private readonly LinkedList<CECAttackEvent> m_AttackLinkedList = new LinkedList<CECAttackEvent>();
|
|
private CECMultiSectionSkillMan m_pMultiSkillGfxComposerMan;
|
|
|
|
#if UNITY_EDITOR
|
|
public List<CECAttackEvent> m_AttackList = new List<CECAttackEvent>();
|
|
#endif
|
|
|
|
private void Update()
|
|
{
|
|
#if UNITY_EDITOR
|
|
m_AttackList = m_AttackLinkedList.ToList();
|
|
#endif
|
|
var node = m_AttackLinkedList.First;
|
|
while (node != null)
|
|
{
|
|
var next = node.Next;
|
|
//BMLogger.LogError("HoangDev: Update CECAttackEvent node.Value.m_bFinished: " + node.Value.m_bFinished);
|
|
if (node.Value.m_bFinished)
|
|
m_AttackLinkedList.Remove(node);
|
|
else node.Value.Tick((uint)(Time.deltaTime * 1000));
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
public CECAttackerEvents FindAttackByAttacker(int idHost)
|
|
{
|
|
CECAttackerEvents result = new CECAttackerEvents();
|
|
//BMLogger.LogError("HoangDev: FindAttackByAttacker idHost: " + m_AttackLinkedList.Count);
|
|
foreach (var attack in m_AttackLinkedList)
|
|
{
|
|
if (attack.m_idHost == idHost)
|
|
{
|
|
result.Add(attack);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public CECAttackEvent AddMeleeAttack(int idHost, int idTarget, int idWeapon, uint dwModifier, int nDamage, int nTimeFly = 10)
|
|
{
|
|
var newEvent = new CECAttackEvent(
|
|
this,
|
|
idHost,
|
|
0, // idCastTarget
|
|
idTarget,
|
|
idWeapon,
|
|
0, // idSkill
|
|
0, // nSkillLevel
|
|
dwModifier,
|
|
nDamage,
|
|
200, // timeToBeFired
|
|
nTimeFly // timeToDoDamage
|
|
);
|
|
m_AttackLinkedList.AddLast(newEvent);
|
|
|
|
newEvent.UpdateTargetFlag();
|
|
return m_AttackLinkedList.Last.Value;
|
|
}
|
|
public bool GetSkillSectionActionSuffix(int skill, int section, out string suffix)
|
|
{
|
|
// TODO: Implement multi-section skill logic
|
|
// 待实现:多段技能逻辑
|
|
if (m_pMultiSkillGfxComposerMan != null)
|
|
{
|
|
CECMultiSectionSkillMan::SectionInfo info = m_pMultiSkillGfxComposerMan.GetSecionInfo(skill, section);
|
|
if (info && info->action_suffix != "0") // 0 表示技能没有后缀
|
|
{
|
|
suffix = info->action_suffix;
|
|
return true;
|
|
}
|
|
}
|
|
suffix = null;
|
|
return false;
|
|
}
|
|
public CECAttackEvent AddSkillAttack(int idHost, int idCastTarget, int idTarget, int idWeapon, int idSkill, int nSkillLevel, uint dwModifier, int nDamage)
|
|
{
|
|
var newEvent = new CECAttackEvent(
|
|
this,
|
|
idHost,
|
|
idCastTarget,
|
|
idTarget,
|
|
idWeapon,
|
|
idSkill,
|
|
nSkillLevel,
|
|
dwModifier,
|
|
nDamage,
|
|
200, // timeToBeFired
|
|
1000 // timeToDoDamage
|
|
);
|
|
m_AttackLinkedList.AddLast(newEvent);
|
|
|
|
newEvent.UpdateTargetFlag();
|
|
return m_AttackLinkedList.Last.Value;
|
|
}
|
|
|
|
// === thêm tạm để code có thể compile ===
|
|
public void AddAttack(CECAttackEvent evt)
|
|
{
|
|
m_AttackLinkedList.AddLast(evt);
|
|
}
|
|
public class TARGET_DATA
|
|
{
|
|
public int idTarget;
|
|
public uint dwModifier;
|
|
public int nDamage;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Manager for multi-section skills
|
|
/// 多段技能管理器
|
|
/// </summary>
|
|
public class CECMultiSectionSkillMan
|
|
{
|
|
/// <summary>
|
|
/// Section information for multi-section skills
|
|
/// 多段技能的段信息
|
|
/// </summary>
|
|
[Serializable]
|
|
public class SectionInfo
|
|
{
|
|
public int skill_id; // 技能ID / Skill ID
|
|
public byte section; // 段号 / Section number
|
|
public string action_suffix; // 动作后缀 / Action suffix
|
|
public A3DSkillGfxComposer pComposer; // 技能特效组合器 / Skill GFX composer
|
|
|
|
public SectionInfo()
|
|
{
|
|
skill_id = 0;
|
|
section = 1;
|
|
action_suffix = string.Empty;
|
|
pComposer = null;
|
|
}
|
|
}
|
|
|
|
// 用于多段技能的sgc映射 / Map for multi-section skill SGC files
|
|
private readonly Dictionary<string, A3DSkillGfxComposer> m_SgcName2ComposerMap = new Dictionary<string, A3DSkillGfxComposer>();
|
|
|
|
// 多段技能组合器列表 / Multi-section skill composer list
|
|
private readonly List<SectionInfo> m_MultiSectionSkillComposerVec = new List<SectionInfo>();
|
|
|
|
public CECMultiSectionSkillMan()
|
|
{
|
|
}
|
|
|
|
~CECMultiSectionSkillMan()
|
|
{
|
|
Release();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release resources
|
|
/// 释放资源
|
|
/// </summary>
|
|
public void Release()
|
|
{
|
|
m_MultiSectionSkillComposerVec.Clear();
|
|
|
|
foreach (var kvp in m_SgcName2ComposerMap)
|
|
{
|
|
if (kvp.Value != null)
|
|
{
|
|
// TODO: Implement proper disposal if A3DSkillGfxComposer has cleanup
|
|
// kvp.Value.Release();
|
|
}
|
|
}
|
|
|
|
m_SgcName2ComposerMap.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load configuration from file
|
|
/// 从文件加载配置
|
|
/// </summary>
|
|
/// <param name="szFile">Configuration file path / 配置文件路径</param>
|
|
/// <returns>Success / 是否成功</returns>
|
|
public bool LoadConfig(string szFile)
|
|
{
|
|
// TODO: Implement file loading using Unity's file system
|
|
// This would require porting AScriptFile functionality or using Unity's TextAsset
|
|
BMLogger.LogWarning($"CECMultiSectionSkillMan.LoadConfig: Not yet implemented for {szFile}");
|
|
|
|
/*
|
|
// Original C++ logic:
|
|
// 1. Open script file
|
|
// 2. Parse skill ID followed by { }
|
|
// 3. Inside braces, parse comma-separated values: section,suffix,sgc
|
|
// 4. Load or reuse A3DSkillGfxComposer for each SGC file
|
|
// 5. Store SectionInfo in vector
|
|
|
|
// Example file format:
|
|
// skill_id
|
|
// {
|
|
// section,suffix,sgc_file
|
|
// section,suffix,sgc_file
|
|
// }
|
|
*/
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Play multi-section skill effect
|
|
/// 播放多段技能特效
|
|
/// </summary>
|
|
/// <param name="nSkillID">Skill ID / 技能ID</param>
|
|
/// <param name="section">Skill section / 技能段数</param>
|
|
/// <param name="nHostID">Host character ID / 施法者ID</param>
|
|
/// <param name="nCastTargetID">Cast target ID / 施法目标ID</param>
|
|
/// <param name="Targets">Target data list / 目标数据列表</param>
|
|
/// <param name="bIsGoblinSkill">Is goblin skill / 是否为精灵技能</param>
|
|
public void Play(int nSkillID, int section, int nHostID, int nCastTargetID,
|
|
List<TARGET_DATA> Targets, bool bIsGoblinSkill = false)
|
|
{
|
|
foreach (var info in m_MultiSectionSkillComposerVec)
|
|
{
|
|
if (nSkillID == info.skill_id && section == info.section && info.pComposer != null)
|
|
{
|
|
// TODO: Implement A3DSkillGfxComposer.Play method
|
|
// info.pComposer.Play(nHostID, nCastTargetID, Targets, bIsGoblinSkill);
|
|
BMLogger.Log($"CECMultiSectionSkillMan.Play: Skill {nSkillID}, Section {section}");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get skill GFX composer for specific skill and section
|
|
/// 获取指定技能和段数的特效组合器
|
|
/// </summary>
|
|
/// <param name="skill">Skill ID / 技能ID</param>
|
|
/// <param name="section">Section number / 段号</param>
|
|
/// <returns>Skill GFX composer or null / 技能特效组合器或null</returns>
|
|
public A3DSkillGfxComposer GetSkillGfxComposer(int skill, int section)
|
|
{
|
|
foreach (var info in m_MultiSectionSkillComposerVec)
|
|
{
|
|
if (skill == info.skill_id && section == info.section && info.pComposer != null)
|
|
{
|
|
return info.pComposer;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get section information for specific skill and section
|
|
/// 获取指定技能和段数的段信息
|
|
/// </summary>
|
|
/// <param name="skill">Skill ID / 技能ID</param>
|
|
/// <param name="section">Section number / 段号</param>
|
|
/// <returns>Section info or null / 段信息或null</returns>
|
|
public SectionInfo GetSecionInfo(int skill, int section)
|
|
{
|
|
foreach (var info in m_MultiSectionSkillComposerVec)
|
|
{
|
|
if (skill == info.skill_id && section == info.section)
|
|
{
|
|
return info;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Placeholder class for A3DSkillGfxComposer
|
|
/// A3DSkillGfxComposer的占位类
|
|
/// </summary>
|
|
public class A3DSkillGfxComposer
|
|
{
|
|
public uint m_dwFlyTime; // 飞行时间 / Fly time in milliseconds
|
|
|
|
public A3DSkillGfxComposer()
|
|
{
|
|
m_dwFlyTime = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load composer from file
|
|
/// 从文件加载组合器
|
|
/// </summary>
|
|
public bool Load(string sgcFile)
|
|
{
|
|
// TODO: Implement SGC file loading
|
|
BMLogger.LogWarning($"A3DSkillGfxComposer.Load: Not yet implemented for {sgcFile}");
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize composer
|
|
/// 初始化组合器
|
|
/// </summary>
|
|
public void Init(object pSkillGfxMan)
|
|
{
|
|
// TODO: Implement initialization
|
|
}
|
|
|
|
/// <summary>
|
|
/// Play skill effect
|
|
/// 播放技能特效
|
|
/// </summary>
|
|
/* public void Play(int nHostID, int nCastTargetID, List<TARGET_DATA> targets, bool bIsGoblinSkill = false)
|
|
{
|
|
// TODO: Implement skill effect playback
|
|
BMLogger.Log($"A3DSkillGfxComposer.Play: Host {nHostID}, Target {nCastTargetID}");
|
|
}*/
|
|
}
|
|
[Serializable]
|
|
public class CECAttackEvent
|
|
{
|
|
public CECAttacksMan? m_pManager;
|
|
|
|
public bool m_bSignaled;
|
|
public bool m_bDoFired;
|
|
public bool m_bDoDamaged;
|
|
public bool m_bFinished;
|
|
|
|
public uint m_timeLived;
|
|
public uint m_timeToBeFired;
|
|
public uint m_timeToDoDamage;
|
|
|
|
public int m_idHost;
|
|
public int m_idCastTarget;
|
|
public List<TARGET_DATA> m_targets = new List<TARGET_DATA>();
|
|
|
|
public int m_idWeapon;
|
|
public int m_idSkill;
|
|
public int m_nSkillLevel;
|
|
public int m_nSkillSection;
|
|
|
|
public CECAttackEvent() { }
|
|
|
|
public CECAttackEvent(CECAttacksMan? pManager, int idHost, int idCastTarget, int idTarget,
|
|
int idWeapon, int idSkill, int nSkillLevel, uint dwModifier,
|
|
int nDamage, int nTimeToBeFired, int nTimeToDoDamage)
|
|
{
|
|
m_pManager = pManager;
|
|
m_idHost = idHost;
|
|
m_idCastTarget = idCastTarget;
|
|
m_idWeapon = idWeapon;
|
|
m_idSkill = idSkill;
|
|
m_nSkillLevel = nSkillLevel;
|
|
m_timeToBeFired = (uint)nTimeToBeFired;
|
|
m_timeToDoDamage = (uint)nTimeToDoDamage;
|
|
m_bFinished = false;
|
|
|
|
AddTarget(idTarget, dwModifier, nDamage);
|
|
}
|
|
public bool Tick(uint dwDeltaTime)
|
|
{
|
|
m_timeLived += dwDeltaTime;
|
|
|
|
if (!m_bSignaled)
|
|
{
|
|
if (m_timeLived > 3500)
|
|
{
|
|
// too long time, this event will be deleted now
|
|
//BMLogger.LogError("HoangDev: m_bSignaled: " + m_bSignaled);
|
|
|
|
m_bFinished = true;
|
|
//DoFire();
|
|
DoDamage();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (m_timeToBeFired != 0)
|
|
{
|
|
if (m_timeToBeFired <= dwDeltaTime)
|
|
{
|
|
m_timeToBeFired = 0;
|
|
|
|
// Fire here
|
|
DoFire();
|
|
}
|
|
else
|
|
m_timeToBeFired -= dwDeltaTime;
|
|
}
|
|
else if (m_timeToDoDamage != 0)
|
|
{
|
|
//BMLogger.LogError($"HoangDev: m_timeToDoDamage:{m_timeToDoDamage} , dwDeltaTime: {dwDeltaTime}");
|
|
if (m_timeToDoDamage <= dwDeltaTime)
|
|
{
|
|
m_timeToDoDamage = 0;
|
|
|
|
// Do damage here
|
|
DoDamage();
|
|
}
|
|
else
|
|
m_timeToDoDamage -= dwDeltaTime;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
public void SetSkillSection(int nSection) { m_nSkillSection = nSection; }
|
|
bool DoFire()
|
|
{
|
|
float vFlyScale = 1.0f;
|
|
float vHitScale = 1.0f;
|
|
|
|
m_bDoFired = true;
|
|
|
|
if (GPDataTypeHelper.ISPLAYERID(m_idHost))
|
|
{
|
|
if (m_idSkill != 0)
|
|
{
|
|
// const A3DSkillGfxComposer* pComposer = NULL;
|
|
//
|
|
// // we use skill composed gfx to present the skill effect
|
|
// if (m_nSkillSection>0) // ¶à¶Î¼¼ÄÜ
|
|
// {
|
|
// CECMultiSectionSkillMan pMan = m_pManager.GetMultiSkillGfxComposerMan();
|
|
// if(pMan)
|
|
// {
|
|
// pMan->Play(m_idSkill, m_nSkillSection,m_idHost, m_idCastTarget, m_targets,GNET::ElementSkill::IsGoblinSkill(m_idSkill));
|
|
// pComposer = pMan->GetSkillGfxComposer(m_idSkill, m_nSkillSection);
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// if(GNET::ElementSkill::IsGoblinSkill(m_idSkill))
|
|
// m_pManager->GetSkillGfxComposerMan()->Play(m_idSkill, m_idHost, m_idCastTarget, m_targets, true);
|
|
// else
|
|
// m_pManager->GetSkillGfxComposerMan()->Play(m_idSkill, m_idHost, m_idCastTarget, m_targets);
|
|
// pComposer = m_pManager->GetSkillGfxComposerMan()->GetSkillGfxComposer(m_idSkill);
|
|
// }
|
|
//
|
|
// if (pComposer && pComposer->m_dwFlyTime == 0) // ¼¼ÄÜgfxûÓзÉÐÐʵ¼Ê£¬ÔòÂíÉÏÍ·¶¥Ã°×Ö
|
|
// {
|
|
// m_timeToDoDamage = 1;
|
|
// }
|
|
// else
|
|
// {
|
|
// A3DVECTOR3 vecHost, vecTarget;
|
|
// // now we estimated a time to do damage
|
|
// if( m_targets.size() && _get_pos_by_id(m_idHost, vecHost) && _get_pos_by_id(m_targets[0].idTarget, vecTarget) )
|
|
// {
|
|
// m_timeToDoDamage = int(Magnitude(vecHost - vecTarget) / 20.0f * 1000.0f);
|
|
// a_ClampFloor(m_timeToDoDamage, 10ul);
|
|
// }
|
|
// }
|
|
}
|
|
else if (m_idWeapon != 0)
|
|
{
|
|
// first determine gfx used
|
|
// const char * szFlyGFX = NULL;
|
|
// const char * szHitGFX = NULL;
|
|
//
|
|
// // using weapon gfx
|
|
// DATA_TYPE dt;
|
|
// const void * pData = g_pGame->GetElementDataMan()->get_data_ptr(
|
|
// m_idWeapon, ID_SPACE_ESSENCE, dt);
|
|
// ASSERT(dt == DT_WEAPON_ESSENCE || dt == DT_PROJECTILE_ESSENCE);
|
|
//
|
|
// if( dt == DT_PROJECTILE_ESSENCE )
|
|
// {
|
|
// // Ô¶³ÌÎäÆ÷£¬Ê¹Óõ¯Ò©µÄЧ¹û
|
|
// PROJECTILE_ESSENCE * pProjectile = (PROJECTILE_ESSENCE *) pData;
|
|
//
|
|
// szFlyGFX = pProjectile->file_firegfx + 4; // skip gfx/
|
|
// szHitGFX = pProjectile->file_hitgfx + 4; // skip gfx/
|
|
// }
|
|
// else if (dt == DT_WEAPON_ESSENCE)
|
|
// {
|
|
// // ½ü³ÌÎïÀí¹¥»÷£¬Ê¹ÓÃÎäÆ÷µÄЧ¹û
|
|
// WEAPON_ESSENCE * pWeapon = (WEAPON_ESSENCE *) pData;
|
|
// WEAPON_SUB_TYPE * pWeaponType = (WEAPON_SUB_TYPE*) g_pGame->GetElementDataMan()->get_data_ptr(pWeapon->id_sub_type, ID_SPACE_ESSENCE, dt);
|
|
// ASSERT(dt == DT_WEAPON_SUB_TYPE);
|
|
// szFlyGFX = NULL;
|
|
// szHitGFX = pWeaponType->file_hitgfx + 4; // skip gfx/
|
|
// }
|
|
//
|
|
// bool bHideFlyGfx = !CECOptimize::Instance().GetGFX().CanShowFly(m_idHost);
|
|
// bool bHideHitGfx = !CECOptimize::Instance().GetGFX().CanShowHit(m_idHost);
|
|
// int nNumTargets = m_targets.size();
|
|
// for(int i=0; i<nNumTargets; i++)
|
|
// {
|
|
// const TARGET_DATA& data = m_targets[i];
|
|
//
|
|
// const char * pszFlyGFX = szFlyGFX;
|
|
// const char * pszHitGFX = szHitGFX;
|
|
//
|
|
// if( data.dwModifier & MOD_NULLITY )
|
|
// pszHitGFX = "³ÌÐòÁªÈë\\»÷ÖÐ\\ÎÞЧ¹¥»÷»÷ÖÐ.gfx";
|
|
//
|
|
// if (bHideFlyGfx) pszFlyGFX = NULL;
|
|
// if (bHideHitGfx) pszHitGFX = NULL;
|
|
//
|
|
// g_pGame->GetGameRun()->GetWorld()->GetSkillGfxMan()->AddSkillGfxEvent(m_idHost, data.idTarget,
|
|
// pszFlyGFX, pszHitGFX, m_timeToDoDamage, false, enumLinearMove, 1, 0, NULL, vFlyScale, vHitScale, data.dwModifier);
|
|
// }
|
|
}
|
|
else
|
|
{
|
|
// without weapon
|
|
// ʹÓÃÈÌ×ÀàµÄ»÷ÖÐЧ¹û
|
|
DATA_TYPE dt = DATA_TYPE.DT_INVALID;
|
|
var pData = ElementDataManProvider.GetElementDataMan().get_data_ptr(183, ID_SPACE.ID_SPACE_ESSENCE, ref dt);
|
|
WEAPON_SUB_TYPE pWeaponType = (WEAPON_SUB_TYPE)pData;
|
|
|
|
bool bHideHitGfx = false;//!CECOptimize::Instance().GetGFX().CanShowHit(m_idHost);
|
|
string szGFX = null;
|
|
int nNumTargets = m_targets.Count();
|
|
for (int i = 0; i < nNumTargets; i++)
|
|
{
|
|
TARGET_DATA data = m_targets[i];
|
|
|
|
// szGFX = pWeaponType->file_hitgfx + 4; // skip gfx/
|
|
string fullGfx = pWeaponType.GetFileHitGfx();
|
|
szGFX = fullGfx.Length > 4 ? fullGfx.Substring(4) : string.Empty;
|
|
if ((data.dwModifier & (int)MOD.MOD_NULLITY) != 0)
|
|
szGFX = "程序联入\\击中\\无效攻击击中.gfx";//Program link\Hit\InvalidAttackHit.gfx
|
|
if (bHideHitGfx) szGFX = null;
|
|
|
|
// g_pGame->GetGameRun()->GetWorld()->GetSkillGfxMan()->AddSkillGfxEvent(m_idHost, data.idTarget, NULL,
|
|
// szGFX, m_timeToDoDamage, false, GfxMoveMode.enumLinearMove, 1, 0, NULL, vFlyScale, vHitScale, data.dwModifier);
|
|
var target = EC_ManMessageMono.Instance?.GetObject(data.idTarget, 0)?.gameObject.transform;
|
|
if (target == null)
|
|
{
|
|
BMLogger.LogError("Target is null!");
|
|
return false;
|
|
}
|
|
var vfx = CECGameRun.Instance.GetTestVfx();
|
|
vfx.transform.position = target.position;
|
|
CECGameRun.Instance.DestroyTestVfx(vfx, 1f);
|
|
}
|
|
}
|
|
}
|
|
else if (GPDataTypeHelper.ISNPCID(m_idHost))
|
|
{
|
|
// if( m_idSkill != 0 )
|
|
// {
|
|
// const A3DSkillGfxComposer* pComposer = NULL;
|
|
// // we use skill composed gfx to present the skill effect
|
|
// if (m_nSkillSection>0) // ¶à¶Î¼¼ÄÜ
|
|
// {
|
|
// CECMultiSectionSkillMan* pMan = m_pManager->GetMultiSkillGfxComposerMan();
|
|
// if(pMan)
|
|
// {
|
|
// pMan->Play(m_idSkill, m_nSkillSection,m_idHost, m_idCastTarget, m_targets);
|
|
// pComposer = pMan->GetSkillGfxComposer(m_idSkill, m_nSkillSection);
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// m_pManager->GetSkillGfxComposerMan()->Play(m_idSkill, m_idHost, m_idCastTarget, m_targets);
|
|
// pComposer = m_pManager->GetSkillGfxComposerMan()->GetSkillGfxComposer(m_idSkill);
|
|
// }
|
|
//
|
|
// if (pComposer && pComposer->m_dwFlyTime == 0) // ¼¼ÄÜûÓзÉÐÐʱ¼ä£¬ÔòÖ±½ÓÍ·¶¥Ã°×Ö
|
|
// {
|
|
// m_timeToDoDamage = 1;
|
|
// }
|
|
// else
|
|
// {
|
|
// A3DVECTOR3 vecHost, vecTarget;
|
|
// // now we estimated a time to do damage
|
|
// if( m_targets.size() && _get_pos_by_id(m_idHost, vecHost) && _get_pos_by_id(m_targets[0].idTarget, vecTarget) )
|
|
// {
|
|
// m_timeToDoDamage = int(Magnitude(vecHost - vecTarget) / 20.0f * 1000.0f);
|
|
// a_ClampFloor(m_timeToDoDamage, 10ul);
|
|
// }
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// CECNPC * pNPC = g_pGame->GetGameRun()->GetWorld()->GetNPCMan()->GetNPC(m_idHost);
|
|
// if (!pNPC)
|
|
// return true;
|
|
//
|
|
// const char * szFlyGFX = NULL;
|
|
// const char * szHitGFX = NULL;
|
|
//
|
|
// if (pNPC->IsMonsterNPC())
|
|
// {
|
|
// const MONSTER_ESSENCE * pEssence = ((CECMonster *)pNPC)->GetDBEssence();
|
|
//
|
|
// szFlyGFX = pEssence->file_gfx_short + 4; // skip gfx/
|
|
// szHitGFX = pEssence->file_gfx_short_hit + 4; // skip gfx/
|
|
// }
|
|
// else if (pNPC->IsPetNPC())
|
|
// {
|
|
// //szFlyGFX = "";
|
|
// const PET_ESSENCE* pEssence = ((CECPet*)pNPC)->GetDBEssence();
|
|
//
|
|
// szFlyGFX = pEssence->file_gfx_short + 4; //"²ß»®ÁªÈë\\ͨÓ÷ÉÐÐ\\¹¼ý·ÉÐÐ.gfx";
|
|
//
|
|
// szHitGFX = "²ß»®ÁªÈë\\¹ÖÎï»÷ÖÐ\\¹ÖÎïÈⲫ»÷ÖÐ.gfx";
|
|
// }
|
|
// else
|
|
// return false;
|
|
//
|
|
// if( !szFlyGFX[0] )
|
|
// szFlyGFX = NULL;
|
|
//
|
|
// if( !szHitGFX[0] )
|
|
// szHitGFX = NULL;
|
|
//
|
|
// bool bHideFlyGfx = !CECOptimize::Instance().GetGFX().CanShowFly(m_idHost);
|
|
// bool bHideHitGfx = !CECOptimize::Instance().GetGFX().CanShowHit(m_idHost);
|
|
// int nNumTargets = m_targets.size();
|
|
// for(int i=0; i<nNumTargets; i++)
|
|
// {
|
|
// const TARGET_DATA& data = m_targets[i];
|
|
//
|
|
// const char * pszFlyGFX = szFlyGFX;
|
|
// const char * pszHitGFX = szHitGFX;
|
|
// if( data.nDamage <= 0 )
|
|
// {
|
|
// pszHitGFX = NULL;
|
|
// }
|
|
// else if( data.dwModifier & MOD_NULLITY )
|
|
// {
|
|
// pszHitGFX = "³ÌÐòÁªÈë\\»÷ÖÐ\\ÎÞЧ¹¥»÷»÷ÖÐ.gfx";
|
|
// }
|
|
//
|
|
// if (bHideFlyGfx) pszFlyGFX = NULL;
|
|
// if (bHideHitGfx) pszHitGFX = NULL;
|
|
//
|
|
// g_pGame->GetGameRun()->GetWorld()->GetSkillGfxMan()->AddSkillGfxEvent(m_idHost, data.idTarget,
|
|
// pszFlyGFX, pszHitGFX, m_timeToDoDamage, false, enumLinearMove, 1,
|
|
// 0, NULL, vFlyScale, vHitScale, data.dwModifier);
|
|
// }
|
|
// }
|
|
}
|
|
else
|
|
return true;
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool DoDamage()
|
|
{
|
|
m_bDoDamaged = true;
|
|
m_bFinished = true;
|
|
|
|
/* CECGameRun pGameRun = g_pGame-GetGameRun();
|
|
int idHostPlayer = pGameRun->GetHostPlayer()->GetCharacterID();*/
|
|
|
|
// Get host name
|
|
/* ACString strHostName;
|
|
CECObject* pHostObject = pGameRun->GetWorld()->GetObject(m_idHost, 0);
|
|
if (pHostObject)
|
|
{
|
|
if (ISNPCID(m_idHost))
|
|
strHostName = ((CECNPC*)pHostObject)->GetName();
|
|
else if (ISPLAYERID(m_idHost))
|
|
strHostName = GetPlayerName((CECPlayer*)pHostObject);
|
|
}*/
|
|
int nNumTargets = m_targets.Count;
|
|
for (int i = 0; i < nNumTargets; i++)
|
|
{
|
|
TARGET_DATA data = m_targets[i];
|
|
int idTarget = data.idTarget;
|
|
string strName;
|
|
|
|
if (GPDataTypeHelper.ISNPCID(idTarget))
|
|
{
|
|
CECNPC pNPC = null;
|
|
if ((data.dwModifier & (uint)MOD.MOD_SUCCESS) != 0)
|
|
pNPC = EC_ManMessageMono.Instance._CECNPCMan.GetNPCFromAll(idTarget);
|
|
else
|
|
{
|
|
pNPC = EC_ManMessageMono.Instance._CECNPCMan.GetNPCFromAll(idTarget);
|
|
if (!pNPC)
|
|
return true;
|
|
|
|
//strName = pNPC->GetNameToShow();
|
|
}
|
|
|
|
if (!pNPC)
|
|
return true;
|
|
|
|
pNPC.Damaged(data.nDamage, data.dwModifier);
|
|
}
|
|
else if (GPDataTypeHelper.ISPLAYERID(idTarget))
|
|
{
|
|
|
|
CECPlayer pPlayer = EC_ManMessageMono.Instance.GetECManPlayer.GetPlayer(idTarget);
|
|
//BMLogger.LogError("HoangDev: CECPlayer pPlayer = " + pPlayer );
|
|
|
|
if (!pPlayer)
|
|
return true;
|
|
|
|
//strName = GetPlayerName(pPlayer);
|
|
|
|
pPlayer.Damaged(data.nDamage, data.dwModifier, m_idSkill);
|
|
}
|
|
|
|
/* if (data.nDamage > 0)
|
|
{
|
|
if (m_idHost == idHostPlayer)
|
|
{
|
|
if (!strName.IsEmpty())
|
|
pGameRun->AddFixedChannelMsg(FIXMSG_DODAMAGE, GP_CHAT_DAMAGE, strName, data.nDamage);
|
|
}
|
|
else if (data.idTarget == idHostPlayer)
|
|
{
|
|
if (!strHostName.IsEmpty())
|
|
pGameRun->AddFixedChannelMsg(FIXMSG_BEDAMAGED, GP_CHAT_DAMAGE, strHostName, data.nDamage);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool AddTarget(int idTarget, uint dwModifier, int nDamage)
|
|
{
|
|
m_targets.Add(new TARGET_DATA
|
|
{
|
|
idTarget = idTarget,
|
|
dwModifier = dwModifier,
|
|
nDamage = nDamage
|
|
});
|
|
return true;
|
|
}
|
|
public bool UpdateTargetFlag()
|
|
{
|
|
// update all targets' bAboutToDie flag
|
|
|
|
int nNumTargets = m_targets.Count;
|
|
for (int i = 0; i < nNumTargets; i++)
|
|
{
|
|
TARGET_DATA data = m_targets[i];
|
|
/*
|
|
if( data.dwModifier & MOD_DEADLYSTRIKE )
|
|
{
|
|
int idTarget = data.idTarget;
|
|
if (ISNPCID(idTarget))
|
|
{
|
|
CECNPC* pNPC = g_pGame->GetGameRun()->GetWorld()->GetNPCMan()->GetNPC(idTarget);
|
|
if (!pNPC)
|
|
return true;
|
|
|
|
pNPC->SetAboutToDie(true);
|
|
}
|
|
else if (ISPLAYERID(idTarget))
|
|
{
|
|
CECPlayer* pPlayer = g_pGame->GetGameRun()->GetWorld()->GetPlayerMan()->GetPlayer(idTarget);
|
|
if (!pPlayer)
|
|
return true;
|
|
|
|
pPlayer->SetAboutToDie(true);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
public class CECAttackerEvents
|
|
{
|
|
private readonly List<CECAttackEvent> m_list = new List<CECAttackEvent>();
|
|
|
|
public void Add(CECAttackEvent? evt)
|
|
{
|
|
if (evt != null)
|
|
m_list.Add(evt);
|
|
}
|
|
|
|
public bool IsEmpty() => m_list.Count == 0;
|
|
public int Count() => m_list.Count;
|
|
|
|
public CECAttackEvent? Find(int idSkill = 0, int nSkillSection = 0)
|
|
{
|
|
foreach (var evt in m_list)
|
|
{
|
|
if (evt.m_idSkill == idSkill && evt.m_nSkillSection == nSkillSection)
|
|
return evt;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void Signal()
|
|
{
|
|
foreach (var evt in m_list)
|
|
evt.m_bSignaled = true;
|
|
m_list.Clear();
|
|
}
|
|
|
|
public static implicit operator bool(CECAttackerEvents events)
|
|
{
|
|
return !events.IsEmpty();
|
|
}
|
|
}
|
|
enum MOD
|
|
|
|
{
|
|
MOD_PHYSIC_ATTACK_RUNE = 0x0001, // ÎïÀí¹¥»÷ÓÅ»¯·ûÉúЧ
|
|
MOD_MAGIC_ATTACK_RUNE = 0x0002, // ·¨Êõ¹¥»÷ÓÅ»¯·ûÉúЧ
|
|
MOD_PHYSIC_DEFENCE_RUNE = 0x0004, // ÎïÀí·ÀÓùÓÅ»¯·ûÉúЧ
|
|
MOD_MAGIC_DEFENCE_RUNE = 0x0008, // ·¨Êõ·ÀÓùÓÅ»¯·ûÉúЧ
|
|
MOD_CRITICAL_STRIKE = 0x0010, // ±¬»÷
|
|
MOD_RETORT = 0x0020, // ·´Õð
|
|
MOD_NULLITY = 0x0040, // ÎÞЧ¹¥»÷
|
|
MOD_IMMUNE = 0x0080, // ÃâÒßÁ˴˴ι¥»÷£¬ÓÅÏȼ¶¸ßÓÚÎÞЧ
|
|
MOD_ENCHANT_FAILED = 0x0100, // enchant ʧ°Ü
|
|
MOD_SUCCESS = 0x0200, // ³É¹¦
|
|
MOD_DODGE_DAMAGE = 0x0400, // É˺¦¶ãÉÁ
|
|
MOD_DODGE_DEBUFF = 0x0800, // ״̬¶ãÉÁ
|
|
MOD_ATTACK_AURA = 0x1000, // ¹â»·¹¥»÷
|
|
MOD_REBOUND = 0x2000, // ·´µ¯
|
|
MOD_BEAT_BACK = 0x4000, // ·´»÷
|
|
};
|
|
enum GfxMoveMode
|
|
{
|
|
enumLinearMove = 0, // Linear
|
|
enumParabolicMove, // Parabolic
|
|
enumMissileMove, // Missile
|
|
enumMeteoricMove, // Meteoric (shooting star)
|
|
enumHelixMove, // Helix (spiral)
|
|
enumCurvedMove, // Curved
|
|
enumAccMove, // Accelerated
|
|
enumOnTarget, // Targeted
|
|
enumLink, // Linked
|
|
enumRandMove, // Random movement
|
|
enumMoveModeNum
|
|
}; |