/* * FILE: CECComboSkill.cs * * DESCRIPTION: Combo skill system for Perfect World client * * CONVERTED FROM: EC_ComboSkill.cpp/EC_ComboSkill.h * * CREATED BY: Duyuxin, 2005/12/26 * * HISTORY: * * Copyright (c) 2005 Archosaur Studio, All Rights Reserved. */ using BrewMonster.Assets.PerfectWorld.Scripts.Players; using BrewMonster.Network; using BrewMonster.Scripts.Skills; using CSNetwork; using UnityEngine; using static CECPlayerWrapper; namespace BrewMonster { /////////////////////////////////////////////////////////////////////////// // // Class CECComboSkill - 连招技能管理类 / Combo Skill Manager Class // /////////////////////////////////////////////////////////////////////////// public class CECComboSkill { // Special skill id - 特殊技能ID / Special Skill IDs public enum SpecialSkillID { SID_ATTACK = -1, // Normal attack - 普通攻击 / Normal attack SID_LOOPSTART = -2, // Loop start flag - 循环开始标记 / Loop start flag } // Attributes - 成员变量 / Member Variables protected CECHostPlayer m_pHost; // Host player object - 宿主玩家对象 / Host player object protected EC_COMBOSKILL m_cs; // Combo skill data - 连招技能数据 / Combo skill data protected int m_iGroup; // Group index - 组索引 / Group index protected int m_iCursor; // 当前光标位置 / Current cursor position protected bool m_bStop; // Stop flag - 停止标志 / Stop flag protected int m_iLoopStart; // Loop start index - 循环开始索引 / Loop start index protected int m_idTarget; // Attack target - 攻击目标 / Attack target protected bool m_bForceAtk; // Force attack flag - 强制攻击标志 / Force attack flag protected bool m_bIgnoreAtkLoop; // Ignore attack and loop flag - 忽略攻击和循环标志 / Ignore attack and loop flag // Constructor - 构造函数 / Constructor public CECComboSkill() { m_pHost = null; m_iGroup = -1; m_iCursor = 0; m_bStop = false; m_iLoopStart = -1; m_idTarget = 0; m_bForceAtk = false; m_bIgnoreAtkLoop = false; m_cs = new EC_COMBOSKILL(true); } // Initialize object - 初始化对象 / Initialize object public bool Init(CECHostPlayer pHost, int iGroup, int idTarget, bool bForceAttack, bool bIgnoreAtkLoop) { if (iGroup < 0 || iGroup >= EC_ConfigConstants.EC_COMBOSKILL_NUM) { BMLogger.LogError("CECComboSkill.Init: Invalid group index"); return false; } m_pHost = pHost; m_iGroup = iGroup; m_iCursor = 0; m_bStop = false; m_idTarget = idTarget; m_bForceAtk = bForceAttack; m_bIgnoreAtkLoop = bIgnoreAtkLoop; // TODO: Get combo skill configuration from CECConfigs CECConfigs pCfg = EC_Game.GetConfigs(); m_cs = pCfg.GetVideoSettings().comboSkill[iGroup]; m_iLoopStart = -1; for (int i = 0; i < EC_ConfigConstants.EC_COMBOSKILL_LEN; i++) { if (m_cs.idSkill[i] == (short)SpecialSkillID.SID_LOOPSTART) m_iLoopStart = i; } // Ensure there is valid skill id after loop start flag // 确保循环标记后有有效的技能ID / Ensure there is valid skill id after loop start flag if (m_iLoopStart >= 0) { int i; for (i = m_iLoopStart + 1; i < EC_ConfigConstants.EC_COMBOSKILL_LEN; i++) { int id = m_cs.idSkill[i]; if (id > 0 || id == (short)SpecialSkillID.SID_ATTACK) break; } // If no valid skill after loop start flag is found, // disable this loop start flag // 如果在循环标记后没有找到有效技能,禁用此循环标记 // If no valid skill after loop start flag is found, disable this loop start flag if (i >= EC_ConfigConstants.EC_COMBOSKILL_LEN) m_iLoopStart = -1; } // Move cursor to proper position - 移动光标到合适的位置 / Move cursor to proper position StepCursor(true); return true; } // Continue combo skill - 继续连招技能 / Continue combo skill // Return true if combo skill will continues, false if combo skill stops // 返回true表示连招将继续,false表示连招停止 / Return true if combo skill will continues, false if combo skill stops // bMeleeing: true, player is being meleeing - 玩家正在进行近战 / true, player is being meleeing public bool Continue(bool bMeleeing) { int nextSkill = GetNextSkill(); BMLogger.Log($"[COMBO] Continue ENTRY cursor={m_iCursor} nextSkill={nextSkill} m_bStop={m_bStop} nIcon={m_cs.nIcon} " + $"target={m_idTarget} selTarget={m_pHost?.m_idSelTarget} bMeleeing={bMeleeing} group={m_iGroup}"); if (m_bStop || m_cs.nIcon == 0 || m_pHost == null || m_iCursor < 0 || m_iCursor >= EC_ConfigConstants.EC_COMBOSKILL_LEN || (m_idTarget != m_pHost.m_idSelTarget && !m_bIgnoreAtkLoop)) { BMLogger.Log($"[COMBO] Continue EXIT early (guard) m_bStop={m_bStop} nIcon={m_cs.nIcon} cursor={m_iCursor} targetMatch={m_pHost != null && m_idTarget == m_pHost.m_idSelTarget}"); return false; } // TODO: Get PlayerWrapper from CECAutoPolicy CECPlayerWrapper pWrapper = CECAutoPolicy.GetInstance().GetPlayerWrapper(); int idSkill = nextSkill; if (idSkill > 0) { BMLogger.Log($"[COMBO] Continue applying SKILL idSkill={idSkill}"); bool bRet = m_pHost.ApplySkillShortcut(idSkill, true, m_idTarget, m_bForceAtk ? 1 : 0); if (!bRet) { // 施放失败,尝试施放下一个技能 / Cast failed, try to cast next skill StepCursor(false); if (!IsStop()) { // TODO: Post message to continue combo skill EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CONTINUECOMBOSKILL, MANAGER_INDEX.MAN_PLAYER, 0, bMeleeing ? 1 : 0, m_iGroup); } else { // 连击技能停止 / Combo skill finish AP_ActionEvent((int)AP_EVENT. AP_EVENT_COMBOFINISH); } return false; } else { // TODO: Handle auto policy events if (CECAutoPolicy.GetInstance().IsAutoPolicyEnabled()) pWrapper.AddAttackError(); // 检测自己施放是否追加 / Check if self-cast should be tracked int iSelfCureFlag = 0; if (m_pHost.GetPrepSkill() != null) iSelfCureFlag = 1; AP_ActionEvent((int)AP_EVENT.AP_EVENT_COMBOCONTINUE, iSelfCureFlag); } BMLogger.Log($"[COMBO] Continue SKILL success, will StepCursor and return true"); } else if (idSkill == (short)SpecialSkillID.SID_ATTACK) { BMLogger.Log($"[COMBO] Continue SID_ATTACK branch bMeleeing={bMeleeing} bIgnoreAtkLoop={m_bIgnoreAtkLoop}"); if (!bMeleeing && !m_bIgnoreAtkLoop) { BMLogger.Log($"[COMBO] Continue calling CmdNormalAttack for normal attack step"); bool bRet = m_pHost.CmdNormalAttack(false, true, m_idTarget, m_bForceAtk ? 1 : 0); if (bRet) { // TODO: Handle auto policy events if (CECAutoPolicy.GetInstance().IsAutoPolicyEnabled()) pWrapper.AddAttackError(); AP_ActionEvent((int)AP_EVENT.AP_EVENT_COMBOCONTINUE); } else { StepCursor(false); if (!IsStop()) { // TODO: Post message to continue combo skill EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CONTINUECOMBOSKILL, MANAGER_INDEX.MAN_PLAYER, 0, bMeleeing ? 1 : 0, m_iGroup); } else { // 连击技能停止 / Combo skill finish AP_ActionEvent((int)AP_EVENT.AP_EVENT_COMBOFINISH); } return false; } } else { // 当前是攻击,执行下一个元素 / Current is attack, execute next element StepCursor(false); if (!IsStop()) { // TODO: Post message to continue combo skill EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CONTINUECOMBOSKILL, MANAGER_INDEX.MAN_PLAYER, 0, bMeleeing ? 1 : 0, m_iGroup); } else { // 连击技能停止 / Combo skill finish AP_ActionEvent((int)AP_EVENT.AP_EVENT_COMBOFINISH); } return false; } } else { BMLogger.LogError("CECComboSkill.Continue: Invalid skill id"); return false; } // Forward cursor - 前进光标 / Forward cursor StepCursor(false); BMLogger.Log($"[COMBO] Continue after StepCursor cursor={m_iCursor} m_bStop={m_bStop} returning true"); return true; } // Step cursor - 步进光标 / Step cursor protected void StepCursor(bool bFirst) { if (bFirst) m_iCursor = -1; while (true) { m_iCursor++; if (m_iCursor >= EC_ConfigConstants.EC_COMBOSKILL_LEN) { // If we has loop flag, jump to it // 如果有循环标记,跳转到它 / If we has loop flag, jump to it if (m_iLoopStart >= 0 && !m_bIgnoreAtkLoop) m_iCursor = m_iLoopStart; else { m_bStop = true; return; } } else { int id = m_cs.idSkill[m_iCursor]; if (id > 0 || (id == (short)SpecialSkillID.SID_ATTACK && !m_bIgnoreAtkLoop)) break; } } } // Get id of skill next Continue() will trigger // 获取下一次Continue()将触发的技能ID / Get id of skill next Continue() will trigger public int GetNextSkill() { if (m_bStop || m_iCursor < 0 || m_iCursor >= EC_ConfigConstants.EC_COMBOSKILL_LEN) return 0; return m_cs.idSkill[m_iCursor]; } // Get stop flag - 获取停止标志 / Get stop flag public bool IsStop() { return m_bStop; } // Replace skill id - 替换技能ID / Replace skill id public void ReplaceSkillID(int idOld, int idNew) { for (int i = 0; i < EC_ConfigConstants.EC_COMBOSKILL_LEN; i++) { if (m_cs.idSkill[i] == (short)idOld) m_cs.idSkill[i] = (short)idNew; } } // Replace skill id (multiple old IDs) - 替换技能ID(多个旧ID) / Replace skill id (multiple old IDs) public void ReplaceSkillID(SkillArrayWrapper idOlds, int idNew) { for (int i = 0; i < idOlds.Count(); i++) { ReplaceSkillID((int)idOlds[i], idNew); } } // Find skill ID in combo - 在连招中查找技能ID / Find skill ID in combo public bool FindSkillID(int idSkill) { bool bFound = false; if (idSkill > 0) { for (int i = 0; i < EC_ConfigConstants.EC_COMBOSKILL_LEN; i++) { if (m_cs.idSkill[i] == (short)idSkill) { bFound = true; break; } } } return bFound; } // Get the target - 获取目标 / Get the target public int GetTarget() { return m_idTarget; } // Do we ignore the attack and loop flag ? - 是否忽略攻击和循环标志 / Do we ignore the attack and loop flag ? public bool IsIgnoreAtkLoop() { return m_bIgnoreAtkLoop; } // Get group index - 获取组索引 / Get group index public int GetGroupIndex() { return m_iGroup; } public void AP_ActionEvent(int iEvent, int iParam = 0) { if (!CECAutoPolicy.GetInstance().IsAutoPolicyEnabled()) return; CECPlayerWrapper pWrapper = CECAutoPolicy.GetInstance().GetPlayerWrapper(); if (pWrapper != null) pWrapper.OnActionEvent(iEvent, iParam); } } }