yyl
6 天以前 5bc2cc9a3e007b96a0de96e70e87f25bc5a254a2
125 【战斗】战斗系统 战斗系统迭代 协议接入 阵容部分代码
24 文件已复制
43个文件已修改
28个文件已添加
3个文件已删除
1 文件已重命名
3477 ■■■■ 已修改文件
Main/Component/UI/Effect/EffectPlayer.cs 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/SkillConfig.cs 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/SkillConfig.Partial.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/SkillConfig.Partial.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0407_tagNPCDisappear.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0418_tagObjInfoRefresh.cs 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0423_tagObjPropertyRefreshView.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H06_PlayerVsNPC/DTC0602_tagObjBaseAttack.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H06_PlayerVsNPC/DTC0602_tagObjBaseAttack.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H06_PlayerVsNPC/DTC0604_tagUseSkillAttack.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H06_PlayerVsNPC/DTC0605_tagObjAddBuff.cs 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB420_tagMCTurnFightState.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB421_tagMCTurnFightObjAction.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB422_tagMCTurnFightObjDead.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB423_tagMCTurnFightObjReborn.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB424_tagSCTurnFightInit.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB425_tagSCTurnFightReportSign.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB430_tagSCTurnFightReport.cs 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/GameNetPackBasic.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleEffectMgr.cs 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleEffectMgr.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/BattleField.cs 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/BattleRootNode.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/RecordActions/DeathRecordAction.cs 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/RecordActions/DeathRecordAction.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/RecordActions/EditorSkillRecordAction.cs 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/RecordActions/EditorSkillRecordAction.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/RecordActions/SkillRecordAction.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/StoryBattleField.cs 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleFieldFactory.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleFieldFactory.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleManager.cs 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleObject/BattleObjMgr.cs 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleObject/BattleObject.cs 194 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleObject/BattleObjectFactory.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleTweenMgr.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleTweenMgr.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleUtility.cs 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleUtility.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Buff/BattleObjectBuffMgr.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Buff/BattleObjectBuffMgr.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Buff/BuffBase.cs 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Buff/BuffBase.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Buff/BuffFactory.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Buff/BuffFactory.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet/Bullet.cs 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet/Bullet.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet/BulletFactory.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet/BulletFactory.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet/CloseCombatBullet.cs 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet/CloseCombatBullet.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet/TrajectoryBullet.cs 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Bullet/TrajectoryBullet.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Define/BattleType.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Define/BattleType.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Define/PlayEffectType.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Define/PlayEffectType.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Motion/MotionBase.cs 134 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Motion/MotionName.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/RecordPlayer/RecordAction.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/DirectlyDamageSkill.cs 79 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillBase.cs 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillCastMode.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillCastMode.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillEffectAnchor.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillEffectAnchor.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillEffectType.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillEffectType.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillFactory.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillType.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillType.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect/BulletSkillEffect.cs 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect/BulletSkillEffect.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect/NormalSkillEffect.cs 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect/NormalSkillEffect.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect/SkillEffect.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect/SkillEffect.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect/SkillEffectFactory.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/SkillEffect/SkillEffectFactory.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/DamageContent.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/DamageLine.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GeneralConfig/GeneralDefine.cs 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroFetterInfo.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Lineup.cs 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Lineup.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroManager.cs 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/MainLevel.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamBase.Properties.cs 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamBase.cs 400 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamConst.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamHero.cs 97 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamManager.cs 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/MathUtility.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/UniTaskExtension.cs 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Effect/EffectPlayer.cs
@@ -1,26 +1,33 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Spine.Unity;
using UnityEngine;
using Spine;
public class EffectPlayer : MonoBehaviour
{
    public int effectId;
    public int effectId;
    public bool autoDestroy = false;
    public bool autoDestroy = false;
    public float destroyDelay = 0f;
    public float destroyDelay = 0f;
    [HideInInspector] public Canvas canvas = null;
    public Action<EffectPlayer> onDestroy;
    [HideInInspector] public Canvas canvas = null;
    [HideInInspector] public GameObject effectTarget = null;
    protected EffectPenetrationBlocker blocker = null;
    protected void Start()
    {
    {
        ReStart();
    }
    public void Stop()
    {
    {
        if (null != effectTarget)
        {
            DestroyImmediate(effectTarget);
@@ -30,9 +37,12 @@
    public void Play()
    {
        if (!isActiveAndEnabled)
        {
            gameObject.SetActive(true);
        }
        ReStart();
    }
    protected void ReStart()
@@ -80,7 +90,7 @@
        }
        // 添加特效穿透阻挡器
        EffectPenetrationBlocker blocker = effectTarget.AddComponent<EffectPenetrationBlocker>();
        blocker = effectTarget.AddMissingComponent<EffectPenetrationBlocker>();
        blocker.parentCanvas = canvas;
        //  延迟一帧才生效
@@ -95,4 +105,141 @@
        }
    }
    public void SetSortingOrderOffset(int offset)
    {
        // 被Destroy之后effectTarget == null 为 true 但是访问内容会报错
        if (blocker != null && effectTarget != null)
        {
            blocker.sortingOrderOffset = offset;
            blocker.UpdateSortingOrder();
        }
        else
        {
            blocker = null;
            effectTarget = null;
        }
    }
    protected void OnDestroy()
    {
        if (onDestroy != null)
        {
            onDestroy.Invoke(this);
            onDestroy = null;
        }
    }
    //  创建后的特效会自动隐藏 需要手动调用Play才能播放
    public static EffectPlayer Create(int effectId, Transform parent, bool createNewChild = false, bool _autoDestroy = true, float _destroyDelay = 5f)
    {
        EffectPlayer effectPlayer = null;
        if (createNewChild)
        {
            GameObject newGo = new GameObject("EffectPlayer_" + effectId);
            newGo.transform.SetParent(parent, false);
            effectPlayer = newGo.AddComponent<EffectPlayer>();
        }
        else
        {
            effectPlayer = parent.AddMissingComponent<EffectPlayer>();
            effectPlayer.effectId = effectId;
            effectPlayer.autoDestroy = _autoDestroy;
            effectPlayer.destroyDelay = _destroyDelay;
        }
        effectPlayer.SetActive(false);
        return effectPlayer;
    }
    public void Pause()
    {
        if (effectTarget == null) return;
        // Spine动画
        var spineGraphics = effectTarget.GetComponentsInChildren<SkeletonGraphic>(true);
        foreach (var sg in spineGraphics)
        {
            sg.timeScale = 0f;
        }
        // Animator动画
        var animators = effectTarget.GetComponentsInChildren<Animator>(true);
        foreach (var animator in animators)
        {
            animator.speed = 0f;
        }
        // 粒子特效
        var particles = effectTarget.GetComponentsInChildren<ParticleSystem>(true);
        foreach (var ps in particles)
        {
            ps.Pause();
        }
    }
    public void Resume()
    {
        if (effectTarget == null) return;
        // Spine动画
        var spineGraphics = effectTarget.GetComponentsInChildren<SkeletonGraphic>(true);
        foreach (var sg in spineGraphics)
        {
            sg.timeScale = 1f;
        }
        // Animator动画
        var animators = effectTarget.GetComponentsInChildren<Animator>(true);
        foreach (var animator in animators)
        {
            animator.speed = 1f;
        }
        // 粒子特效
        var particles = effectTarget.GetComponentsInChildren<ParticleSystem>(true);
        foreach (var ps in particles)
        {
            ps.Play();
        }
    }
    public bool IsFinish()
    {
        if (effectTarget == null) return true;
        // Spine动画
        var spineGraphics = effectTarget.GetComponentsInChildren<SkeletonGraphic>(true);
        foreach (var sg in spineGraphics)
        {
            if (!sg.AnimationState.GetCurrent(0).IsComplete)
            {
                return false;
            }
        }
        // Animator动画
        var animators = effectTarget.GetComponentsInChildren<Animator>(true);
        foreach (var animator in animators)
        {
            AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
            //  循环动画不考虑结束的问题
            if (!stateInfo.loop && stateInfo.normalizedTime < 1f)
            {
                return false;
            }
        }
        // 粒子特效
        var particles = effectTarget.GetComponentsInChildren<ParticleSystem>(true);
        foreach (var ps in particles)
        {
            if (ps.IsAlive())
            {
                return false;
            }
        }
        return true;
    }
}
Main/Config/Configs/SkillConfig.cs
@@ -1,6 +1,6 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           Friday, June 27, 2025
//    [  Date ]:           2025年7月17日
//--------------------------------------------------------
using System.Collections.Generic;
@@ -85,9 +85,15 @@
    public string IconName;
    public string Description;
    public string BuffDescription;
    public int Skillactmark;
    public int BuffDisplay;
    public int CastPosition;
    public int CastDistance;
    public int[] TriggerFrames;
    public int[][] DamageDivide;
    public string SkillMotionName;
    public int EffectId;
    public int ExplotionEffectId;
    public float FlyTime;
    public override int LoadKey(string _key)
    {
@@ -243,11 +249,35 @@
            BuffDescription = tables[71];
            int.TryParse(tables[72],out Skillactmark);
            int.TryParse(tables[72],out BuffDisplay);
            int.TryParse(tables[73],out BuffDisplay);
            int.TryParse(tables[73],out CastPosition);
            int.TryParse(tables[74],out CastPosition);
            int.TryParse(tables[74],out CastDistance);
            if (tables[75].Contains("["))
            {
                TriggerFrames = JsonMapper.ToObject<int[]>(tables[75]);
            }
            else
            {
                string[] TriggerFramesStringArray = tables[75].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                TriggerFrames = new int[TriggerFramesStringArray.Length];
                for (int i=0;i<TriggerFramesStringArray.Length;i++)
                {
                     int.TryParse(TriggerFramesStringArray[i],out TriggerFrames[i]);
                }
            }
            DamageDivide = JsonMapper.ToObject<int[][]>(tables[76].Replace("(", "[").Replace(")", "]"));
            SkillMotionName = tables[77];
            int.TryParse(tables[78],out EffectId);
            int.TryParse(tables[79],out ExplotionEffectId);
            float.TryParse(tables[80],out FlyTime);
        }
        catch (Exception exception)
        {
Main/Config/PartialConfigs/SkillConfig.Partial.cs
New file
@@ -0,0 +1,24 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2025年7月11日
//--------------------------------------------------------
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System;
using UnityEngine;
using LitJson;
public partial class SkillConfig : ConfigBase<int, SkillConfig>
{
    public SkillType skillType;
    public SkillCastMode castMode;
    public SkillEffectType effectType;
    public SkillEffectAnchor effectAnchor;
    public MotionName GetMotionName()
    {
        return Enum.Parse<MotionName>(SkillMotionName);
    }
}
Main/Config/PartialConfigs/SkillConfig.Partial.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/Config/PartialConfigs/SkillConfig.Partial.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 80ceeaf1ce130d946a2e79320bdc9919
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0407_tagNPCDisappear.cs
@@ -7,5 +7,13 @@
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        H0407_tagNPCDisappear vNetData = vNetPack as H0407_tagNPCDisappear;
        //玩家主动点击休息时,后端会回收创建的战斗主阵容武将实例,前端收到//04 07 NPC消失#tagNPCDisappear时进行解绑
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetPack.packUID);
        //    提前结束战斗 删除NPCID()实际上是对象的ObjID
        battleField.FinishBattleInAdvance(vNetData.NPCID);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0418_tagObjInfoRefresh.cs
@@ -2,18 +2,33 @@
using System.Collections;
using static UnityEditor.Experimental.GraphView.GraphView;
//04 18 周围对象刷新#tagObjInfoRefresh
public class DTC0418_tagObjInfoRefresh : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        H0418_tagObjInfoRefresh vNetData = vNetPack as H0418_tagObjInfoRefresh;
        Update0418(vNetData);
    }
//04 18 周围对象刷新#tagObjInfoRefresh
public class DTC0418_tagObjInfoRefresh : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        H0418_tagObjInfoRefresh vNetData = vNetPack as H0418_tagObjInfoRefresh;
        // 1 - 玩家(主公)
        // 2 - NPC(武将或怪物)
        switch (vNetData.ObjType)
        {
            case 1:
                Update0418(vNetData);
                break;
            case 2:
                UpdateBattleField(vNetData);
                break;
            default:
                Debug.LogError("DTC0418_tagObjInfoRefresh 服务端同步了一个未知类型的ObjType的刷新 " + vNetData.ObjType);
                break;
        }
    }
    public static void Update0418(H0418_tagObjInfoRefresh vNetData)
    {
    {
        if (PlayerDatas.Instance.PlayerId == vNetData.ObjID)
        {
            
@@ -24,5 +39,11 @@
            //其他玩家数据,如果需要同场景处理
            //_player.UpdateData(vNetData);
        }
    }
}
    }
    public static void UpdateBattleField(H0418_tagObjInfoRefresh vNetData)
    {
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetData.packUID);
        battleField.ObjInfoRefresh(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0423_tagObjPropertyRefreshView.cs
@@ -7,5 +7,18 @@
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        H0423_tagObjPropertyRefreshView vNetData = vNetPack as H0423_tagObjPropertyRefreshView;
    // public uint ObjID;    //对象ID
    // public byte ObjType;    //对象类型
    // public uint SkillID;    //技能ID
    // public uint DiffValue;    //值
    // public uint DiffValueEx;    //超亿值
    // public byte AttackType;    //攻击类型
    // public uint SrcObjID;    //飘血来源
    // public byte SrcObjType;
    // public uint HP;    //剩余血量
    // public uint HPEx;    //剩余血量 亿
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetPack.packUID);
        battleField.ObjPropertyRefreshView(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/H06_PlayerVsNPC/DTC0602_tagObjBaseAttack.cs
File was deleted
Main/Core/NetworkPackage/DTCFile/ServerPack/H06_PlayerVsNPC/DTC0602_tagObjBaseAttack.cs.meta
File was deleted
Main/Core/NetworkPackage/DTCFile/ServerPack/H06_PlayerVsNPC/DTC0604_tagUseSkillAttack.cs
@@ -7,5 +7,23 @@
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        H0604_tagUseSkillAttack vNetData = vNetPack as H0604_tagUseSkillAttack;
    // public uint ObjID;
    // public byte ObjType;
    // public byte BattleType;    //物理/魔法
    // public ushort SkillID;
    // public uint AttackID;    //主攻击目标
    // public byte AttackObjType;    //主攻击目标
    // public ushort HurtCount;    //伤害数目
    // public  tagSkillHurtObj[] HurtList;    //size = HurtCount
        // ObjType类型的ObjID使用BattleType类技能SkillID攻击了AttackObjType类型的AttackID 伤害数字是HurtList
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetPack.packUID);
        battleField.ProcessUseSkillAttack(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/H06_PlayerVsNPC/DTC0605_tagObjAddBuff.cs
@@ -1,11 +1,23 @@
using UnityEngine;
using System.Collections;
using UnityEngine;
using System.Collections;
//06 05 对象增加Buf#tagObjAddBuff
public class DTC0605_tagObjAddBuff : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
    public override void Done(GameNetPackBasic vNetPack)
    {
        base.Done(vNetPack);
        H0605_tagObjAddBuff vNetData = vNetPack as H0605_tagObjAddBuff;
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetData.packUID);
        BattleObject vBattleObject = battleField.battleObjMgr.GetBattleObject((int)vNetData.ObjID);
        if (null == vBattleObject)
        {
            return;
        }
        vBattleObject.buffMgr.AddBuff(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB420_tagMCTurnFightState.cs
@@ -1,5 +1,6 @@
using UnityEngine;
using System.Collections;
using LitJson;
// B4 20 回合制战斗状态 #tagMCTurnFightState
@@ -7,5 +8,15 @@
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        HB420_tagMCTurnFightState vNetData = vNetPack as HB420_tagMCTurnFightState;
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetPack.packUID);
        //  MapID;    // 自定义地图ID,可用于绑定战斗地图场景功能(如主线关卡、主线boss、爬塔、竞技场等)
        //  FuncLineID;    // MapID对应的扩展值,如具体某个关卡等  章节*10000+关卡编号*100+第x波,如第一章,第10关卡的boss值 = 11001
        //  State;    // 0-起始状态标记;1-准备完毕;2-战斗中;3-战斗结束;4-结算奖励;5-结束状态标记
        //  TurnNum;    // 当前轮次
        //  Len;
        //  Msg;    //size = Len
        JsonData extendData = JsonMapper.ToObject(vNetData.Msg);
        battleField.OnTurnFightState(vNetData.TurnNum, vNetData.State, (int)vNetData.FuncLineID, extendData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB421_tagMCTurnFightObjAction.cs
@@ -1,5 +1,6 @@
using UnityEngine;
using System.Collections;
using LitJson;
// B4 21 回合战斗对象开始行动 #tagMCTurnFightObjAction
@@ -7,5 +8,8 @@
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        HB421_tagMCTurnFightObjAction vNetData = vNetPack as HB421_tagMCTurnFightObjAction;
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetPack.packUID);
        battleField.OnTurnFightObjAction(vNetData.TurnNum, (int)vNetData.ObjID);
        // BattleManager.Instance.OnTurnFightObjAction(vNetData.TurnNum, (int)vNetData.ObjID);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB422_tagMCTurnFightObjDead.cs
@@ -7,5 +7,9 @@
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        HB422_tagMCTurnFightObjDead vNetData = vNetPack as HB422_tagMCTurnFightObjDead;
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetPack.packUID);
        battleField.OnObjDead((int)vNetData.ObjID);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB423_tagMCTurnFightObjReborn.cs
@@ -6,6 +6,11 @@
public class DTCB423_tagMCTurnFightObjReborn : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        //    服务端说暂时没做到这边 暂放 TODO YYL
        HB423_tagMCTurnFightObjReborn vNetData = vNetPack as HB423_tagMCTurnFightObjReborn;
        BattleField battleField = BattleManager.Instance.GetBattleField(vNetPack.packUID);
        // battleField.OnObjReborn(vNetData.ObjID);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB424_tagSCTurnFightInit.cs
@@ -33,13 +33,6 @@
        // FactionCnt;
        // FactionList;    // 阵营列表,通常固定只有两个阵营
        uint chapter = vNetData.FuncLineID / 10000;
        uint wave = vNetData.MapID == 1 ? vNetData.FuncLineID % 100 : 1;//第几波怪
        uint level = (vNetData.FuncLineID % 10000) / 100;
        JsonData extendData = JsonMapper.ToObject(vNetData.Msg);
        List<TeamBase> redTeamList = new List<TeamBase>();
        List<TeamBase> blueTeamList = new List<TeamBase>();
@@ -64,5 +57,20 @@
            }
        }
        //    主线
        // if (vNetData.MapID <= 2)
        // {
        //     int chapter = (int)vNetData.FuncLineID / 10000;
        //     int wave = (int)(vNetData.MapID == 1 ? vNetData.FuncLineID % 100 : 1);//第几波怪
        //     int level = (int)(vNetData.FuncLineID % 10000) / 100;
            JsonData extendData = JsonMapper.ToObject(vNetData.Msg);
        //     BattleManager.Instance.CreateStoryBattle(chapter, wave, level, extendData, redTeamList[0], blueTeamList[0]);
        // }
        string guid = BattleManager.Instance.GetGUID(vNetPack.packUID);
        BattleManager.Instance.CreateBattleField(guid, (int)vNetData.MapID, (int)vNetData.FuncLineID, extendData, redTeamList, blueTeamList);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB425_tagSCTurnFightReportSign.cs
@@ -7,5 +7,15 @@
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        HB425_tagSCTurnFightReportSign vNetData = vNetPack as HB425_tagSCTurnFightReportSign;
        //    截断 PackageRegedit Distribute 里的封包
        //    直到sign = 1;
    }
    public static bool IsCorrectType(GameNetPackBasic vNetPack)
    {
        return vNetPack is HB425_tagSCTurnFightReportSign;
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB430_tagSCTurnFightReport.cs
@@ -1,11 +1,79 @@
using UnityEngine;
using System.Collections;
using System;
// B4 30 查看战报结果 #tagSCTurnFightReportRet
public class DTCB430_tagSCTurnFightReport : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
    private static GameNetEncode encoder = new GameNetEncode();
    static byte[] vCmdBytes = new byte[2];
    public override void Done(GameNetPackBasic vNetPack)
    {
        base.Done(vNetPack);
        HB430_tagSCTurnFightReport vNetData = vNetPack as HB430_tagSCTurnFightReport;
        // 战报结果
        string guid = vNetData.GUID;
        try
        {
            byte[] reportBytes = GameNetPackBasic.StrEncoding.GetBytes(vNetData.Report);
            int vReadIndex = 0;
            byte[] vPackBytes;
            int vLeavingLeng = 0;
            int vBodyLeng = 0;
            int vTotalLeng = reportBytes.Length;
            while (vReadIndex < vTotalLeng)
            {
                if (vLeavingLeng < 6)
                {
                    Debug.LogError("DTCB430_tagSCTurnFightReport: vLeavingLeng < 6 解包失败");
                    break;
                }
                vBodyLeng = BitConverter.ToInt32(reportBytes, vReadIndex + 2);
                if (vBodyLeng > vLeavingLeng - 6)// 未完整的包 报错
                {
                    Debug.LogError("DTCB430_tagSCTurnFightReport: vBodyLeng > vLeavingLeng - 6 解包失败");
                    break;
                }
                vPackBytes = new byte[vBodyLeng];
                Array.Copy(reportBytes, vReadIndex + 6, vPackBytes, 0, vBodyLeng);
                vPackBytes = encoder.BaseXorSub(vPackBytes);
                Array.Copy(vPackBytes, 0, vCmdBytes, 0, 2);
                var cmd = (ushort)((ushort)(vCmdBytes[0] << 8) + vCmdBytes[1]);
                bool isRegist = false;  // 未注册封包处理
                if (PackageRegedit.Contain(cmd))
                {
                    GameNetPackBasic vNetpack = PackageRegedit.TransPack(ServerType.Main, cmd, vPackBytes);
                    if (vNetpack != null)
                    {
                        BattleManager.Instance.PushPackage(guid, vNetpack);
                        isRegist = true;
                    }
                }
                vReadIndex += 6 + vBodyLeng;
                // 未注册封包处理
                if (!isRegist)
                {
#if UNITY_EDITOR
                    PackageRegedit.TransPack(ServerType.Main, cmd, vPackBytes);
#endif
                }
            }
        }
        catch (Exception e)
        {
            Debug.LogErrorFormat("DTCB430_tagSCTurnFightReport 解包异常:{0}", e);
        }
    }
}
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
@@ -1,8 +1,6 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
//using ILCrossBinding;
public class PackageRegedit
{
@@ -29,7 +27,7 @@
    public static void Init()
    {
        // 登记相应的数据体及对应的数据转逻辑类
        // 登记相应的数据体及对应的数据转逻辑类(去重后)
        Register(typeof(H0102_tagCDBPlayer), typeof(DTC0102_tagCDBPlayer));
        Register(typeof(HA004_tagServerDateTime), typeof(DTCA004_tagServerDateTime));
        Register(typeof(HA005_tagOpenServerDay), typeof(DTCA005_tagOpenServerDay));
@@ -94,11 +92,17 @@
        Register(typeof(HA921_tagUpdatePlayerNameResult), typeof(DTCA921_tagUpdatePlayerNameResult));
        Register(typeof(H3202_tagServerResponse), typeof(DTC3202_tagServerResponse));
        Register(typeof(HA130_tagMCViewBillboardRet), typeof(DTCA130_tagMCViewBillboardRet));
        Register(typeof(HB420_tagMCTurnFightState), typeof(DTCB420_tagMCTurnFightState));
        Register(typeof(HB421_tagMCTurnFightObjAction), typeof(DTCB421_tagMCTurnFightObjAction));
        Register(typeof(HB422_tagMCTurnFightObjDead), typeof(DTCB422_tagMCTurnFightObjDead));
        Register(typeof(HB423_tagMCTurnFightObjReborn), typeof(DTCB423_tagMCTurnFightObjReborn));
        Register(typeof(HB424_tagSCTurnFightInit), typeof(DTCB424_tagSCTurnFightInit));
        Register(typeof(HB425_tagSCTurnFightReportSign), typeof(DTCB425_tagSCTurnFightReportSign));
        Register(typeof(HB430_tagSCTurnFightReport), typeof(DTCB430_tagSCTurnFightReport));
        Register(typeof(H0407_tagNPCDisappear), typeof(DTC0407_tagNPCDisappear));
        Register(typeof(H0423_tagObjPropertyRefreshView), typeof(DTC0423_tagObjPropertyRefreshView));
        Register(typeof(H0604_tagUseSkillAttack), typeof(DTC0604_tagUseSkillAttack));
    }
    //主工程注册封包
    public static void Register(Type _pack, Type _business)
@@ -116,8 +120,6 @@
        }
    }
    public static bool Contain(ushort _cmd)
    {
        return packToBusinessTable.ContainsKey(_cmd);
@@ -127,8 +129,9 @@
        try
        {
            //执行主工程的封包
            if (packToBusinessTable.ContainsKey(_package.cmd))
            if (packToBusinessTable.ContainsKey(_package.cmd) && BattleManager.Instance.IsCanDistributePackage(_package))
                packToBusinessTable[_package.cmd].Done(_package);
        }
        catch (Exception ex)
        {
@@ -178,7 +181,4 @@
        }
        return null;
    }
}
}
Main/Core/NetworkPackage/GameNetPackBasic.cs
@@ -8,6 +8,21 @@
public class GameNetPackBasic
{
    static ulong AutoIncreaseUID = 0;
    public ulong packUID
    {
        get;
        private set;
    }
    public GameNetPackBasic()
    {
        packUID = AutoIncreaseUID++;
    }
    public ServerType socketType = ServerType.Main; //默认且唯一
    /** 协议号 */
Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs
File was deleted
Main/System/Battle/BattleEffectMgr.cs
New file
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using UnityEngine;
public class BattleEffectMgr : MonoBehaviour
{
    private BattleField battleField;
    private Dictionary<int, List<EffectPlayer>> effectDict = new Dictionary<int, List<EffectPlayer>>();
    public void Init(BattleField _battleField)
    {
        battleField = _battleField;
    }
    public void PauseGame()
    {
        foreach (KeyValuePair<int, List<EffectPlayer>> kvPair in effectDict)
        {
            foreach (EffectPlayer effectPlayer in kvPair.Value)
            {
                if (effectPlayer != null)
                {
                    effectPlayer.Pause();
                }
            }
        }
    }
    public void ResumeGame()
    {
        foreach (KeyValuePair<int, List<EffectPlayer>> kvPair in effectDict)
        {
            foreach (EffectPlayer effectPlayer in kvPair.Value)
            {
                if (effectPlayer != null)
                {
                    effectPlayer.Resume();
                }
            }
        }
    }
    public EffectPlayer PlayEffect(int ObjID, int effectId, Transform parent)
    {
        if (!effectDict.ContainsKey(effectId))
        {
            effectDict[effectId] = new List<EffectPlayer>();
        }
        EffectPlayer effectPlayer = EffectPlayer.Create(effectId, parent, true, false);
        effectPlayer.onDestroy = OnEffectDestroy;
        if (effectPlayer != null)
        {
            effectDict[effectId].Add(effectPlayer);
            effectPlayer.Play();
        }
        return effectPlayer;
    }
    public void RemoveEffect(int effectId, EffectPlayer effectPlayer)
    {
        List<EffectPlayer> effectPlayers = null;
        if (effectDict.TryGetValue(effectId, out effectPlayers))
        {
            effectPlayers.Remove(effectPlayer);
            GameObject.DestroyImmediate(effectPlayer.gameObject);
            effectPlayer = null;
        }
        else
        {
            Debug.LogError("could not find effect in list, effectid : " + effectId);
        }
    }
    protected void OnEffectDestroy(EffectPlayer effectPlayer)
    {
        if (effectDict.ContainsKey(effectPlayer.effectId))
        {
            effectDict[effectPlayer.effectId].Remove(effectPlayer);
            if (effectDict[effectPlayer.effectId].Count == 0)
            {
                effectDict.Remove(effectPlayer.effectId);
            }
        }
    }
}
Main/System/Battle/BattleEffectMgr.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/BattleEffectMgr.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 3b551bfee6a101744995645eecf86bb8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/BattleField/BattleField.cs
@@ -1,16 +1,30 @@
using System.Collections.Generic;
using UnityEngine;
using System;
using LitJson;
using DG.Tweening;
public class BattleField
{
    public BattleObjMgr battleObjMgr;
    public BattleEffectMgr battleEffectMgr;
    public BattleTweenMgr battleTweenMgr;
    public RecordPlayer recordPlayer;
    public IOperationAgent operationAgent;
    public int round = 0;
    public string guid = string.Empty;
    public int MapID = 0;
    public int FuncLineID = 0;
    public JsonData extendData;
    public bool IsActive
    {
@@ -63,15 +77,45 @@
    public Action<bool> OnBattlePause;
    public virtual void Init(TeamBase _redTeam, TeamBase _blueTeam)
    protected List<TeamBase> redTeamList = null;
    protected List<TeamBase> blueTeamList = null;
    protected int redTeamIndex = 0;
    protected int blueTeamIndex = 0;
    public BattleField(string _guid)
    {
        guid = _guid;
    }
    public virtual void Init(int _MapID, int _FuncLineID, JsonData _extendData,
        List<TeamBase> _redTeamList, List<TeamBase> _blueTeamList)
    {
        MapID = _MapID;
        redTeamList = _redTeamList;
        blueTeamList = _blueTeamList;
        FuncLineID = _FuncLineID;
        extendData = _extendData;
        redTeamIndex = 0;
        blueTeamIndex = 0;
        GameObject go = ResManager.Instance.LoadAsset<GameObject>("Battle/Prefabs", "BattleRootNode");
        GameObject battleRootNodeGO = GameObject.Instantiate(go);
        battleRootNode = battleRootNodeGO.GetComponent<BattleRootNode>();
        battleRootNodeGO.name = this.GetType().Name;
        battleObjMgr = new BattleObjMgr();
        battleObjMgr.Init(this, _redTeam, _blueTeam);
        battleObjMgr.Init(this, redTeamList[redTeamIndex], blueTeamList[blueTeamIndex]);
        battleEffectMgr = new BattleEffectMgr();
        battleEffectMgr.Init(this);
        battleTweenMgr = new BattleTweenMgr();
        battleTweenMgr.Init(this);
        //  这里的Init交给各个子类的Init里去实现
        // battleObjMgr.Init(this, _redTeam, _blueTeam);
        recordPlayer = new RecordPlayer();
        recordPlayer.Init(this);
@@ -148,6 +192,8 @@
    {
        battleObjMgr.ResumeGame();
        recordPlayer.ResumeGame();
        battleEffectMgr.ResumeGame();
        battleTweenMgr.ResumeGame();
    }
    protected virtual void PauseGame()
@@ -156,8 +202,89 @@
        battleObjMgr.PauseGame();
        recordPlayer.PauseGame();
        battleEffectMgr.PauseGame();
        battleTweenMgr.PauseGame();
    }
    public virtual void TurnFightState(int TurnNum, int State,
        uint FuncLineID, JsonData extendData)
    {
        round = TurnNum;
    }
    public virtual void OnTurnFightObjAction(int turnNum, int ObjID)
    {
    }
    public virtual void OnTurnFightState(int turnNum, int State, int FuncLineID, JsonData extendData)
    {
    }
    public void ObjInfoRefresh(H0418_tagObjInfoRefresh _refreshInfo)
    {
        BattleObject battleObj = battleObjMgr.GetBattleObject((int)_refreshInfo.ObjID);
        if (null != battleObj)
        {
            battleObj.OnObjInfoRefresh(_refreshInfo);
        }
    }
    public void ObjPropertyRefreshView(H0423_tagObjPropertyRefreshView vNetData)
    {
    }
    public virtual void OnObjDead(int ObjID)
    {
        DeathRecordAction recordAction = new DeathRecordAction(this, battleObjMgr.GetBattleObject(ObjID));
        recordPlayer.PlayRecord(recordAction);
    }
    public virtual void Destroy()
    {
        //  销毁全部内容
    }
    public void FinishBattleInAdvance(uint[] ObjIDArr)
    {
        //  让npc隐藏后 左边播放睡觉动作
    }
    public virtual void ProcessUseSkillAttack(H0604_tagUseSkillAttack vNetData)
    {
        // H0604_tagUseSkillAttack
        // public uint ObjID;
        // public byte ObjType;
        // public byte BattleType;    //物理/魔法
        // public ushort SkillID;
        // public uint AttackID;    //主攻击目标
        // public byte AttackObjType;    //主攻击目标
        // public ushort HurtCount;    //伤害数目
        // public  tagSkillHurtObj[] HurtList;    //size = HurtCount
        // ObjType类型的ObjID使用BattleType类技能SkillID攻击了AttackObjType类型的AttackID 伤害数字是HurtList
        // SkillAction skillAction = new SkillAction();
    }
    public RectTransform GetTeamNode(BattleCamp battleCamp)
    {
        if (battleCamp == BattleCamp.Red)
        {
            return battleRootNode.redTeamNode;
        }
        else
        {
            return battleRootNode.blueTeamNode;
        }
    }
    public bool IsRoundReachLimit()
    {
        // return round > xxx;
Main/System/Battle/BattleField/BattleRootNode.cs
@@ -4,11 +4,26 @@
public class BattleRootNode : MonoBehaviour
{
    public RectTransform redTeamNode;
    public List<GameObject> redTeamNodeList = new List<GameObject>();
    public RectTransform blueTeamNode;
    public List<GameObject> blueTeamNodeList = new List<GameObject>();
    public RawImage imgBackground;
    void Awake()
    {
        if (redTeamNodeList.Count != TeamConst.MaxTeamSlotCount)
        {
            Debug.LogError("redTeamNodeList count is not equal to MaxTeamSlotCount: " + redTeamNodeList.Count);
        }
        if (blueTeamNodeList.Count != TeamConst.MaxTeamSlotCount)
        {
            Debug.LogError("blueTeamNodeList count is not equal to MaxTeamSlotCount: " + blueTeamNodeList.Count);
        }
        DontDestroyOnLoad(gameObject);
    }
}
Main/System/Battle/BattleField/RecordActions/DeathRecordAction.cs
New file
@@ -0,0 +1,35 @@
using UnityEngine;
using System.Collections.Generic;
public class DeathRecordAction : RecordAction
{
    public DeathRecordAction(BattleField _battleField, BattleObject _battleObj)
        : base(RecordActionType.Death, _battleField, _battleObj)
    {
        isFinish = false;
        isRunOnce = false;
    }
    public override bool IsFinished()
    {
        return isFinish;
    }
    public override void Run()
    {
        if (isRunOnce)
        {
            return;
        }
        base.Run();
        isRunOnce = true;
        battleObject.OnDeath(OnDeathAnimationEnd);
    }
    private void OnDeathAnimationEnd()
    {
        isFinish = true;
    }
}
Main/System/Battle/BattleField/RecordActions/DeathRecordAction.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/BattleField/RecordActions/DeathRecordAction.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 4db9e665fb4537244ad07b87755fbbc2
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/BattleField/RecordActions/EditorSkillRecordAction.cs
New file
@@ -0,0 +1,89 @@
// #if UNITY_EDITOR
// using UnityEngine;
// using System.Collections.Generic;
// public class EditorSkillRecordAction : RecordAction
// {
//     public int SkillId;
//     public SkillConfig skillConfig;
//     protected SkillBase skillBase;
//     protected List<Dictionary<int, List<int>>> damageList = new List<Dictionary<int, List<int>>>();
//     private bool isCast = false;
//     public EditorSkillRecordAction(int _SkillId, BattleField _battleField, BattleObject _battleObj)
//         : base(RecordActionType.Skill, _battleField, _battleObj)
//     {
//         SkillId = _SkillId;
//         skillConfig = SkillConfig.Get(SkillId);
//         if (null == skillConfig)
//         {
//             Debug.LogError("找不到技能配置 " + SkillId);
//         }
//         skillBase = SkillFactory.CreateSkill(skillConfig);
//         // _battleObj使用了SkillId的技能
//         damageList = skillBase.GetDamageList(battleObject, battleField);
//     }
//     public EditorSkillRecordAction(int _SkillId, BattleField _battleField, BattleObject _battleObj, List<Dictionary<int, List<int>>> _damageList)
//         : base(RecordActionType.Skill, _battleField, _battleObj)
//     {
//         SkillId = _SkillId;
//         skillConfig = SkillConfig.Get(SkillId);
//         if (null == skillConfig)
//         {
//             Debug.LogError("找不到技能配置 " + SkillId);
//         }
//         skillBase = SkillFactory.CreateSkill(skillConfig);
//         damageList = _damageList;
//         // _battleObj使用了SkillId的技能
//     }
//     public override bool IsFinished()
//     {
//         return skillBase.IsFinished();
//     }
//     public override void Run()
//     {
//         base.Run();
//         if (null == skillBase)
//         {
//             return;
//         }
//         if (!skillBase.IsFinished())
//         {
//             skillBase.Run();
//         }
//         if (isCast)
//             return;
//         if (damageList.Count > 0)
//         {
//             skillBase.Cast(battleObject, battleField, damageList);
//         }
//         else
//         {
//             skillBase.ForceFinished();
//         }
//         isCast = true;
//     }
// }
// #endif
Main/System/Battle/BattleField/RecordActions/EditorSkillRecordAction.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/BattleField/RecordActions/EditorSkillRecordAction.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: eb0ee7684ec97d5479a7bf8d36a1673c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/BattleField/RecordActions/SkillRecordAction.cs
@@ -3,51 +3,14 @@
public class SkillRecordAction : RecordAction
{
    public int SkillId;
    public SkillConfig skillConfig;
    protected SkillBase skillBase;
    protected List<Dictionary<int, List<int>>> damageList = new List<Dictionary<int, List<int>>>();
    private bool isCast = false;
    public SkillRecordAction(int _SkillId, BattleField _battleField, BattleObject _battleObj)
        : base(RecordActionType.Skill, _battleField, _battleObj)
    public SkillRecordAction(BattleField _battleField, BattleObject _caster, H0604_tagUseSkillAttack vNetData)
        : base(RecordActionType.Skill, _battleField, _caster)
    {
        SkillId = _SkillId;
        skillConfig = SkillConfig.Get(SkillId);
        if (null == skillConfig)
        {
            Debug.LogError("找不到技能配置 " + SkillId);
        }
        skillBase = SkillFactory.CreateSkill(skillConfig);
        // _battleObj使用了SkillId的技能
        damageList = skillBase.GetDamageList(battleObject, battleField);
    }
    public SkillRecordAction(int _SkillId, BattleField _battleField, BattleObject _battleObj, List<Dictionary<int, List<int>>> _damageList)
        : base(RecordActionType.Skill, _battleField, _battleObj)
    {
        SkillId = _SkillId;
        skillConfig = SkillConfig.Get(SkillId);
        if (null == skillConfig)
        {
            Debug.LogError("找不到技能配置 " + SkillId);
        }
        skillBase = SkillFactory.CreateSkill(skillConfig);
        damageList = _damageList;
        // _battleObj使用了SkillId的技能
        skillBase = SkillFactory.CreateSkill(_caster, vNetData, _battleField);
    }
    public override bool IsFinished()
@@ -73,14 +36,7 @@
        if (isCast)
            return;
        if (damageList.Count > 0)
        {
            skillBase.Cast(battleObject, battleField, damageList);
        }
        else
        {
            skillBase.ForceFinished();
        }
        skillBase.Cast();
        isCast = true;
    }
Main/System/Battle/BattleField/StoryBattleField.cs
@@ -1,13 +1,43 @@
using System;
using LitJson;
using UnityEngine;
using System.Collections.Generic;
public class StoryBattleField : BattleField
{
    public override void Init(TeamBase _redTeam, TeamBase _blueTeam)
    {
        base.Init(_redTeam, _blueTeam);
    protected int chapter;//   章节
    protected int wave;//  波数
    protected int level;// 关卡
    protected JsonData extendData;
    protected MainChapterConfig chapterConfig;
    protected MainLevelConfig levelConfig;
    public StoryBattleField() : base(string.Empty)
    {
    }
    public override void Init(int MapID, int FuncLineID, JsonData _extendData,
        List<TeamBase> _redTeamList, List<TeamBase> _blueTeamList)
    {
        base.Init(MapID, FuncLineID, extendData, _redTeamList, _blueTeamList);
        LoadBattleMode();
        chapter = FuncLineID / 10000;
        wave = MapID == 1 ? FuncLineID % 100 : 1;//第几波怪
        level = (FuncLineID % 10000) / 100;
        extendData = _extendData;
        chapterConfig = MainChapterConfig.Get(chapter);
        levelConfig = MainLevelConfig.Get(level);
    }
    protected void LoadBattleMode()
    {
        string savedStr = LocalSave.GetString("StoryBattleFieldBattleMode");
        if (string.IsNullOrEmpty(savedStr))
        {
@@ -16,6 +46,43 @@
        SetBattleMode((BattleMode)Enum.Parse(typeof(BattleMode), savedStr));
    }
    public override void TurnFightState(int TurnNum, int State,
        uint FuncLineID, JsonData extendData)
    {
        base.TurnFightState(TurnNum, State, FuncLineID, extendData);
        switch (State)
        {
            //  起始状态标记
            case 0:
                break;
            case 1://准备完毕
                break;
            case 2://战斗中
                break;
            case 3://战斗结束
                break;
            case 4://结算奖励
                break;
            case 5://结束状态标记
                break;
            default:
                Debug.LogError("recieve a unknown State");
                break;
        }
    }
    public override void OnTurnFightObjAction(int turnNum, int ObjID)
    {
        base.OnTurnFightObjAction(turnNum, ObjID);
    }
    public override void OnTurnFightState(int turnNum, int State, int FuncLineID, JsonData extendData)
    {
        base.OnTurnFightState(turnNum, State, FuncLineID, extendData);
    }
    // public override void Run()
    // {
    //     //  一定要记住这个
Main/System/Battle/BattleFieldFactory.cs
New file
@@ -0,0 +1,29 @@
using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
public static class BattleFieldFactory
{
    public static BattleField CreateBattleField(string guid, int MapID, int FuncLineID, JsonData extendData, List<TeamBase> redTeamList, List<TeamBase> blueTeamList)
    {
        BattleField battleField = null;
        switch (MapID)
        {
            case 1:
            case 2:
                battleField = new StoryBattleField();
                break;
            default:
                break;
        }
        return battleField;
    }
}
Main/System/Battle/BattleFieldFactory.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/BattleFieldFactory.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 04ec4d20225cf8e4a8e9abfccf38e154
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/BattleManager.cs
@@ -1,57 +1,213 @@
using System.Collections.Generic;
using UnityEngine;
using LitJson;
public class BattleManager : GameSystemManager<BattleManager>
{
    public StoryBattleField storyBattleField = null;
    protected Dictionary<int, BattleField> battleFields = new Dictionary<int, BattleField>();
    //  同时只能有一场战斗在进行 guid, battlefield
    protected Dictionary<string, BattleField> battleFields = new Dictionary<string, BattleField>();
    protected LogicUpdate logicUpdate = new LogicUpdate();
    public override void Init()
    {
        base.Init();
        StartStoryBattle();
        // StartStoryBattle();
        logicUpdate.Start(Run);
    }
    public override void Release()
    {
        Debug.LogError("who release this one");
        base.Release();
        logicUpdate.Destroy();
    }
    public void StartStoryBattle()
    public void CreateStoryBattle(int MapID, int FuncLineID, JsonData extendData, List<TeamBase> redTeamList, List<TeamBase> blueTeamList)
    {
        if (null == storyBattleField)
        {
            storyBattleField = new StoryBattleField();
        }
            TeamBase redTeam = GetStoryTeam();//TeamManager.Instance.GetTeam(TeamType.Story);
            TeamBase blueTeam = GetStoryTeam();
            storyBattleField.Init(/*这里要加个创建背景的*/redTeam, blueTeam);
            // storyBattleField.Start();
            storyBattleField.battleRootNode.transform.SetParent(Launch.Instance.transform);
        storyBattleField.Init(MapID, FuncLineID, extendData, redTeamList, blueTeamList);
    }
    public void OnBattleClose(BattleField _battleField)
    {
    }
#region 截断网络派发包 只收入当前包的后续
    private bool allow = true;
    private Queue<GameNetPackBasic> packQueue = new Queue<GameNetPackBasic>();
    public bool IsCanDistributePackage(GameNetPackBasic _package)
    {
        if (_package is HB425_tagSCTurnFightReportSign)
        {
            HB425_tagSCTurnFightReportSign pkg = _package as HB425_tagSCTurnFightReportSign;
            // 0-战报片段开始;1-战报片段结束;
            if (pkg.Sign == 0)
            {
                allow = false;
            }
            else
            {
                allow = true;
            }
        }
        else
        {
            if (!allow)
            {
                packQueue.Enqueue(_package);
            }
        }
        return allow;
    }
    public bool DistributeNextPackage()
    {
        if (packQueue.Count > 0)
        {
            GameNetPackBasic pack = packQueue.Dequeue();
            PackageRegedit.Distribute(pack);
            return true;
        }
        else
        {
            return false;
        }
    }
    private TeamBase GetStoryTeam()
    public void OnConnected()
    {
        TeamBase teamBase = new TeamBase();
        #if UNITY_EDITOR
        teamBase.FillWithFakeData();
        #else
        //  YYL TODO
        //  根据配表塞英雄 后面可能还要塞其他东西 先放在这里反正 做主线了再转移
        #endif
        if (!allow)
        {
            allow = true;
            packQueue.Clear();
        return teamBase;
            //  重新发送要上一组战斗包的请求
            //  TODO YYL
        }
    }
#endregion
#region 战报部分
    protected Dictionary<string, Queue<GameNetPackBasic>> battleReportDict = new Dictionary<string, Queue<GameNetPackBasic>>();
    protected Dictionary<string, List<ulong>> battlePackRelationList = new Dictionary<string, List<ulong>>();
    public void PushPackage(string guid, GameNetPackBasic vNetPack)
    {
        Queue<GameNetPackBasic> queue = null;
        if (!battleReportDict.TryGetValue(guid, out queue))
        {
            queue = new Queue<GameNetPackBasic>();
        }
        queue.Enqueue(vNetPack);
        List<ulong> uidList = null;
        if (!battlePackRelationList.TryGetValue(guid, out uidList))
        {
            uidList = new List<ulong>();
        }
        uidList.Add(vNetPack.packUID);
    }
    public BattleField GetBattleField(ulong packUID)
    {
        return GetBattleField(GetGUID(packUID));
    }
    public BattleField GetBattleField(string guid)
    {
        BattleField battleField = null;
        battleFields.TryGetValue(guid, out battleField);
        if (battleField == null)
        {
            battleField = storyBattleField;
        }
        return battleField;
    }
    public string GetGUID(ulong packUID)
    {
        foreach (var kv in battlePackRelationList)
        {
            if (kv.Value.Contains(packUID))
            {
                return kv.Key;
            }
        }
        return string.Empty;
    }
    public void DistributeNextReportPackage(string guid)
    {
        Queue<GameNetPackBasic> queue = null;
        if (!battleReportDict.TryGetValue(guid, out queue))
        {
            Debug.LogError("DistributeNextReportPackage could not find queue for guid : " + guid);
            return;
        }
        PackageRegedit.Distribute(queue.Dequeue());
        if (queue.Count <= 0)
        {
            battleReportDict.Remove(guid);
            battlePackRelationList.Remove(guid);
        }
    }
#endregion
    public BattleField CreateBattleField(string guid, int MapID, int FuncLineID, JsonData extendData, List<TeamBase> redTeamList, List<TeamBase> blueTeamList)
    {
        BattleField battleField = null;
        if (battleFields.TryGetValue(guid, out battleField))
        {
            Debug.LogError("战场已存在 先进行销毁");
            battleField.Destroy();
        }
        battleField = BattleFieldFactory.CreateBattleField(guid, MapID, FuncLineID, extendData, redTeamList, blueTeamList);
        battleFields.Add(guid, battleField);
        if (string.Empty == guid)
        {
            storyBattleField = battleField as StoryBattleField;
        }
        return battleField;
    }
    // public void OnTurnFightObjAction(battleType, vNetData.TurnNum, (int)vNetData.ObjID)
    public void Run()
    {
        if (null != storyBattleField)
Main/System/Battle/BattleObject/BattleObjMgr.cs
@@ -21,7 +21,8 @@
        battleField = _battleField;
        CreateTeam(battleField.battleRootNode.redTeamNodeList, redCampDict, _redTeam, BattleCamp.Red);
        CreateTeam(battleField.battleRootNode.blueTeamNodeList, blueCampDict, _blueTeam, BattleCamp.Blue);
    }
    }
    protected void CreateTeam(List<GameObject> posNodeList, Dictionary<int, BattleObject> campDict, TeamBase teamBase, BattleCamp _Camp)
    {
@@ -32,19 +33,34 @@
            if (teamHero != null)
            {
                BattleObject battleObj = BattleObjectFactory.CreateBattleObject(battleField, posNodeList, teamHero, _Camp);
                allBattleObjDict.Add(battleObj.BattleObjectId, battleObj);
                campDict.Add(teamHero.heroIndex, battleObj);
                allBattleObjDict.Add(battleObj.ObjID, battleObj);
                campDict.Add(teamHero.positionNum, battleObj);
            }
        }
    }
    
    public BattleObject GetBattleObject(int battleObjId)
    public BattleObject GetBattleObject(int objId)
    {
        if (allBattleObjDict.TryGetValue(battleObjId, out BattleObject battleObj))
        if (allBattleObjDict.TryGetValue(objId, out BattleObject battleObj))
        {
            return battleObj;
        }
        return null;
    }
    public List<BattleObject> GetBattleObjList(H0604_tagUseSkillAttack tagUseSkillAttack)
    {
        List<BattleObject> retList = new List<BattleObject>();
        foreach (var hurt in tagUseSkillAttack.HurtList)
        {
            BattleObject obj = GetBattleObject((int)hurt.ObjID);
            if (null != obj)
            {
                retList.Add(obj);
            }
        }
        return retList;
    }
    protected void DestroyTeam(Dictionary<int, BattleObject> campDict)
@@ -54,7 +70,7 @@
            BattleObject battleObj = item.Value;
            if (battleObj != null)
            {
                allBattleObjDict.Remove(battleObj.BattleObjectId);
                allBattleObjDict.Remove(battleObj.ObjID);
                BattleObjectFactory.DestroyBattleObject(item.Key, battleObj);
            }
        }
@@ -127,7 +143,7 @@
        return blueCampList;
    }
#if UNITY_EDITOR
#if UNITY_EDITOR_STOP_USING
    public void ReviveAll()
    {
        foreach (var kv in allBattleObjDict)
@@ -168,7 +184,7 @@
                //暂时没有召唤物
                // 放在第7格的BOSS后排的。。位置放在正中间
                // a)前排,1、2、3号为前排,前排全部阵亡后,4、5、6号即是前排也是后排 7其实也是后排
                List<BattleObject> frontList = new List<BattleObject>(from BO in returnList where BO.teamHero.heroIndex < 3 && !BO.IsDead() select BO);
                List<BattleObject> frontList = new List<BattleObject>(from BO in returnList where BO.teamHero.positionNum < 3 && !BO.IsDead() select BO);
                if (frontList.Count == 0)
                {
                    frontList.AddRange(returnList);
@@ -176,7 +192,7 @@
                returnList = frontList;                
                break;
            case SkillTargetRangeType.Back:
                List<BattleObject> backList = new List<BattleObject>(from BO in returnList where BO.teamHero.heroIndex >= 3 && !BO.IsDead() select BO);
                List<BattleObject> backList = new List<BattleObject>(from BO in returnList where BO.teamHero.positionNum >= 3 && !BO.IsDead() select BO);
                if (backList.Count == 0)
                {
                    backList.AddRange(returnList);
Main/System/Battle/BattleObject/BattleObject.cs
@@ -30,7 +30,9 @@
{
    public BattleField battleField;
    public int BattleObjectId { get; set; }
    public BattleObjectBuffMgr buffMgr;
    public int ObjID { get; set; }
    public BattleCamp Camp { get; protected set; }
@@ -38,13 +40,19 @@
    // public BuffMgr buffMgr;
    protected MotionBase motionBase;
    public MotionBase motionBase;
    public GameObject heroGo
    {
        get;
        private set;
    }
    protected Action onDeathAnimationComplete;
    protected Renderer[] renderers;
    public Transform effectNode;
    public BattleObject(BattleField _battleField)
    {
@@ -58,6 +66,11 @@
        Camp = _camp;
        motionBase = new MotionBase();
        motionBase.Init(heroGo.GetComponentInChildren<SkeletonGraphic>(true));
        motionBase.onAnimationComplete += OnAnimationComplete;
        buffMgr.Init(this);
        renderers = heroGo.GetComponentsInChildren<Renderer>(true);
    }
@@ -88,7 +101,27 @@
        motionBase.Release();
        motionBase = null;
        teamHero = null;
        BattleObjectId = 0;
        ObjID = 0;
        motionBase.onAnimationComplete -= OnAnimationComplete;
    }
    public void OnObjInfoRefresh(H0418_tagObjInfoRefresh _refreshInfo)
    {
        switch ((PlayerDataType)_refreshInfo.RefreshType)
        {
            case PlayerDataType.HP:
                teamHero.curHp = GeneralDefine.GetFactValue(_refreshInfo.Value, _refreshInfo.ValueEx);
                break;
            case PlayerDataType.MaxHP:
                teamHero.maxHp = GeneralDefine.GetFactValue(_refreshInfo.Value, _refreshInfo.ValueEx);
                break;
            case PlayerDataType.XP:
                teamHero.rage = (int)GeneralDefine.GetFactValue(_refreshInfo.Value, _refreshInfo.ValueEx);
                break;
            default:
                Debug.LogError("BattleObject.ObjInfoRefresh 出现意外类型 " + _refreshInfo.RefreshType.ToString());
                break;
        }
    }
    //  眩晕
@@ -108,12 +141,6 @@
    {
        return teamHero.isStoned;
    }
    // //   禁锢
    // public bool IsConfined()
    // {
    //     return false;
    // }
    //  被沉默
    public bool IsSlient()
@@ -180,32 +207,21 @@
        return true;
    }
    public virtual void TakeDamage(List<int> damageValues)
    public virtual void Hurt(List<long> damageValues, int attackType)
    {
        if (IsDead())
            return;
        PopDamage(damageValues);
        PopDamage(teamHero.curHp, damageValues, attackType);
        motionBase.PlayAnimation(MotionName.hit, false);
        //  计算伤害
        int totalDamage = 0;
        long totalDamage = 0;
        foreach (var damage in damageValues)
        {
            totalDamage += damage;
        }
        //  扣血
        teamHero.curHp -= totalDamage;
        //  其实这里应该是等服务器发death的action
        // if (IsDead())
        // {
        //     OnDeath();
        // }
    }
    //  闪避开始
@@ -213,8 +229,10 @@
    {
        float pingpongTime = 0.2f;
        RectTransform rectTrans = heroGo.GetComponent<RectTransform>();
        rectTrans.DOAnchorPos(new Vector3(-50, 50, 0), pingpongTime)
        var tween = rectTrans.DOAnchorPos(new Vector3(-50, 50, 0), pingpongTime)
            .SetEase(Ease.OutCubic);
        battleField.battleTweenMgr.OnPlayTween(tween);
    }
    //  闪避结束
@@ -222,21 +240,26 @@
    {
        float pingpongTime = 0.2f;
        RectTransform rectTrans = heroGo.GetComponent<RectTransform>();
        rectTrans.DOAnchorPos(Vector3.zero, pingpongTime)
        var tween = rectTrans.DOAnchorPos(Vector3.zero, pingpongTime)
                            .SetEase(Ease.OutCubic);
        battleField.battleTweenMgr.OnPlayTween(tween);
    }
    protected virtual void OnDeath()
    public virtual void OnDeath(Action _onDeathAnimationComplete)
    {
        motionBase.OnOtherAnimationComplete = OnOtherAnimationComplete;
        onDeathAnimationComplete = _onDeathAnimationComplete;
        motionBase.PlayAnimation(MotionName.dead, false);
    }
    protected virtual void OnOtherAnimationComplete(MotionName motionName)
    protected virtual void OnAnimationComplete(MotionName motionName)
    {
        if (motionName == MotionName.dead)
        {
            OnDeadAnimationComplete();
            onDeathAnimationComplete?.Invoke();
            onDeathAnimationComplete = null;
        }
    }
@@ -247,7 +270,7 @@
    }
     // 伤害还要看 是否闪避 暴击 and so on 需要有一个DamageType 服务器应该会给
    protected virtual void PopDamage(List<int> damageValues)
    protected virtual void PopDamage(long curHp, List<long> damageValues, int attackType)
    {
        //  其实应该通知出去给UI界面解耦 让UI界面自己来显示的 YYL TODO
        //  播放伤害数字
@@ -258,121 +281,20 @@
            Debug.Log($"Damage: {damage}");
        }
        EventBroadcast.Instance.Broadcast<BattleObject, List<int>>(EventName.BATTLE_DAMAGE_TAKEN, this, damageValues);
        EventBroadcast.Instance.Broadcast(EventName.BATTLE_DAMAGE_TAKEN, battleField.guid, this, damageValues);
    }
    public void PlaySkill(SkillConfig skillConfig, List<Dictionary<int, List<int>>> damageList, Action _onComplete)
    public RectTransform GetAliasTeamNode()
    {
        bool moveToTarget = true;
        if (moveToTarget)
        {
            int targetId = damageList[0].First().Key;
            BattleObject _targetObj = battleField.battleObjMgr.GetBattleObject(targetId);
            RectTransform selfRect = heroGo.GetComponent<RectTransform>();
            RectTransform targetRect = _targetObj.heroGo.GetComponent<RectTransform>();
            Vector2 curAnchoredPos = selfRect.anchoredPosition;
            MoveToTargetUI(selfRect, targetRect, new Vector2(100f, 0f), () =>
            {
                PlaySkillAnimation(skillConfig, damageList, () =>
                {
                    // 回到原位置
                    selfRect.DOAnchorPos(curAnchoredPos, 0.2f)
                        .SetEase(Ease.Linear)
                        .OnComplete(() => {
                            _onComplete?.Invoke();
                        });
                });
            });
        }
        else
        {
            PlaySkillAnimation(skillConfig, damageList, _onComplete);
        }
        return battleField.GetTeamNode(Camp);
    }
    protected void MoveToTargetUI(RectTransform selfRect, RectTransform targetRect, Vector2 offset, Action _onComplete)
    public RectTransform GetEnemyTeamNode()
    {
        // 1. 目标的本地坐标转为世界坐标
        Vector3 targetWorldPos = targetRect.TransformPoint(targetRect.anchoredPosition + offset);
        // 2. 世界坐标转为自己父节点下的本地坐标
        RectTransform parentRect = selfRect.parent as RectTransform;
        Vector2 targetAnchoredPos;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            parentRect,
            RectTransformUtility.WorldToScreenPoint(null, targetWorldPos),
            null,
            out targetAnchoredPos);
        // 3. DOTween 移动
        selfRect.DOAnchorPos(targetAnchoredPos, 0.2f)
            .SetEase(Ease.Linear)
            .OnComplete(() => _onComplete?.Invoke());
        return battleField.GetTeamNode(Camp == BattleCamp.Red ? BattleCamp.Blue : BattleCamp.Red);
    }
    protected void PlaySkillAnimation(SkillConfig skillConfig, List<Dictionary<int, List<int>>> damageList, Action _onComplete)
    {
        //  关键帧列表
        List<int> keyFrameList = new List<int>() { 15 };
        motionBase.OnAttackHitEvent = (int _frame) =>
        {
            Dictionary<int, List<int>> oneRoundDamage = damageList[keyFrameList.IndexOf(_frame)];
            foreach (var kvp in oneRoundDamage)
            {
                int targetId = kvp.Key;
                List<int> damageValues = kvp.Value;
                BattleObject targetObj = battleField.battleObjMgr.GetBattleObject(targetId);
                if (targetObj != null && !targetObj.IsDead())
                {
                    targetObj.TakeDamage(damageValues);
                }
            }
        };
        motionBase.OnAttackAnimationComplete = () =>
        {
            _onComplete?.Invoke();
            motionBase.OnAttackHitEvent = null;
            motionBase.OnAttackAnimationComplete = null;
            //  死亡确定其实不应该在这里进行触发 应该由服务器下发 YYL TODO
#if UNITY_EDITOR
            //  暂时的处理
            HashSet<int> hitTargets = new HashSet<int>();
            foreach (var dmgDict in damageList)
            {
                foreach (var kvp in dmgDict)
                {
                    int targetId = kvp.Key;
                    hitTargets.Add(targetId);
                }
            }
            foreach (int targetId in hitTargets)
            {
                BattleObject targetObj = battleField.battleObjMgr.GetBattleObject(targetId);
                if (targetObj != null && targetObj.IsDead())
                {
                    targetObj.OnDeath();
                }
            }
#endif
        };
        motionBase.PlayAnimationEx(MotionName.attack, false, keyFrameList);
    }
#if UNITY_EDITOR
#if UNITY_EDITOR_STOP_USING
    public void EditorRevive()
    {
        teamHero.curHp = 100;
Main/System/Battle/BattleObject/BattleObjectFactory.cs
@@ -2,20 +2,24 @@
using System.Collections.Generic;
using System;
using UnityEngine;
using Spine.Unity;
public class BattleObjectFactory
{
    //  本意是要在这里做池的内容的 但是想了一下 利用效率有点差 最多做一下红色方的缓存 蓝色方的即时用 即时删除 或者缓存上一次战斗的就行
    private static int AutoIncrementID = 100000;
    // private static int AutoIncrementID = 100000;
    public static BattleObject CreateBattleObject(BattleField _battleField, List<GameObject> posNodeList, TeamHero teamHero, BattleCamp _Camp)
    {
        HeroSkinConfig skinCfg = teamHero.heroInfo.skinConfig;
        HeroSkinConfig skinCfg = teamHero.skinConfig;
        GameObject battleGO = ResManager.Instance.LoadAsset<GameObject>("Hero/SpineRes", "Hero_001"/*skinCfg.SpineRes*/);
        GameObject goParent = posNodeList[teamHero.heroIndex];
        SkeletonGraphic skeletonGraphic = battleGO.GetComponent<SkeletonGraphic>();
        skeletonGraphic.skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("Hero/SpineRes/", skinCfg.SpineRes);
        skeletonGraphic.Initialize(true);
        GameObject goParent = posNodeList[teamHero.positionNum];
        BattleObject battleObject = new BattleObject(_battleField);
        battleObject.BattleObjectId = AutoIncrementID++;
        battleGO.name = battleObject.BattleObjectId.ToString();
        battleObject.ObjID = teamHero.ObjID;
        battleGO.name = battleObject.ObjID.ToString();
        battleObject.Init(GameObject.Instantiate(battleGO, goParent.transform), teamHero, _Camp);
        return battleObject;
    }
Main/System/Battle/BattleTweenMgr.cs
New file
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
public class BattleTweenMgr
{
    private BattleField battleField;
    private List<Tween> tweenList = new List<Tween>();
    public void Init(BattleField _battleField)
    {
        battleField = _battleField;
    }
    public void PauseGame()
    {
        foreach (var tween in tweenList)
        {
            if (tween != null && !tween.IsComplete())
            {
                tween.Pause();
            }
        }
    }
    public void ResumeGame()
    {
        foreach (var tween in tweenList)
        {
            if (tween != null && !tween.IsComplete())
            {
                tween.Play();
            }
        }
    }
    public void OnPlayTween(Tween tween)
    {
        if (tween == null || tween.IsComplete())
        {
            return;
        }
        tweenList.Add(tween);
        tween.OnComplete(() =>
        {
            tweenList.Remove(tween);
        });
        tween.Play();
    }
}
Main/System/Battle/BattleTweenMgr.cs.meta
File was renamed from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: ee889e8a605a1904599601b289691889
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/BattleUtility.cs
New file
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using DG.Tweening.Core;
public static class BattleUtility
{
    // 其他通用的战斗工具方法可以放在这里
    public static TweenerCore<Vector2, Vector2, DG.Tweening.Plugins.Options.VectorOptions> MoveToTarget(RectTransform transform, RectTransform target, Vector2 offset, float duration, Action onComplete = null)
    {
        Vector3 targetWorldPos = target.TransformPoint(target.anchoredPosition + offset);
        RectTransform parentRect = transform.parent as RectTransform;
        Vector2 targetAnchoredPos;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            parentRect,
            RectTransformUtility.WorldToScreenPoint(null, targetWorldPos),
            null,
            out targetAnchoredPos);
        // 3. DOTween 移动
        return transform.DOAnchorPos(targetAnchoredPos, duration)
            .SetEase(Ease.Linear)
            .OnComplete(() => onComplete?.Invoke());
    }
}
Main/System/Battle/BattleUtility.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/BattleUtility.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: f2c6020c5feefa04e8a7975071123f79
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Buff/BattleObjectBuffMgr.cs
New file
@@ -0,0 +1,39 @@
using System.Collections.Generic;
using UnityEngine;
public class BattleObjectBuffMgr
{
    // private Dictionary<BuffBase, EffectPlayer> buffDict = new Dictionary<BuffBase, EffectPlayer>();
    private BattleObject battleObject;
    public void Init(BattleObject _battleObject)
    {
        battleObject = _battleObject;
    }
    public void Release()
    {
    }
    //  增加buff
    public void AddBuff(H0605_tagObjAddBuff vNetData)
    {
        // BuffBase buffBase = BuffFactory.CreateBuff(vNetData, battleObject);
        // if (null == buffBase)
        // {
        //     return;
        // }
        // buffBase.OnAdd();
    }
    //  删除buff
    public void RemoveBuff(H0606_tagObjDelBuff vNetData)
    {
        // buffBase.OnRemove();
    }
}
Main/System/Battle/Buff/BattleObjectBuffMgr.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Buff/BattleObjectBuffMgr.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 1a6e63c004530d242982e4cdaddaa74d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Buff/BuffBase.cs
New file
@@ -0,0 +1,41 @@
using UnityEngine;
/// <summary>
/// Buff基类,所有Buff类型继承自此类
/// </summary>
public abstract class BuffBase
{
    // public int BuffId { get; protected set; }
    // public BattleObject Owner { get; protected set; }
    // public int DurationRound { get; protected set; } // 持续回合数
    // public int ElapsedRound { get; protected set; }  // 已经过的回合数
    protected H0605_tagObjAddBuff h0605_TagObjAddBuff;
    public BuffBase(H0605_tagObjAddBuff _h0605_TagObjAddBuff)
    {
        h0605_TagObjAddBuff = _h0605_TagObjAddBuff;
        // BuffId = buffId;
        // Owner = owner;
        // DurationRound = durationRound;
        // ElapsedRound = 0;
    }
    /// <summary>
    /// Buff添加时调用
    /// </summary>
    public virtual void OnAdd()
    {
    }
    /// <summary>
    /// Buff移除时调用
    /// </summary>
    public virtual void OnRemove()
    {
    }
}
Main/System/Battle/Buff/BuffBase.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Buff/BuffBase.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 6afb02269964eb748bbe21d946a0ef7a
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Buff/BuffFactory.cs
New file
@@ -0,0 +1,24 @@
using UnityEngine;
/// <summary>
/// Buff工厂,根据数据创建对应的Buff实例
/// </summary>
public static class BuffFactory
{
    public static BuffBase CreateBuff(H0605_tagObjAddBuff vNetData, BattleObject owner)
    {
        // int buffId = vNetData.buffId;
        // int durationRound = vNetData.durationRound;
        // 示例:根据buffId创建不同类型Buff
        // switch (buffId)
        // {
        //     // case 1: return new StunBuff(buffId, owner, durationRound);
        //     // case 2: return new PoisonBuff(buffId, owner, durationRound);
        //     // 继续添加其它具体Buff类型
        //     default:
        //         return null; // 未知buff不创建
        // }
        return null;
    }
}
Main/System/Battle/Buff/BuffFactory.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Buff/BuffFactory.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 6c080c000bb94344d94179aa6a0bfa3b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Bullet.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 115fa1b85e4a51a4d9869f53fca7c35c
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Battle/Bullet/Bullet.cs
New file
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using UnityEngine;
public class Bullet
{
    protected BattleObject caster;
    protected SkillConfig skillConfig;
    protected List<H0604_tagUseSkillAttack.tagSkillHurtObj> hurtList;
    protected BattleField battleField;
    protected Action onComplete;
    protected int curFrame = 0;
    public Bullet(BattleObject _caster, SkillConfig skillConfig,
        List<H0604_tagUseSkillAttack.tagSkillHurtObj> hurtList, BattleField battleField, Action _onComplete)
    {
        this.caster = _caster;
        this.skillConfig = skillConfig;
        this.hurtList = hurtList;
        this.battleField = battleField;
        this.onComplete = _onComplete;
    }
    public virtual void Start()
    {
    }
    public virtual void Run()
    {
    }
    protected virtual void PlayEffect(int effectId, SkillEffectAnchor target)
    {
        // 还需要SoAttackType来判断是怎样的特效
        switch (target)
        {
            //  特效先不管 TODO YYL 等特效重新写过一遍后再写
            case SkillEffectAnchor.Caster:
                // caster.PlayEffect(effectId);
                break;
            case SkillEffectAnchor.Target:
                // caster.teamHero.PlayEffectOnAllies(effectId);
                break;
            case SkillEffectAnchor.AlliesCenter:
                // caster.teamHero.PlayEffectOnAlliesCenter(effectId);
                break;
            case SkillEffectAnchor.EnemiesCenter:
                // caster.teamHero.PlayEffectOnEnemiesCenter(effectId);
                break;
            default:
                Debug.LogWarning($"Unhandled effect target: {target}");
                break;
        }
    }
    protected virtual void PlayAudio(int audioId)
    {
        SoundPlayer.Instance.PlayUIAudio(audioId);
    }
    protected virtual void ShakeScreen(int intensity)
    {
        // Implement screen shaking logic here
        Debug.Log($"Shaking screen with intensity: {intensity}");
    }
    protected virtual void PopDamage()
    {
    }
    protected virtual void OnSkillComplete()
    {
        onComplete?.Invoke();
    }
    protected virtual void PlaySkill(int skillId)
    {
    }
}
Main/System/Battle/Bullet/Bullet.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Bullet/Bullet.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 8d4d5668dea0ef64da48bab26e795fac
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Bullet/BulletFactory.cs
New file
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using UnityEngine;
public static class BulletFactory
{
    public static Bullet CreateBullet(BattleObject _caster, SkillConfig skillConfig, List<H0604_tagUseSkillAttack.tagSkillHurtObj> hurtList, BattleField battleField, Action _onComplete)
    {
        return null;
    }
}
Main/System/Battle/Bullet/BulletFactory.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Bullet/BulletFactory.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: b007b1040446daf4db9c2a14b6d25b1d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Bullet/CloseCombatBullet.cs
New file
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using UnityEngine;
//    近战攻击
public class CloseCombatBullet : Bullet
{
    protected int keyFrameIndex = 0;
    protected int frame = 0;
    public CloseCombatBullet(BattleObject _caster, SkillConfig _skillConfig, List<H0604_tagUseSkillAttack.tagSkillHurtObj> _hurtList, BattleField _battleField, Action _onComplete)
        : base(_caster, _skillConfig, _hurtList, _battleField, _onComplete)
    {
    }
    public override void Start()
    {
        base.Start();
        //    用skillconfig.xp来判断不保险
        MotionName motionName = GetMotionName(skillConfig.SkillMotionName);
        caster.motionBase.PlayAnimation(motionName, false);
    }
    public MotionName GetMotionName(string strMotionName)
    {
        return Enum.Parse<MotionName>(strMotionName);
    }
    protected override void OnSkillComplete()
    {
        foreach (var hurtObj in hurtList)
        {
            BattleObject targetObj = battleField.battleObjMgr.GetBattleObject((int)hurtObj.ObjID);
            if (targetObj != null)
            {
                long curHP = GeneralDefine.GetFactValue(hurtObj.CurHP, hurtObj.CurHPEx);
                targetObj.teamHero.curHp = curHP;
            }
        }
        base.OnSkillComplete();
    }
    protected override void PopDamage()
    {
        if (keyFrameIndex >= skillConfig.DamageDivide.Length)
        {
            Debug.LogError("CloseCombatBullet PopDamage keyFrameIndex out of range: " + keyFrameIndex);
            return;
        }
        int[] tenKDamagePercentArray = skillConfig.DamageDivide[keyFrameIndex++];
        foreach (var hurtObj in hurtList)
        {
            long factDamage = GeneralDefine.GetFactValue(hurtObj.HurtHP, hurtObj.HurtHPEx);
            List<long> damageList = new List<long>();
            for (int i = 0; i < tenKDamagePercentArray.Length; i++)
            {
                long damage = (long)(Mathf.Round(factDamage * tenKDamagePercentArray[i] / 10000.0f));
                damageList.Add(damage);
            }
            BattleObject targetObj = battleField.battleObjMgr.GetBattleObject((int)hurtObj.ObjID);
            if (targetObj != null)
            {
                //    播放受击动作
                targetObj.Hurt(damageList, hurtObj.AttackType);
            }
        }
    }
}
Main/System/Battle/Bullet/CloseCombatBullet.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Bullet/CloseCombatBullet.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: a21315aaaa6e8f6448f864298b4516c0
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Bullet/TrajectoryBullet.cs
Main/System/Battle/Bullet/TrajectoryBullet.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Bullet/TrajectoryBullet.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 3d40a22572b07c24e9eefb52db0bfd87
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Define/BattleType.cs
New file
@@ -0,0 +1,7 @@
public enum BattleType
{
    MainLevelMobs = 1,    //主线小怪
    MainLevelBoss = 2,    //主线boss
}
Main/System/Battle/Define/BattleType.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Define/BattleType.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: ca8a7ce9d3982ee40bec8c3ea869a461
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Define/PlayEffectType.cs
New file
@@ -0,0 +1,8 @@
public enum PlayEffectType
{
    None = 0,
    SkillStart,        // 技能开始
    AttackHit,         // 攻击命中
}
Main/System/Battle/Define/PlayEffectType.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Define/PlayEffectType.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: ae351bd17066a3040a5e423c69aa28eb
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Motion/MotionBase.cs
@@ -10,47 +10,19 @@
/// </summary>
public class MotionBase
{
    public class WaitingTask
    public static List<string> AttackMotionList = new List<string>
    {
        public List<int> triggerFrame;
        public MotionName motionName;
        public int currentFrame;
        public Action<MotionName, int> callback;
        MotionName.attack.ToString(),
        MotionName.angerSkill.ToString(),
        MotionName.passiveSkill.ToString(),
    };
        public WaitingTask(List<int> triggerFrame, MotionName motionName, Action<MotionName, int> _callback)
        {
            this.triggerFrame = new List<int>(triggerFrame);
            this.motionName = motionName;
            this.currentFrame = 0;
            this.callback = _callback;
        }
        public bool IsFinished()
        {
            return triggerFrame.Count <= 0;
        }
        public void Run()
        {
            if (triggerFrame.Count > 0)
            {
                currentFrame ++;
                if (currentFrame >= triggerFrame[0])
                {
                    int frame = triggerFrame[0];
                    triggerFrame.RemoveAt(0);
                    callback?.Invoke(motionName, frame);
                }
            }
        }
    }
    private Dictionary<Spine.TrackEntry, Action> trackEntryCompleteDict = new Dictionary<Spine.TrackEntry, Action>();
    // 动画事件
    public Action OnAttackAnimationComplete;
    public Action OnHitAnimationComplete;
    public Action<MotionName> OnOtherAnimationComplete;
    public Action<int> OnAttackHitEvent; //trigger index
    public Action<MotionName> onAnimationComplete;
    #region 组件引用
    
@@ -66,9 +38,6 @@
    protected float defaultMixDuration = 0f;
    
    #endregion
    protected List<WaitingTask> waitingTaskList = new List<WaitingTask>();
    protected List<WaitingTask> removeList = new List<WaitingTask>();
    private Spine.TrackEntry currentTrackEntry;
@@ -117,11 +86,8 @@
        skeletonGraphic = null;
        skeleton = null;
        currentTrackEntry = null;
        waitingTaskList.Clear();
        removeList.Clear();
    }
    #endregion
    #region 动画控制
@@ -131,15 +97,35 @@
    /// </summary>
    /// <param name="motionName">动画枚举</param>
    /// <param name="loop">是否循环</param>
    /// <param name="_onComplete">动画播放完成回调</param>
    /// <returns>动画轨道条目</returns>
    public virtual Spine.TrackEntry PlayAnimation(MotionName motionName, bool loop)
    public virtual Spine.TrackEntry PlayAnimation(MotionName motionName, bool loop, Action _onComplete = null)
    {
        if (spineAnimationState == null) return null;
        // 如果当前动画未完成
        if (currentTrackEntry != null && !currentTrackEntry.IsComplete)
        {
            if (trackEntryCompleteDict.TryGetValue(currentTrackEntry, out var __onComplete))
            {
                __onComplete?.Invoke();
                trackEntryCompleteDict.Remove(currentTrackEntry);
            }
            currentTrackEntry = null;
        }
        // 直接使用 ToString() 而不是调用 GetAnimationName
        currentTrackEntry = spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
        // 绑定回调
        if (_onComplete != null && currentTrackEntry != null)
        {
            trackEntryCompleteDict[currentTrackEntry] = _onComplete;
        }
        return currentTrackEntry;
    }
    
    /// <summary>
    /// 设置动画事件监听
@@ -147,84 +133,44 @@
    protected virtual void SetupAnimationHandlers()
    {
        if (spineAnimationState == null) return;
        // 监听动画完成事件
        spineAnimationState.Complete += OnAnimationComplete;
    }
    /// <summary>
    /// 动画完成事件处理
    /// </summary>
    protected virtual void OnAnimationComplete(Spine.TrackEntry trackEntry)
    {
        string animation = trackEntry.Animation.Name;
        // 攻击动画完成后恢复到待机状态
        if (animation == MotionName.attack.ToString() ||
            animation == MotionName.angerSkill.ToString())
        if (AttackMotionList.Contains(animation))
        {
            OnAttackAnimationComplete?.Invoke();
            PlayAnimation(MotionName.idle, true);
        }
        // 受伤动画完成后恢复到待机状态
        // 受伤动画完成后恢复到待机状态 可能触发多次 因为有多段攻击的存在
        else if (animation == MotionName.hit.ToString())
        {
            OnHitAnimationComplete?.Invoke();
            PlayAnimation(MotionName.idle, true);
        }
        else
        onAnimationComplete?.Invoke((MotionName)Enum.Parse(typeof(MotionName), animation));
        // 只调用本次TrackEntry的回调
        if (trackEntryCompleteDict.TryGetValue(trackEntry, out var cb))
        {
            OnOtherAnimationComplete?.Invoke((MotionName)Enum.Parse(typeof(MotionName), animation));
            cb?.Invoke();
            trackEntryCompleteDict.Remove(trackEntry);
        }
    }
    
    /// <summary>
    /// 动画事件处理
    /// </summary>
    protected virtual void OnTriggerHitEvent(MotionName motionName, int hitIndex)
    {
        OnAttackHitEvent?.Invoke(hitIndex);
    }
    //  拓展了播放动画方法,添加了触发帧的功能,在攻击动画中多段攻击的触发帧
    //  例如:攻击动画有三段,第一段攻击在第1帧触发,第二段攻击在第10帧触发,第三段攻击在第20帧触发
    //  那么triggerFrame就应该是[1, 10, 20]
    public virtual void PlayAnimationEx(MotionName motionName, bool loop, List<int> triggerFrame = null)
    {
        PlayAnimation(motionName, loop);
        if (triggerFrame != null && triggerFrame.Count >= 0)
        {
            AddWaitingTask(triggerFrame, motionName);
        }
    }
    protected void AddWaitingTask(List<int> triggerFrame, MotionName motionName)
    {
        if (triggerFrame != null && triggerFrame.Count > 0)
        {
            WaitingTask waitingTask = new WaitingTask(triggerFrame, motionName, OnTriggerHitEvent);
            waitingTaskList.Add(waitingTask);
        }
    }
    public virtual void Run()
    {
        foreach (WaitingTask waitingTask in waitingTaskList)
        {
            waitingTask.Run();
            if (waitingTask.IsFinished())
            {
                removeList.Add(waitingTask);
            }
        }
        foreach (WaitingTask waitingTask in removeList)
        {
            waitingTaskList.Remove(waitingTask);
        }
        removeList.Clear();
    }
    public virtual void Pause()
Main/System/Battle/Motion/MotionName.cs
@@ -16,4 +16,5 @@
    dead,//死亡 
    angerSkill,//怒气技能
    attack,//普攻
    passiveSkill,//被动技能
}
Main/System/Battle/RecordPlayer/RecordAction.cs
@@ -7,8 +7,14 @@
{
    protected BattleField battleField;
    //  本次Action的主角
    protected BattleObject battleObject;
    //  给子类用的 并不代表事件的结束与否
    protected bool isFinish = false;
    protected bool isRunOnce = false;
    public RecordAction(RecordActionType _actionType, BattleField _battleField, BattleObject _battleObj)
    {
        actionType = _actionType;
Main/System/Battle/Skill/DirectlyDamageSkill.cs
@@ -1,44 +1,93 @@
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;
public class DirectlyDamageSkill : SkillBase
{
    public DirectlyDamageSkill(SkillConfig _skillCfg) : base(_skillCfg)
    public DirectlyDamageSkill(BattleObject _caster, SkillConfig _skillCfg,
            H0604_tagUseSkillAttack vNetData, BattleField _battleField) : base(_caster, _skillCfg, vNetData, _battleField)
    {
    }
    public override void Run()
    {
    {
        base.Run();
    }
    public override void Cast(BattleObject _caster, BattleField _battleField, List<Dictionary<int, List<int>>> damageList)
    protected override void OnTriggerEvent(int triggerIndex, int triggerFrame)
    {
        if (0 == triggerIndex)
        {
            List<BattleObject> targetList = GetTargetList();
            //    子类实现具体的技能逻辑
            SkillEffect effect = SkillEffectFactory.CreateSkillEffect(caster, skillConfig, tagUseSkillAttack);
            if (null != effect)
            {
                //    如果是子弹类型的
                effect.Play((hitIndex, hurList) =>
                {
                    for (int i = 0; i < hurList.Count; i++)
                    {
                        var hurt = hurList[i];
                        BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)hurt.ObjID);
                        List<long> damageList = new List<long>();
                        //    计算伤害 分段的话
                        if (hitIndex >= skillConfig.DamageDivide.Length)
                        {
                            Debug.LogError("DamageDivide有问题 需要检查 只显示一段伤害 取triggerFrames的");
                            long totalDamage = GeneralDefine.GetFactValue(hurt.HurtHP, hurt.HurtHPEx) / (skillConfig.TriggerFrames.Length == 0 ? 1 : skillConfig.TriggerFrames.Length);
                            damageList.Add(totalDamage);
                        }
                        else
                        {
                            long totalDamage = GeneralDefine.GetFactValue(hurt.HurtHP, hurt.HurtHPEx);
                            if (hitIndex >= skillConfig.DamageDivide.Length)
                            {
                                int[] tenKDamagePercentArray = skillConfig.DamageDivide[hitIndex];
                                long factDamage = totalDamage;
                                for (int j = 0; j < tenKDamagePercentArray.Length; j++)
                                {
                                    long damage = (long)(Mathf.Round(factDamage * tenKDamagePercentArray[j] / 10000.0f));
                                    damageList.Add(damage);
                                }
                            }
                        }
                        target.Hurt(damageList, hurt.AttackType);
                    }
                });
            }
        }
    }
    protected override void DoSkillLogic(Action _onComplete = null)
    {
        _caster.PlaySkill(skillConfig, damageList, OnSkillAnimationFinished);
        //    开始记录帧数
        startCounting = true;
    }
    protected void OnSkillAnimationFinished()
    {
        isFinished = true;
    }
#if UNITY_EDITOR
#if UNITY_EDITOR_STOP_USING
    //    伤害列表理应由服务器发给客户端 这边只做粗浅模拟
    public override List<Dictionary<int, List<int>>> GetDamageList(BattleObject _caster, BattleField battleField)
    public override List<Dictionary<int, List<int>>> GetDamageList(BattleField battleField)
    {
        List<Dictionary<int, List<int>>> damageList = new List<Dictionary<int, List<int>>>();
        List<BattleObject> affectList = GetTargetList(_caster, battleField);
        List<BattleObject> affectList = GetTargetList(battleField);
        Dictionary<int, List<int>> oneRoundDamage = new Dictionary<int, List<int>>();
        for (int j = 0; j < affectList.Count; j++)
        {
            BattleObject obj = affectList[j];
            List<int> tryDmgList = _caster.TryAttack(obj, skillConfig);
            oneRoundDamage.Add(obj.BattleObjectId, tryDmgList);
            List<int> tryDmgList = caster.TryAttack(obj, skillConfig);
            oneRoundDamage.Add(obj.ObjID, tryDmgList);
        }
        damageList.Add(oneRoundDamage);
Main/System/Battle/Skill/SkillBase.cs
@@ -1,26 +1,206 @@
using System.Collections.Generic;
using UnityEngine;
using System;
using DG.Tweening;
using Spine;
public class SkillBase
{
    protected H0604_tagUseSkillAttack tagUseSkillAttack;
    protected SkillConfig skillConfig;
    protected bool isFinished = false;
    public SkillBase(SkillConfig _skillCfg)
    protected BattleField battleField = null; // 战场
    protected RectTransform targetNode = null; // 目标节点
    protected BattleObject caster = null; // 施法者
    protected bool startCounting = false;
    protected bool pauseState = false;
    protected int curFrame = 0;
    protected List<int> triggerFrames = new List<int>();
    public SkillBase(BattleObject _caster, SkillConfig _skillCfg, H0604_tagUseSkillAttack vNetData, BattleField _battleField = null)
    {
        caster = _caster;
        skillConfig = _skillCfg;
        tagUseSkillAttack = vNetData;
        battleField = _battleField;
        triggerFrames.Clear();
        triggerFrames.AddRange(skillConfig.TriggerFrames);
    }
    public virtual void Run()
    {
        if (startCounting)
        {
            curFrame++;
            if (triggerFrames.Contains(curFrame))
            {
                OnTriggerEvent(triggerFrames.IndexOf(curFrame), curFrame);
            }
        }
    }
    public virtual void Cast(BattleObject _caster, BattleField battleField, List<Dictionary<int, List<int>>> damageList)
    protected virtual void OnTriggerEvent(int triggerIndex, int triggerFrame)
    {
        Debug.LogError("SkillBase Cast should be overridden by derived class");
    }
    public void Pause()
    {
        pauseState = startCounting;
        startCounting = false;
    }
    public void Resume()
    {
        startCounting = pauseState;
    }
    // 0·移动到距离目标n码,的距离释放(可配置,9999即原地释放,负数则是移动到人物背面,人物要转身)
    // 1·移动到距离阵容位置n码的距离(如2号位,5号位)释放(即战场中央此类)
    public virtual void Cast()
    {
        //    高亮所有本次技能相关的目标
        HighLightAllTargets();
        switch (skillConfig.castMode)
        {
            case SkillCastMode.StandCast:
                PlayCastAnimation(() => DoSkillLogic(OnSkillFinished));
                break;
            case SkillCastMode.MoveToTarget:
                MoveToTarget(_onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
                break;
            case SkillCastMode.DashCast:
                DashToTarget(() => BackToOrigin(OnSkillFinished));
                break;
            default:
                Debug.LogError("暂时不支持其他的方式释放 有需求请联系策划");
                break;
        }
    }
    //    冲刺的技能 动作 跟移动 是同时进行的 移动到目标的一瞬间就要进行技能逻辑
    protected void DashToTarget(Action _onComplete)
    {
        TrackEntry entry = PlayCastAnimation();
        //    做一个微微的提前
        MoveToTarget(entry.TrackTime - 0.05f, () => DoSkillLogic(_onComplete));
    }
    protected void GetTargetNode()
    {
        targetNode = null;
        if (skillConfig.castMode == SkillCastMode.StandCast)
        {
            //    原地施法
            targetNode = caster.heroGo.transform as RectTransform;
        }
        else if (skillConfig.castMode == SkillCastMode.MoveToTarget || skillConfig.castMode == SkillCastMode.DashCast)
        {
            if (tagUseSkillAttack.AttackID <= 0)
            {
                Debug.LogError("技能没有指定目标");
                return;
            }
            //    移动到目标位置施法
            BattleObject _mainTarget = battleField.battleObjMgr.GetBattleObject((int)tagUseSkillAttack.AttackID);
            if (_mainTarget == null)
            {
                Debug.LogError("技能指定的目标不存在");
                return;
            }
            targetNode = _mainTarget.heroGo.transform as RectTransform;
        }
        else if (skillConfig.castMode == SkillCastMode.MoveToFormation)
        {
            //    TODO YYL
            targetNode = /*caster.GetEnemyTeamNode();*/ battleField.GetTeamNode(caster.Camp == BattleCamp.Blue ? BattleCamp.Red : BattleCamp.Blue);
        }
        else
        {
            Debug.LogError("未知的施法方式 技能id:" + skillConfig.SkillID);
            return;
        }
    }
    protected List<BattleObject> GetTargetList()
    {
        return battleField.battleObjMgr.GetBattleObjList(tagUseSkillAttack);
    }
    protected virtual void DoSkillLogic(Action _onComplete = null)
    {
        //    子类实现具体的技能逻辑
    }
    protected TrackEntry PlayCastAnimation(Action onComplete = null)
    {
        // 播放施法动作
        MotionName motionName = skillConfig.GetMotionName();
        TrackEntry trackEntry = caster.motionBase.PlayAnimation(motionName, false, onComplete);
        return trackEntry;
    }
    public void MoveToTarget(float duration = 0.2f, Action _onComplete = null)
    {
        GetTargetNode();
        Vector2 offset = new Vector2(skillConfig.CastDistance, 0);
        RectTransform selfRect = caster.heroGo.transform as RectTransform;
        RectTransform targetRect = targetNode;
        var tweener = BattleUtility.MoveToTarget(selfRect, targetRect, offset, duration, _onComplete);
        battleField.battleTweenMgr.OnPlayTween(tweener);
    }
    public void TurnBack(Action _onComplete)
    {
        if (skillConfig.CastDistance < 0)
        {
            caster.heroGo.transform.localScale = new Vector3(-1, 1, 1);
        }
        _onComplete?.Invoke();
    }
    public void BackToOrigin(Action _onComplete = null)
    {
        RectTransform selfRect = caster.heroGo.transform as RectTransform;
        Vector2 targetAnchoredPos = Vector2.zero;
        var tween = selfRect.DOAnchorPos(targetAnchoredPos, 0.2f)
            .SetEase(Ease.Linear)
            .OnComplete(() =>
            {
                caster.heroGo.transform.localScale = Vector3.one;
                _onComplete?.Invoke();
            });
        battleField.battleTweenMgr.OnPlayTween(tween);
    }
    protected void HighLightAllTargets()
    {
        // 高亮所有目标
        HashSet<BattleObject> highlightList = new HashSet<BattleObject>(battleField.battleObjMgr.GetBattleObjList(tagUseSkillAttack));
        highlightList.Add(caster);
        //    把这些BO全高亮 或者说把除了这些的都放在遮罩后面
        //    YYL TODO
    }
    public virtual bool IsFinished()
@@ -33,17 +213,22 @@
        isFinished = true;
    }
#if UNITY_EDITOR
    public virtual List<BattleObject> GetTargetList(BattleObject _caster, BattleField battleField)
    public void OnSkillFinished()
    {
        isFinished = true;
    }
#if UNITY_EDITOR_STOP_USING
    public virtual List<BattleObject> GetTargetList(BattleField _battleField)
    {
        SkillTargetType targetType = SkillTargetType.Enemy;
        SkillTargetRangeType rangeType = SkillTargetRangeType.LowestHP;
        List<BattleObject> affectList = battleField.battleObjMgr.GetTargetList(_caster, targetType, rangeType);
        List<BattleObject> affectList = _battleField.battleObjMgr.GetTargetList(caster, targetType, rangeType);
        return affectList;
    }
    public virtual List<Dictionary<int, List<int>>> GetDamageList(BattleObject _caster, BattleField battleField)
    public virtual List<Dictionary<int, List<int>>> GetDamageList(BattleField _battleField)
    {
        Debug.LogError("SkillBase GetDamageList should be overridden by derived class");
        return null;
Main/System/Battle/Skill/SkillCastMode.cs
New file
@@ -0,0 +1,9 @@
public enum SkillCastMode
{
    StandCast,      // 原地释放
    MoveToTarget,   // 跑到目标面前释放再回来
    DashCast,       // 原地动作但角色会冲到目标面前
    MoveToFormation, // 移动到阵容位置施法
}
Main/System/Battle/Skill/SkillCastMode.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Skill/SkillCastMode.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: f9281a48533a94f4ba2a84bc44bbe18d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Skill/SkillEffectAnchor.cs
New file
@@ -0,0 +1,7 @@
public enum SkillEffectAnchor
{
    Caster, //在施法者上
    Target, //在目标上
    AlliesCenter,// 在友方中心
    EnemiesCenter,// 在敌方中心
}
Main/System/Battle/Skill/SkillEffectAnchor.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Skill/SkillEffectAnchor.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 8c9269b4bdb75f042b97225006214939
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Skill/SkillEffectType.cs
New file
@@ -0,0 +1,7 @@
public enum SkillEffectType
{
    Bullet, //  子弹类型
    Direct, // 直接伤害
    BuffEffect, //  Buff
    StageEffect, // 场景技
}
Main/System/Battle/Skill/SkillEffectType.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Skill/SkillEffectType.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 0e224e8034be71b4d8571bb3adba5ecf
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/Skill/SkillFactory.cs
@@ -40,13 +40,16 @@
    public static SkillBase CreateSkill(SkillConfig _skillConfig)
    public static SkillBase CreateSkill(BattleObject _caster, H0604_tagUseSkillAttack vNetData, BattleField battleField)
    {
        SkillConfig skillConfig = SkillConfig.Get(vNetData.SkillID);
        SkillBase skill = null;
        switch (_skillConfig.SkillType)
        switch (skillConfig.SkillType)
        {
            case 1:
                skill = new DirectlyDamageSkill(_skillConfig);
                //    先实现一个攻击的 后续有再慢慢增加
                skill = new DirectlyDamageSkill(_caster, skillConfig, vNetData, battleField);
                break;
            case 2:
                // skill = new DirectlyHealSkill(_skillConfig);
Main/System/Battle/Skill/SkillType.cs
New file
@@ -0,0 +1,17 @@
// 攻击技能:本回合对敌人武将造成伤害
// 治疗技能:本回合对已方武将回复生命值,回复生命值不可超过生命上限
// 辅助技能:增加怒气、召唤、光环等
// BUFF技能:提升已方属性的增益BUFF(加攻、加血、加状态等),降低敌方属性的减益BUFF(减攻、减血、控制等),持续多回合
// 复活技能:复活已方已死武将并回复X生命值,
//  复活:继承死亡前的所有状态(各种状态、各种BUFF、怒气值等),复活后错过本回合攻击时机则下一回合才能出手
public enum SkillType
{
    Attack,
    Heal,
    Assist,
    Buff,
    Revive
}
Main/System/Battle/Skill/SkillType.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/Skill/SkillType.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: caaeda374b158404196e766e25b9a3b3
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/SkillEffect.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dd67eeb1a237eb44cad753537ec81338
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Battle/SkillEffect/BulletSkillEffect.cs
New file
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using DG.Tweening;
using UnityEngine;
public class BulletSkillEffect : SkillEffect
{
    // protected SkillConfig skillConfig;
    // protected BattleObject caster;
    // protected List<BattleObject> targets; // 目标列表
    public BulletSkillEffect(SkillConfig _skillConfig, BattleObject _caster, H0604_tagUseSkillAttack _tagUseSkillAttack)
        : base(_skillConfig, _caster, _tagUseSkillAttack)
    {
    }
    public override void Play(Action<int, List<H0604_tagUseSkillAttack.tagSkillHurtObj>> _onHit)
    {
        // Caster, //在施法者上
        // Target, //在目标上
        // AlliesCenter,// 在友方中心
        // EnemiesCenter,// 在敌方中心
        Action<int, List<H0604_tagUseSkillAttack.tagSkillHurtObj>> onHitFormation = (_hitIndex, _hurtList) =>
        {
            _onHit?.Invoke(_hitIndex, tagUseSkillAttack.HurtList.ToList());
        };
        if (skillConfig.effectAnchor == SkillEffectAnchor.Caster)
        {
            // 不可能子弹射向自己吧
            Debug.LogError("子弹技能特效不能锚定在施法者上 skillConfig.effectAnchor");
            _onHit?.Invoke(0, default);
        }
        else if (skillConfig.effectAnchor == SkillEffectAnchor.Target)
        {
            if (tagUseSkillAttack == null || tagUseSkillAttack.HurtList.Length <= 0)
            {
                Debug.LogError("子弹技能特效没有目标 tagUseSkillAttack.HurtList.Length <= 0");
                _onHit?.Invoke(0, default);
                return;
            }
            foreach (var hurt in tagUseSkillAttack.HurtList)
            {
                BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)hurt.ObjID);
                if (target == null)
                {
                    Debug.LogError("子弹技能特效目标为空 target == null ObjId : " + hurt.ObjID);
                    continue;
                }
                ShotToTarget(target.heroGo.transform as RectTransform, (index, list) => _onHit(0, new List<H0604_tagUseSkillAttack.tagSkillHurtObj>() { hurt }));
            }
        }
        else if (skillConfig.effectAnchor == SkillEffectAnchor.AlliesCenter)
        {
            ShotToTarget(caster.battleField.GetTeamNode(caster.Camp), onHitFormation);
        }
        else if (skillConfig.effectAnchor == SkillEffectAnchor.EnemiesCenter)
        {
            ShotToTarget(caster.battleField.GetTeamNode(caster.Camp == BattleCamp.Blue ? BattleCamp.Red : BattleCamp.Blue), onHitFormation);
        }
        else
        {
            Debug.LogError("未知的技能特效锚点类型: " + skillConfig.effectAnchor);
        }
    }
    protected void ShotToTarget(RectTransform target, Action<int, List<H0604_tagUseSkillAttack.tagSkillHurtObj>> _onHit)
    {
        EffectPlayer effectPlayer = caster.battleField.battleEffectMgr.PlayEffect(caster.ObjID, skillConfig.EffectId, caster.effectNode);
        effectPlayer.Play();
        RectTransform effectTrans = effectPlayer.transform as RectTransform;
        var tween = BattleUtility.MoveToTarget(effectTrans, target, Vector2.zero, skillConfig.FlyTime, () =>
        {
            // 表现子弹飞行到目标位置
            _onHit?.Invoke(0, null);
            //  击中就销毁子弹
            caster.battleField.battleEffectMgr.RemoveEffect(skillConfig.EffectId, effectPlayer);
            //  播放子弹爆炸特效
            caster.battleField.battleEffectMgr.PlayEffect(caster.ObjID, skillConfig.ExplotionEffectId, target).Play();
        });
        caster.battleField.battleTweenMgr.OnPlayTween(tween);
    }
    public override void Run()
    {
    }
}
Main/System/Battle/SkillEffect/BulletSkillEffect.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/SkillEffect/BulletSkillEffect.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 87730978d5481e24c9433f6cbe3bec78
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/SkillEffect/NormalSkillEffect.cs
New file
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using DG.Tweening;
using UnityEngine;
public class NormalSkillEffect : SkillEffect
{
    // protected SkillConfig skillConfig;
    // protected BattleObject caster;
    // protected List<BattleObject> targets; // 目标列表
    public NormalSkillEffect(SkillConfig _skillConfig, BattleObject _caster, H0604_tagUseSkillAttack _tagUseSkillAttack)
        : base(_skillConfig, _caster, _tagUseSkillAttack)
    {
    }
    public override void Play(Action<int, List<H0604_tagUseSkillAttack.tagSkillHurtObj>> _onHit)
    {
        Action<int, List<H0604_tagUseSkillAttack.tagSkillHurtObj>> onHitFormation = (_hitIndex, _hurtList) =>
        {
            _onHit?.Invoke(_hitIndex, tagUseSkillAttack.HurtList.ToList());
        };
        if (skillConfig.effectAnchor == SkillEffectAnchor.Caster)
        {
            CastInTarget(caster.heroGo.transform as RectTransform, onHitFormation);
        }
        else if (skillConfig.effectAnchor == SkillEffectAnchor.Target)
        {
            if (tagUseSkillAttack == null || tagUseSkillAttack.HurtList.Length <= 0)
            {
                Debug.LogError("没有目标 tagUseSkillAttack.HurtList.Length <= 0");
                _onHit?.Invoke(0, default);
                return;
            }
            foreach (var hurt in tagUseSkillAttack.HurtList)
            {
                BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)hurt.ObjID);
                if (target == null)
                {
                    Debug.LogError("特效目标为空 target == null ObjId : " + hurt.ObjID);
                    continue;
                }
                CastInTarget(target.heroGo.transform as RectTransform, (index, list) => _onHit(0, new List<H0604_tagUseSkillAttack.tagSkillHurtObj>() { hurt }));
            }
        }
        else if (skillConfig.effectAnchor == SkillEffectAnchor.AlliesCenter)
        {
            CastInTarget(caster.battleField.GetTeamNode(caster.Camp), onHitFormation);
        }
        else if (skillConfig.effectAnchor == SkillEffectAnchor.EnemiesCenter)
        {
            CastInTarget(caster.battleField.GetTeamNode(caster.Camp == BattleCamp.Blue ? BattleCamp.Red : BattleCamp.Blue), onHitFormation);
        }
        else
        {
            Debug.LogError("未知的技能特效锚点类型: " + skillConfig.effectAnchor);
        }
    }
    protected void CastInTarget(RectTransform target, Action<int, List<H0604_tagUseSkillAttack.tagSkillHurtObj>> _onHit)
    {
        EffectPlayer effectPlayer = caster.battleField.battleEffectMgr.PlayEffect(caster.ObjID, skillConfig.EffectId, caster.effectNode);
        effectPlayer.Play();
        RectTransform effectTrans = effectPlayer.transform as RectTransform;
        _onHit?.Invoke(0, null);
        //  销毁自身上的特效应该是等特效播放完毕之后
        // caster.battleField.battleEffectMgr.RemoveEffect(skillConfig.EffectId, effectPlayer);
        //  播放受击特效
        caster.battleField.battleEffectMgr.PlayEffect(caster.ObjID, skillConfig.ExplotionEffectId, target).Play();
    }
    public override void Run()
    {
    }
}
Main/System/Battle/SkillEffect/NormalSkillEffect.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/SkillEffect/NormalSkillEffect.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 532aa7a2a76cb1646ba7b5e9e6b5114a
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/SkillEffect/SkillEffect.cs
New file
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using UnityEngine;
public class SkillEffect
{
    protected SkillConfig skillConfig;
    protected BattleObject caster;
    protected H0604_tagUseSkillAttack tagUseSkillAttack;// 目标列表
    public SkillEffect(SkillConfig _skillConfig, BattleObject _caster, H0604_tagUseSkillAttack _tagUseSkillAttack)
    {
        skillConfig = _skillConfig;
        caster = _caster;
        tagUseSkillAttack = _tagUseSkillAttack;
    }
    public Action OnHit; // 表现到达目标时回调
    //
    public virtual void Play(Action<int, List<H0604_tagUseSkillAttack.tagSkillHurtObj>> _onHit)
    {
        // OnHit = _onHit;
        // switch (skillConfig.effectType)
        // {
        //     case SkillEffectType.StageEffect:
        //         Debug.LogError("播放场景特效");
        //         break;
        //     case SkillEffectType.Bullet:
        //         // 伪代码:表现子弹飞行
        //         Debug.Log("播放子弹特效");
        //         // 到达目标时
        //         OnHit?.Invoke();
        //         break;
        //     case SkillEffectType.Direct:
        //         // 伪代码:直接在目标身上播放特效
        //         Debug.Log("播放直接特效");
        //         OnHit?.Invoke();
        //         break;
        //     case SkillEffectType.BuffEffect:
        //         // 伪代码:播放持续特效
        //         Debug.Log("播放Buff特效");
        //         break;
        //     default:
        //         break;
        // }
    }
    public virtual void Run()
    {
    }
}
Main/System/Battle/SkillEffect/SkillEffect.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/SkillEffect/SkillEffect.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: fac494539cf0a0b41b14681032f00cbf
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/SkillEffect/SkillEffectFactory.cs
New file
@@ -0,0 +1,25 @@
using System.Collections.Generic;
public static class SkillEffectFactory
{
    public static SkillEffect CreateSkillEffect(BattleObject caster, SkillConfig skillConfig, H0604_tagUseSkillAttack tagUseSkillAttack)
    {
        switch (skillConfig.effectType)
        {
            case SkillEffectType.Bullet:
                return new BulletSkillEffect(skillConfig, caster, tagUseSkillAttack);
            case SkillEffectType.Direct:
                return new NormalSkillEffect(skillConfig, caster, tagUseSkillAttack);
            // case SkillEffectType.BuffEffect:
            //     return new BuffSkillEffect(skillConfig, caster, targets);
            // case SkillEffectType.StageEffect:
            //     return new StageSkillEffect(skillConfig, caster, targets);
            default:
                UnityEngine.Debug.LogError("Unknown Skill Effect Type");
                break;
        }
        return null;
    }
}
Main/System/Battle/SkillEffect/SkillEffectFactory.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Battle/SkillEffect/SkillEffectFactory.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: 010772e03bcf658429d8aaf42b2483e4
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Battle/UIComp/DamageContent.cs
@@ -18,7 +18,7 @@
        line.SetActive(false);
    }
    public void SetDamage(List<int> damages, Action _onComplete)
    public void SetDamage(List<long> damages, Action _onComplete)
    {
        for (int i = 0; i < damages.Count; i++)
        {
Main/System/Battle/UIComp/DamageLine.cs
@@ -9,13 +9,13 @@
    public TextEx damageValueLabel;
    public void SetDamage(DamageType damageType, int damage)
    public void SetDamage(DamageType damageType, long damage)
    {
        damageTypeLabel.SetActive(true);
        damageValueLabel.text = damage.ToString();
    }
    public void SetDamage(int damage)
    public void SetDamage(long damage)
    {
        damageTypeLabel.SetActive(false);
        damageValueLabel.text = damage.ToString();
Main/System/GeneralConfig/GeneralDefine.cs
@@ -1166,5 +1166,8 @@
        return inputString;
    }
    public static long GetFactValue(uint value, uint valueEx)
    {
        return (long)valueEx * HundredMillion +(long)value;
    }
}
Main/System/Hero/HeroFetterInfo.cs
@@ -37,7 +37,7 @@
            if (null == teamHero)
                continue;
            if (Array.IndexOf(fetterConfig.HeroIDList, teamHero.heroInfo.heroId) >= 0)
            if (Array.IndexOf(fetterConfig.HeroIDList, teamHero.heroId) >= 0)
            {
                count++;
            }
Main/System/Hero/HeroInfo.Lineup.cs
New file
@@ -0,0 +1,35 @@
using System.Collections.Generic;
using UnityEngine;
public partial class HeroInfo
{
    public Dictionary<TeamType, KeyValuePair<int, int>> GetTeamTypeShapeTypePositionDict()
    {
        //  英雄当前所有在的队伍
        List<int> heroTeams = itemHero.GetUseData(81);
        Dictionary<TeamType, KeyValuePair<int, int>> teamTypeShapeTypePositionDict = new Dictionary<TeamType, KeyValuePair<int, int>>();
        foreach (var teamMsg in heroTeams)
        {
            // 所在阵容信息列表 [阵容类型*10000+阵型类型*100+位置编号, ...]
            int teamType = teamMsg / 10000;
            int shapeType = (teamMsg % 10000) / 100;
            int positionIndex = teamMsg % 100;
            if (teamTypeShapeTypePositionDict.ContainsKey((TeamType)teamType))
            {
                //  队伍类型相同,更新阵型和位置
                Debug.LogError("当前英雄拥有两个相同的队伍信息: " + teamType + " " + shapeType + " " + positionIndex + ", hero guid is " + itemHero.guid);
            }
            else
            {
                //  队伍类型不同,添加新的
                KeyValuePair<int, int> shapeTypePosition = new KeyValuePair<int, int>(shapeType, positionIndex);
                teamTypeShapeTypePositionDict.Add((TeamType)teamType, shapeTypePosition);
            }
        }
        return teamTypeShapeTypePositionDict;
    }
}
Main/System/Hero/HeroInfo.Lineup.cs.meta
copy from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta copy to Main/System/Hero/HeroInfo.Lineup.cs.meta
File was copied from Main/Core/NetworkPackage/ServerPack/H06_PlayerVsNPC/H0602_tagObjBaseAttack.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c39e10c6d11822409ee2e39b684f89d
guid: fe5defee75fe9b24c81fd8cb22c7d952
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Hero/HeroInfo.cs
@@ -61,16 +61,6 @@
        UpdateHero(_itemHero);
    }
    public HeroInfo(HB424_tagSCTurnFightInit.tagSCTurnFightObj fightObj)
    {
        //  战斗表现需要这些基本就够 不够还需要服务器加
        heroId = (int)fightObj.HeroID;
        heroConfig = HeroConfig.Get(heroId);
        SkinID = (int)fightObj.SkinID;
        skinConfig = HeroSkinConfig.Get(SkinID);
    }
#if UNITY_EDITOR
    public HeroInfo()
    {
Main/System/Hero/HeroManager.cs
@@ -20,6 +20,8 @@
    public Action<HeroInfo> onHeroChangeEvent;
    public Action<HeroInfo> onHeroDeleteEvent;
    public override void Init()
    {
        base.Init();
@@ -67,6 +69,8 @@
            {
                heroInfo.UpdateHero(PackManager.Instance.GetItemByGuid(guid));
            }
            onHeroChangeEvent?.Invoke(heroInfo);
        }
    }
@@ -74,10 +78,13 @@
    {
        if (packType == PackType.Hero)
        {
            if (heroInfoDict.ContainsKey(guid))
            {
                heroInfoDict.Remove(guid);
            }
            HeroInfo heroInfo = null;
            heroInfoDict.TryGetValue(guid, out heroInfo);
            heroInfoDict.Remove(guid);
            if (null != heroInfo)
                onHeroDeleteEvent?.Invoke(heroInfo);
        }
    }
@@ -88,6 +95,10 @@
        return heroInfoDict[guid];
    }
    public List<HeroInfo> GetHeroList()
    {
        return heroInfoDict.Values.ToList();
    }
    void InitHerosInfo()
Main/System/MainLevel.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a02c6eff6ff37404ab5bdfb3357a7fa1
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Team/TeamBase.Properties.cs
@@ -10,29 +10,30 @@
    protected void UpdateProperties()
    {
        // YYL TODO
        //    羁绊
        for (int i = 0; i < teamHeros.Length; i++)
        {
            TeamHero hero = teamHeros[i];
            if (null != hero)
            {
                for (int j = 0; j < hero.heroInfo.fetterInfoList.Count; j++)
                {
                    hero.heroInfo.fetterInfoList[j].SetIsActiveFetter(this);
                }
            }
        }
        // for (int i = 0; i < teamHeros.Length; i++)
        // {
        //     TeamHero hero = teamHeros[i];
        //     if (null != hero)
        //     {
        //         for (int j = 0; j < hero.heroInfo.fetterInfoList.Count; j++)
        //         {
        //             hero.heroInfo.fetterInfoList[j].SetIsActiveFetter(this);
        //         }
        //     }
        // }
        //    阵型
        //    属性计算
        for (int i = 0; i < teamHeros.Length; i++)
        {
            TeamHero hero = teamHeros[i];
            if (null != hero)
                hero.heroInfo.CalculateProperties();
        }
        // for (int i = 0; i < teamHeros.Length; i++)
        // {
        //     TeamHero hero = teamHeros[i];
        //     if (null != hero)
        //         hero.heroInfo.CalculateProperties();
        // }
    }
    public void CalculatePower()
Main/System/Team/TeamBase.cs
@@ -1,78 +1,71 @@
using System;
using System.Collections.Generic;
using UnityEngine;
//  阵型基础
public partial class TeamBase
{
    // 该阵容在本阵营的编号,不同阵营的阵容编号可能相同,都是从1开始,一般1V1时每个阵营为1个阵容,多V多时则每个阵营为多个阵容
    private int teamIndex = 0;
    private uint playerId = 0;
    // 区分一下阵营跟阵容 阵营为一个TeamBase列表 可能是多个玩家组合而成
    // 阵容为一个TeamBase 是玩家的一个队伍
    private int teamIndex = 0;//该阵营的第几个队伍
    private uint playerId = 0; //该阵容的玩家id
    private int ShapeType; //本阵容阵型,0为默认阵型,可扩展不同的阵型,如boss特殊战斗阵型,或者其他不同站位的阵型
    // 本地可变阵型
    public bool IsPlayer
    {
        get
        {
            return playerId == 0;
        }
        get { return playerId == 0; }
    }
    public TeamHero[] teamHeros = new TeamHero[TeamConst.MaxTeamSlotCount];
    public TeamBase()
    // 服务器下发的阵型和阵容,只能通过网络协议赋值,外部只读
    public TeamType teamType
    {
        get;
        private set;
    }
    private int ShapeType;
    public int ServerShapeType { get; private set; }
    public TeamHero[] teamHeros = new TeamHero[TeamConst.MaxTeamHeroCount];
    public TeamHero[] serverData { get; private set; } = new TeamHero[TeamConst.MaxTeamHeroCount];
    public TeamBase(TeamType _teamType)
    {
        teamType = _teamType;
        teamIndex = 0;
        playerId = PlayerDatas.Instance.baseData.PlayerID;
    }
    //  这边来的可以没有队伍类型
    public TeamBase(HB424_tagSCTurnFightInit.tagSCTurnFightLineup lineUp)
    {
        teamIndex = lineUp.Num;
        playerId = lineUp.OwnerID;
        ShapeType = lineUp.ShapeType;
        ServerShapeType = lineUp.ShapeType;
        for (int i = 0; i < lineUp.ObjCnt; i++)
        {
            if (i < teamHeros.Length)
            {
                TeamHero hero = new TeamHero(lineUp.ObjList[i], this);
                AddTeamHero(hero);
                var fightObj = lineUp.ObjList[i];
                TeamHero hero = new TeamHero(fightObj, this);
                teamHeros[fightObj.PosNum] = hero;
                serverData[fightObj.PosNum] = hero;
            }
            else
            {
                Debug.LogError("TeamBase: Too many heroes in lineup, exceeding MaxTeamHeroCount.");
            }
        }
        Update();
        // 刷新服务器数据
        UpdateProperties();
    }
    protected void Update()
    {
        //  检查国籍
        //  检查羁绊
        //  检查阵型
        //  更新队伍英雄属性
        foreach (var teamHero in teamHeros)
        {
            teamHero.Update();
        }
        //  更新队伍属性
        // UpdateProperties();
        // CalculatePower();
    }
    public void SetShapeType(int shapeType)
    {
        ShapeType = shapeType;
    }
    public int GetTeamHeroCount()
    {
@@ -84,80 +77,9 @@
                count++;
            }
        }
        return count;
    }
    public bool SwapTeamHero(int index1, int index2)
    {
        if (index1 < 0 || index1 >= teamHeros.Length || index2 < 0 || index2 >= teamHeros.Length)
        {
            return false;
        }
        TeamHero temp = teamHeros[index1];
        teamHeros[index1] = teamHeros[index2];
        teamHeros[index2] = temp;
        temp.heroIndex = index2;
        teamHeros[index1].heroIndex = index1;
        return true;
    }
    public void AddHeroInfos(List<HeroInfo> heroInfos)
    {
        for (int i = 0; i < heroInfos.Count; i++)
        {
            AddHeroInfo(heroInfos[i]);
        }
        UpdateProperties();
    }
    public bool AddHeroInfo(HeroInfo heroInfo)
    {
        if (heroInfo == null)
        {
            return false;
        }
        for (int i = 0; i < teamHeros.Length; i++)
        {
            if (teamHeros[i] == null)
            {
                teamHeros[i] = new TeamHero();
                teamHeros[i].heroInfo = heroInfo;
                teamHeros[i].heroIndex = i;
                UpdateProperties();
                return true;
            }
        }
        return false;
    }
    public void AddTeamHero(TeamHero teamHero)
    {
        if (null == teamHero)
        {
            return;
        }
        teamHeros[teamHero.heroIndex] = teamHero;
    }
    public bool RemoveTeamHero(int index)
    {
        if (index < 0 || index >= teamHeros.Length)
        {
            return false;
        }
        teamHeros[index] = null;
        return true;
    }
    public bool IsFull()
    {
@@ -170,18 +92,248 @@
    }
#if UNITY_EDITOR
    public void FillWithFakeData()
    /// <summary>
    /// 保存阵容(预留接口)
    /// </summary>
    public void SaveTeam()
    {
        for (int i = 0; i < TeamConst.MaxTeamHeroCount; i++)
        if (IsEmpty())
        {
            TeamHero hero = new TeamHero();
            hero.curHp = 100;
            hero.heroInfo = new HeroInfo();
            hero.teamBase = this;
            hero.heroIndex = i;
            teamHeros[i] = hero;
            Debug.LogError("Cannot save an empty team. You should at least have one hero in the team.");
            return;
        }
        CB412_tagCSHeroLineupSave savePack = new CB412_tagCSHeroLineupSave();
        savePack.LineupID = (byte)teamType;
        savePack.ShapeType = (byte)ShapeType;
        savePack.PosCnt = (byte)GetTeamHeroCount();
        savePack.HeroPosList = new CB412_tagCSHeroLineupSave.tagCSHeroLineupPos[savePack.PosCnt];
        foreach (var hero in teamHeros)
        {
            if (hero != null)
            {
                int posNum = hero.positionNum;
                var heroInfo = HeroManager.Instance.GetHero(hero.guid);
                if (heroInfo == null)
                {
                    Debug.LogError($"Hero with GUID {hero.guid} not found in HeroManager.");
                    continue;
                }
                savePack.HeroPosList[posNum] = new CB412_tagCSHeroLineupSave.tagCSHeroLineupPos
                {
                    ItemIndex = (ushort)heroInfo.itemHero.gridIndex,
                    PosNum = (byte)posNum
                };
            }
        }
        GameNetSystem.Instance.SendInfo(savePack);
    }
    public void OnChangeShapeType(int newShapeType)
    {
        ShapeType = newShapeType;
    }
    public void OnServerChangeShapeType(int newShapeType)
    {
        ServerShapeType = newShapeType;
        ShapeType = newShapeType;
    }
    //  hero info could be null if the hero is removed from the team
    public void RefreshServerData(int shapeType, int positionIndex, HeroInfo heroInfo)
    {
        TeamHero teamHero = heroInfo == null ? null : new TeamHero(heroInfo, positionIndex, this);
        SetServerTeamHero(positionIndex, teamHero);
        OnServerChangeShapeType(shapeType);
    }
    public void CreateDefault(List<HeroInfo> heroInfos)
    {
        teamIndex = 0;
        playerId = PlayerDatas.Instance.baseData.PlayerID;
        OnServerChangeShapeType(0);
        for (int i = 0; i < heroInfos.Count; i++)
        {
            if (i < teamHeros.Length)
            {
                var heroInfo = heroInfos[i];
                TeamHero hero = new TeamHero(heroInfo, i, this);
                SetServerTeamHero(i, hero);
            }
            else
            {
                Debug.LogError("TeamBase: Too many heroes in lineup");
            }
        }
    }
#endif
    public TeamHero GetHero(string guid)
    {
        foreach (var hero in teamHeros)
        {
            if (hero != null && hero.guid == guid)
            {
                return hero;
            }
        }
        return null;
    }
    public TeamHero GetServerHero(string guid)
    {
        foreach (var hero in serverData)
        {
            if (hero != null && hero.guid == guid)
            {
                return hero;
            }
        }
        return null;
    }
    //  布阵接口
    public bool HasHero(string guid)
    {
        foreach (var hero in teamHeros)
        {
            if (hero != null && hero.guid == guid)
            {
                return true;
            }
        }
        return false;
    }
    public int GetEmptyPosition()
    {
        for (int i = 0; i < teamHeros.Length; i++)
        {
            if (teamHeros[i] == null)
            {
                return i;
            }
        }
        return -1; // No empty position
    }
    //  布阵接口
    public void SetTeamHero(int posNum, TeamHero hero)
    {
        teamHeros[posNum] = hero;
    }
    //  布阵接口
    public void SetServerTeamHero(int posNum, TeamHero hero)
    {
        serverData[posNum] = hero;
        teamHeros[posNum] = hero;
    }
    public void AddHero(HeroInfo heroInfo, int targetPosition)
    {
        if (targetPosition < 0 || targetPosition >= teamHeros.Length)
        {
            Debug.LogError("Invalid target position for adding hero.");
            return;
        }
        TeamHero targetHero = teamHeros[targetPosition];
        if (null == targetHero)
        {
            TeamHero newHero = new TeamHero(heroInfo, targetPosition, this);
            SetTeamHero(targetPosition, newHero);
        }
        else
        {
            SetTeamHero(targetPosition, new TeamHero(heroInfo, targetPosition, this));
        }
    }
    //  add只可能是点下面卡牌
    public bool AddHero(HeroInfo heroInfo)
    {
        if (heroInfo == null || heroInfo.itemHero == null) return false;
        //  如果当前英雄已经在队伍里了 就不处理了
        if (GetHero(heroInfo.itemHero.guid) != null)
        {
            return false; // Hero already in team
        }
        else
        {
            int emptyPosition = GetEmptyPosition();
            if (emptyPosition < 0)
            {
                Debug.LogError("No empty position available in the team.");
                return false; // No empty position available
            }
            TeamHero teamHero = new TeamHero(heroInfo, GetEmptyPosition(), this);
            SetTeamHero(teamHero.positionNum, teamHero);
            return true;
        }
    }
    public bool RemoveHero(HeroInfo heroInfo)
    {
        if (heroInfo == null || heroInfo.itemHero == null) return false;
        TeamHero teamHero = GetHero(heroInfo.itemHero.guid);
        if (teamHero != null)
        {
            //  从当前队伍里移除该英雄
            SetTeamHero(teamHero.positionNum, null);
            return true;
        }
        else
        {
            return false;
        }
    }
    public bool RemoveHero(TeamHero teamHero)
    {
        if (teamHero == null) return false;
        for (int i = 0; i < teamHeros.Length; i++)
        {
            if (teamHeros[i] != null && teamHeros[i].guid == teamHero.guid)
            {
                SetTeamHero(i, null);
                return true; // Hero removed successfully
            }
        }
        return false; // Hero not found
    }
    public void SwapPosition(int index1, int index2)
    {
        if (index1 < 0 || index1 >= teamHeros.Length || index2 < 0 || index2 >= teamHeros.Length)
        {
            Debug.LogError("Invalid indices for swapping positions.");
            return;
        }
        TeamHero temp = teamHeros[index1];
        teamHeros[index1] = teamHeros[index2];
        teamHeros[index2] = temp;
        //  更新位置编号
        if (teamHeros[index1] != null) teamHeros[index1].positionNum = index1;
        if (teamHeros[index2] != null) teamHeros[index2].positionNum = index2;
    }
}
Main/System/Team/TeamConst.cs
@@ -3,5 +3,5 @@
public class TeamConst
{
    public const int MaxTeamHeroCount = 6;//最大布阵数量
    public const int MaxTeamSlotCount = 7;//最大槽位数量
    public const int MaxTeamSlotCount = 7;//最大槽位数量 包含boss的中间位置 布阵的时候不能用这个
}
Main/System/Team/TeamHero.cs
@@ -3,57 +3,100 @@
public partial class TeamHero
{
    public HeroInfo heroInfo;
    public TeamBase teamBase;
    public int heroIndex;
    public int heroId;
    public HeroConfig heroConfig;
    public int SkinID;
    public HeroSkinConfig skinConfig;
    public string guid;
    public TeamBase teamBase
    {
        get; private set;
    }
    public int positionNum;
//  战场数据
    public uint ObjID = 0;// 战斗单位唯一ID
    public int ObjID = 0;// 战斗单位唯一ID
    public uint NPCID = 0;// 战斗NPCID,不同的实例ID对应的NPCID可能一样
    public int NPCID = 0;// 战斗NPCID,不同的实例ID对应的NPCID可能一样
    public TeamHero()
    {
    }
    //  服务器来的构造
    public TeamHero(HB424_tagSCTurnFightInit.tagSCTurnFightObj fightObj, TeamBase _teamBase)
    {
        heroInfo = new HeroInfo(fightObj);
        teamBase = _teamBase;
        if (fightObj.PosNum < 0 || fightObj.PosNum >= TeamConst.MaxTeamSlotCount)
        {
            Debug.LogError("Invalid PosNum: " + fightObj.PosNum);
        }
        else
        {
            teamBase.teamHeros[fightObj.PosNum] = this; // 将当前对象添加到对应位置
        }
        heroId = (int)fightObj.HeroID;
        heroConfig = HeroConfig.Get(heroId);
        SkinID = (int)fightObj.SkinID;
        skinConfig = HeroSkinConfig.Get(SkinID);
        ObjID = fightObj.ObjID;
        NPCID = fightObj.NPCID;
        teamBase = _teamBase;
        ObjID = (int)fightObj.ObjID;
        NPCID = (int)fightObj.NPCID;
        // HPEx * 1亿 + HP
        curHp = (long)fightObj.HPEx * GeneralDefine.HundredMillion +(long)fightObj.HP;
        maxHp = (long)fightObj.MaxHPEx * GeneralDefine.HundredMillion +(long)fightObj.MaxHP;
        curHp = (long)fightObj.HPEx * GeneralDefine.HundredMillion + (long)fightObj.HP;
        maxHp = (long)fightObj.MaxHPEx * GeneralDefine.HundredMillion + (long)fightObj.MaxHP;
        rage = (int)fightObj.AngreXP;
        heroIndex = fightObj.PosNum;
        positionNum = fightObj.PosNum;
        //  【重要】战斗构成里没有卡牌的guid
        guid = string.Empty;
        Update();
    }
    //  布阵时的构造
    public TeamHero(HeroInfo heroInfo, int posNum, TeamBase _teamBase)
    {
        heroId = heroInfo.itemHero.config.ID;
        heroConfig = HeroConfig.Get(heroId);
        SkinID = heroInfo.SkinID;
        skinConfig = heroInfo.skinConfig;
        teamBase = _teamBase;
        ObjID = 0;
        NPCID = 0;
        curHp = heroInfo.hp;
        maxHp = heroInfo.hp;
        rage = 0;
        positionNum = posNum;
        //  【重要】布阵里的要guid
        guid = heroInfo.itemHero.guid;
        Update();
    }
    public void OnSwapPosition(TeamHero teamHero)
    {
        int tempPosNum = positionNum;
        positionNum = teamHero.positionNum;
        teamHero.positionNum = tempPosNum;
        teamBase.teamHeros[positionNum] = this;
        teamBase.teamHeros[teamHero.positionNum] = teamHero;
        Update();
    }
    public void Update()
    {
    }
    //  最终属性 当前属性应该是要在这一层的
    public int GetPower()
    {
        return heroInfo.CalculatePower();
        // 计算战斗力YYL TODO
        return 0;
    }    
}
Main/System/Team/TeamManager.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
@@ -7,15 +8,133 @@
{
    protected Dictionary<TeamType, TeamBase> teamDict = new Dictionary<TeamType, TeamBase>();
    public override void Init()
    {
        base.Init();
    public override void Init()
    {
        base.Init();
        HeroManager.Instance.onHeroChangeEvent += onHeroChangeEvent;
        HeroManager.Instance.onHeroDeleteEvent += onHeroDeleteEvent;
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += OnBeforePlayerDataInitialize;
    }
    public override void Release()
    {
        base.Release();
    public override void Release()
    {
        base.Release();
        HeroManager.Instance.onHeroChangeEvent += onHeroChangeEvent;
        HeroManager.Instance.onHeroDeleteEvent += onHeroDeleteEvent;
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += OnBeforePlayerDataInitialize;
    }
    protected void OnBeforePlayerDataInitialize()
    {
        teamDict.Clear();
    }
    protected void onHeroChangeEvent(HeroInfo heroInfo)
    {
        //  英雄当前所有在的队伍
        List<int> heroTeams = heroInfo.itemHero.GetUseData(81);
        Dictionary<TeamType, KeyValuePair<int, int>> teamTypeShapeTypePositionDict = new Dictionary<TeamType, KeyValuePair<int, int>>();
        foreach (var teamMsg in heroTeams)
        {
            // 所在阵容信息列表 [阵容类型*10000+阵型类型*100+位置编号, ...]
            int teamType = teamMsg / 10000;
            int shapeType = (teamMsg % 10000) / 100;
            int positionIndex = teamMsg % 100;
            if (teamTypeShapeTypePositionDict.ContainsKey((TeamType)teamType))
            {
                //  队伍类型相同,更新阵型和位置
                Debug.LogError("当前英雄拥有两个相同的队伍信息: " + teamType + " " + shapeType + " " + positionIndex + ", hero guid is " + heroInfo.itemHero.guid);
            }
            else
            {
                //  队伍类型不同,添加新的
                KeyValuePair<int, int> shapeTypePosition = new KeyValuePair<int, int>(shapeType, positionIndex);
                teamTypeShapeTypePositionDict.Add((TeamType)teamType, shapeTypePosition);
            }
        }
        //  遍历当前所有队伍 判断当前队伍里是否有该英雄
        //  如果有的话 读取一下当前是否该英雄还在队伍里 位置是否发生变化
        //  或者是阵型发生变化 或者单纯的英雄发生变化
        //  如果没有的话 就说明该英雄被移出队伍了
        foreach (var team in teamDict.Values)
        {
            //  检查一下当前队伍是否有该英雄
            //  如果有的话 读取一下当前是否该英雄还在队伍里 位置是否发生变化
            TeamHero teamHero = team.GetHero(heroInfo.itemHero.guid);
            if (teamHero != null)
            {
                if ((teamTypeShapeTypePositionDict.ContainsKey(team.teamType)))
                {
                    KeyValuePair<int, int> shapeTypePosition = teamTypeShapeTypePositionDict[team.teamType];
                    //  更新队伍信息
                    // 可以判断teamHero的positionNum是否跟shapeTypePosition.Value一致 判断是否变位置了
                    // 可以判断teamHero的ServerShapeType是否跟shapeTypePosition.Key一致 判断是否变阵型了
                    team.RefreshServerData(shapeTypePosition.Key, shapeTypePosition.Value, heroInfo);
                }
                else
                {
                    //  队伍里有这个英雄,但是在队伍信息里没有了 置空 (被移出队伍)
                    team.SetTeamHero(teamHero.positionNum, null);
                }
            }
            //    原来队伍里没这个英雄
            else
            {
                //  如果当前队伍类型在英雄的所在阵容信息列表里有的话
                //    就说明队伍里新增了这个英雄 (新增进队伍)
                if (teamTypeShapeTypePositionDict.ContainsKey(team.teamType))
                {
                    KeyValuePair<int, int> shapeTypePosition = teamTypeShapeTypePositionDict[team.teamType];
                    team.RefreshServerData(shapeTypePosition.Key, shapeTypePosition.Value, null);
                }
            }
        }
        //  遍历英雄所在的队伍信息列表 新增一下当前储存的队伍里没有的队伍
        foreach (var teamTypeShapeTypePosition in teamTypeShapeTypePositionDict)
        {
            //  如果当前队伍类型在队伍字典里没有的话
            if (!teamDict.ContainsKey(teamTypeShapeTypePosition.Key))
            {
                //  新建一个队伍
                TeamBase team = new TeamBase(teamTypeShapeTypePosition.Key);
                team.RefreshServerData(teamTypeShapeTypePosition.Value.Key, teamTypeShapeTypePosition.Value.Value, heroInfo);
                teamDict.Add(teamTypeShapeTypePosition.Key, team);
            }
        }
    }
    protected void onHeroDeleteEvent(HeroInfo heroInfo)
    {
        List<int> heroTeams = heroInfo.itemHero.GetUseData(81);
        foreach (int teamMsg in heroTeams)
        {
            // 所在阵容信息列表 [阵容类型*10000+阵型类型*100+位置编号, ...]
            int teamType = teamMsg / 10000;
            int shapeType = (teamMsg % 10000) / 100;
            int positionIndex = teamMsg % 100;
            TeamBase team = GetTeam((TeamType)teamType);
            if (team != null)
            {
                team.RefreshServerData(shapeType, positionIndex, null);
            }
        }
    }
    public bool HasTeam(TeamType teamType)
    {
        return teamDict.ContainsKey(teamType);
    }
    public TeamBase GetTeam(TeamType teamType)
    {
@@ -23,8 +142,8 @@
        if (!teamDict.TryGetValue(teamType, out team))
        {
            team = new TeamBase();
            team.AddHeroInfos(HeroManager.Instance.GetPowerfulHeroList());
            team = new TeamBase(teamType);
            team.CreateDefault(HeroManager.Instance.GetPowerfulHeroList());
            teamDict.Add(teamType, team);
        }
Main/Utility/MathUtility.cs
@@ -219,4 +219,6 @@
        }
        return 0f;
    }
}
Main/Utility/UniTaskExtension.cs
@@ -4,12 +4,12 @@
public static class UniTaskExtension
{
    public static void DelayFrame(this GameObject go, Action action)
    public static void DelayFrame(this GameObject go, Action action)
    {
        DelayFrameInternal(1, action);
    }
    public static void DelayFrame(this Component cmp, Action action)
    public static void DelayFrame(this Component cmp, Action action)
    {
        DelayFrameInternal(1, action);
    }
@@ -29,4 +29,9 @@
    {
        DelayFrameInternal(frames, action);
    }
    public static void DelayFrame(Action action)
    {
        DelayFrameInternal(1, action);
    }
}