lcy
2025-12-03 b9a6e7e896b451e9c915e782a1789b2afe079cc9
76 聊天系统-客户端
2 文件已重命名
23个文件已修改
44个文件已删除
9 文件已复制
13个文件已添加
8023 ■■■■ 已修改文件
Main/Component/UI/Effect/BattleEffectPlayer.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Effect/EffectPlayer.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/EnhancedScroller.cs 130 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigManager.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/ChatBubbleBoxConfig.cs 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientPack/CB3_PlayerSocial.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientPack/CB3_PlayerSocial/CB320_tagCSTalk.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientPack/CB3_PlayerSocial/CB320_tagCSTalk.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB310_tagMCTalk.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB310_tagMCTalk.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB311_tagMCTalkCacheList.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB311_tagMCTalkCacheList.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0201_tagTalkGong.cs 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0201_tagTalkGong.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0203_tagTalkBang.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0203_tagTalkBang.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0205_tagTalkDui.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0205_tagTalkDui.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0206_tagTalkMi.cs 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0206_tagTalkMi.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0207_tagTalkArea.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0207_tagTalkArea.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0208_tagTalkCountry.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0208_tagTalkCountry.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB310_tagMCTalk.cs 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB310_tagMCTalk.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB311_tagMCTalkCacheList.cs 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB311_tagMCTalkCacheList.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/ResModule/GameObjectPoolManager.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleHUDWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatBubbleBehaviour.cs 426 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatBubbleManager.cs 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatBubbleManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatBulletItem.cs 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatBulletItem.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatBulletView.cs 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatBulletView.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatCenter.cs 544 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatCenter.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatChannelCell.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatChannelCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatContentBehaviour.cs 534 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatContentBehaviour.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatData.cs 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatData.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatDateCell.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatDateCell.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatExtraOneCell.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatExtraOneCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatFloatWin.cs 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatFloatWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatFriend.cs 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatFriend.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatFriendCell.cs 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatFriendCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatFriendTipCell.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatItemCell.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatItemCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatManager.cs 1965 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatPlayerMineCell.cs 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatPlayerOtherCell.cs 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatRecently.cs 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatRecently.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatRecentlyCell.cs 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatRecentlyCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatSendComponent.cs 480 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatSendComponent.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatSettingButton.cs 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatSettingButton.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatSysCell.cs 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatSysCell.cs.meta 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatTabCell.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatTabCell.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatTrailCell.cs 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/ChatWin.cs 735 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/LocalChatHistory.cs 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Chat/LocalChatHistory.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildBaseWin.cs 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/UIHeroController.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Horse/HorseController.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Main/HomeWin.cs 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Message/SysNotifyMgr.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/PhantasmPavilion/ChatBubbleHelper.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/PhantasmPavilion/PhantasmPavilionManager.cs 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/SystemSetting/ChatSetting.cs 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/SystemSetting/ChatSetting.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Tip/ScrollTip.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/LanguageVerify.cs 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Effect/BattleEffectPlayer.cs
@@ -432,7 +432,7 @@
        }
        // 从特效预制体池获取特效
        pool = GameObjectPoolManager.Instance.RequestPool(effectPrefab);
        pool = GameObjectPoolManager.Instance.GetPool(effectPrefab);
        effectTarget = pool.Request();
        // 设置父节点和位置
        effectTarget.transform.SetParent(transform);
Main/Component/UI/Effect/EffectPlayer.cs
@@ -348,7 +348,7 @@
        }
        // 从特效预制体池获取特效
        pool = GameObjectPoolManager.Instance.RequestPool(effectPrefab);
        pool = GameObjectPoolManager.Instance.GetPool(effectPrefab);
        effectTarget = pool.Request();
        // 设置父节点和位置
        effectTarget.transform.SetParent(transform);
Main/Component/UI/EnhancedScroller/EnhancedScroller.cs
@@ -262,7 +262,7 @@
                        // set the vertical position
                        scrollRect.verticalNormalizedPosition = 1f - (_scrollPosition / _ScrollSize);
                        //这里要注意,如果集合进行过复制,那么这里的_scrollSize也一样会比原来的增加,因为_ScrollSize永远比content的总大小小一个可视区大小
                    }
                    else
                    {
@@ -379,9 +379,9 @@
        /// The scroller should only be moving one one axix.
        /// </summary>
        public float LinearVelocity //实质上是操作ScrollRect.velocity
                       //Get : 将向量转为float类型
                       //Set:用一个float类型构造一个相应的向量
        //外部方法可以通过访问这个属性来使scroll运动起来
                                    //Get : 将向量转为float类型
                                    //Set:用一个float类型构造一个相应的向量
                                    //外部方法可以通过访问这个属性来使scroll运动起来
        {
            get
            {
@@ -450,7 +450,7 @@
                return _activeCellViewsStartIndex % NumberOfCells;
            }
        }
        /// <summary>
        /// This is the last data index showing in the scroller's visible area
        /// </summary>
@@ -462,8 +462,10 @@
            }
        }
        public int ActiveCellCnt {
            get {
        public int ActiveCellCnt
        {
            get
            {
                return _activeCellViews.Count;
            }
        }
@@ -512,7 +514,7 @@
                var go = Instantiate(cellPrefab.gameObject);
                cellView = go.GetComponent<EnhancedScrollerCellView>();
                cellView.transform.SetParent(_container);
            }
            Vector3 pos = cellView.transform.localPosition;
            if (pos.x == float.NaN || pos.y == float.NaN)
@@ -881,9 +883,12 @@
        /// Cached reference to the scrollRect
        /// </summary>
        private ScrollRect m_ScrollRect;
        public ScrollRect scrollRect {
            get {
                if (m_ScrollRect == null) {
        public ScrollRect scrollRect
        {
            get
            {
                if (m_ScrollRect == null)
                {
                    m_ScrollRect = this.GetComponent<ScrollRect>();
                }
                return m_ScrollRect;
@@ -1075,10 +1080,10 @@
        /// of the scroller
        /// </summary>
        public float _ScrollSize//这里并不是指scrllrect的大小,而是指真正在滑动的区域的大小
                                /*scrollrect的normallizePosition实际上就是这个真正能滑动的区域的大小
                                 * 在滑动过程中归一化的值,而这个大小就是所有子物体的大小与可视化区域的差
                                 * 当content的大小等于所有子物体的大小时,就可以简化操作直接用content的大小来代替
                                 */
        /*scrollrect的normallizePosition实际上就是这个真正能滑动的区域的大小
         * 在滑动过程中归一化的值,而这个大小就是所有子物体的大小与可视化区域的差
         * 当content的大小等于所有子物体的大小时,就可以简化操作直接用content的大小来代替
         */
        {
            get
            {
@@ -1090,7 +1095,7 @@
                        return 0;
                }
                else
                {
                    if (_container != null && _scrollRectTransform != null)
@@ -1098,9 +1103,88 @@
                    else
                        return 0;
                }
            }
        }
        // 仅聊天功能使用
        public void AddHeight(bool keepPosition, float height)
        {
            // 如果启用循环,回退到完整的 _Resize,因为循环逻辑复杂
            if (loop)
            {
                _Resize(keepPosition);
                return;
            }
            // 缓存原始位置
            var originalScrollPosition = _scrollPosition;
            int cellCount = NumberOfCells;
            if (cellCount == 0)
            {
                // 没有单元格,调用 _Resize 处理
                _Resize(keepPosition);
                return;
            }
            int newCellIndex = cellCount - 1; // 新单元格的索引
            // 检查内部数组是否一致:_cellViewSizeArray 应该已经包含了除新单元格外的所有单元格
            if (_cellViewSizeArray.Count != newCellIndex)
            {
                // 不一致,回退到完整重置
                _Resize(keepPosition);
                return;
            }
            // 计算新单元格的大小(包括间距)
            float newSize = height;
            newSize += spacing;
            // 添加到大小数组
            _cellViewSizeArray.Add(newSize);
            // 更新偏移数组
            float previousOffset = (_cellViewOffsetArray.Count > 0) ? _cellViewOffsetArray.Last() : 0;
            _cellViewOffsetArray.Add(previousOffset + newSize);
            // 设置活动单元格容器的大小
            if (scrollDirection == ScrollDirectionEnum.Vertical)
                _container.sizeDelta = new Vector2(_container.sizeDelta.x, _cellViewOffsetArray.Last() + padding.top + padding.bottom);
            else
                _container.sizeDelta = new Vector2(_cellViewOffsetArray.Last() + padding.left + padding.right, _container.sizeDelta.y);
            // 重建可见单元格
            _ResetVisibleCellViews();
            if (this.Delegate != null)
            {
                this.Delegate.OnRebuildComplete();
            }
            // 如果需要保持原始位置
            if (keepPosition)
            {
                ScrollPosition = originalScrollPosition;
            }
            else
            {
                ScrollPosition = 0;
            }
            // 设置滚动条可见性
            ScrollbarVisibility = scrollbarVisibility;
            if (!m_IsLoadAll)
            {
                m_IsLoadAll = true;
                if (OnFirstLoadAllEvent != null)
                {
                    OnFirstLoadAllEvent();
                }
            }
        }
        /// <summary>
        /// This function will create an internal list of sizes and offsets to be used in all calculations.
@@ -1157,7 +1241,7 @@
            {
                _loopFirstScrollPosition = GetScrollPositionForCellViewIndex(_loopFirstCellIndex, CellViewPositionEnum.Before) + (spacing * 0.5f);
                _loopLastScrollPosition = GetScrollPositionForCellViewIndex(_loopLastCellIndex, CellViewPositionEnum.After) - ScrollRectSize + (spacing * 0.5f);
                _loopFirstJumpTrigger = _loopFirstScrollPosition - ScrollRectSize;
                _loopLastJumpTrigger = _loopLastScrollPosition + ScrollRectSize;
@@ -1399,10 +1483,10 @@
        /// <param name="cellIndex">The index of the cell view</param>
        /// <param name="listPosition">Whether to add the cell to the beginning or the end</param>
        private void _AddCellView(int cellIndex, ListPositionEnum listPosition)
        {
        {
            //通过某个索引添加(从缓冲池中取)子物体到_activeCellViews集合中,并触发相关事件
            //这个方法本身也会根据传过来的索引来初始化CellView
            if (NumberOfCells == 0) return;
            // get the dataIndex. Modulus is used in case of looping so that the first set of cells are ignored
@@ -1662,7 +1746,7 @@
            _lastScrollbarVisibility = scrollbarVisibility;
            //一些变量进行了初始化
            if(OnCompLoad!=null) OnCompLoad();
            if (OnCompLoad != null) OnCompLoad();
            inited = true;
        }
@@ -1672,7 +1756,7 @@
            {
                // if the reload flag is true, then reload the data
                ReloadData();//理论上不用在外部调用ReloadData()因为当成功赋值Delegate后这个方法就会执行一次
                               //但如果想在update之前执行就主动调用ReloadData()
                             //但如果想在update之前执行就主动调用ReloadData()
            }
            // if the scroll rect size has changed and looping is on,
@@ -1770,7 +1854,7 @@
             */
        }
        /// <summary>
        /// This is fired by the tweener when the snap tween is completed
        /// </summary>
Main/Config/ConfigManager.cs
@@ -70,7 +70,6 @@
            typeof(OrderInfoConfig),
            typeof(PlayerAttrConfig),
            typeof(PlayerFaceConfig),
            typeof(PriorBundleConfig),
            typeof(RandomNameConfig),
            typeof(SignInConfig),
            typeof(StoreConfig),
@@ -297,8 +296,6 @@
        ClearConfigDictionary<PlayerAttrConfig>();
        // 清空 PlayerFaceConfig 字典
        ClearConfigDictionary<PlayerFaceConfig>();
        // 清空 PriorBundleConfig 字典
        ClearConfigDictionary<PriorBundleConfig>();
        // 清空 RandomNameConfig 字典
        ClearConfigDictionary<RandomNameConfig>();
        // 清空 SignInConfig 字典
Main/Config/Configs/ChatBubbleBoxConfig.cs
@@ -1,6 +1,6 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2025年11月16日
//    [  Date ]:           2025年12月1日
//--------------------------------------------------------
using System.Collections.Generic;
@@ -30,6 +30,11 @@
    public int[] InitAttrValueList;
    public int[] AttrPerStarAddList;
    public string GetWayString;
    public int[] LeftOffset;
    public int[] RightOffset;
    public int Top;
    public int[] MyColor;
    public int[] OtherColor;
    public override int LoadKey(string _key)
    {
@@ -104,6 +109,64 @@
            }
            GetWayString = tables[13];
            if (tables[14].Contains("["))
            {
                LeftOffset = JsonMapper.ToObject<int[]>(tables[14]);
            }
            else
            {
                string[] LeftOffsetStringArray = tables[14].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                LeftOffset = new int[LeftOffsetStringArray.Length];
                for (int i=0;i<LeftOffsetStringArray.Length;i++)
                {
                     int.TryParse(LeftOffsetStringArray[i],out LeftOffset[i]);
                }
            }
            if (tables[15].Contains("["))
            {
                RightOffset = JsonMapper.ToObject<int[]>(tables[15]);
            }
            else
            {
                string[] RightOffsetStringArray = tables[15].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                RightOffset = new int[RightOffsetStringArray.Length];
                for (int i=0;i<RightOffsetStringArray.Length;i++)
                {
                     int.TryParse(RightOffsetStringArray[i],out RightOffset[i]);
                }
            }
            int.TryParse(tables[16],out Top);
            if (tables[17].Contains("["))
            {
                MyColor = JsonMapper.ToObject<int[]>(tables[17]);
            }
            else
            {
                string[] MyColorStringArray = tables[17].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                MyColor = new int[MyColorStringArray.Length];
                for (int i=0;i<MyColorStringArray.Length;i++)
                {
                     int.TryParse(MyColorStringArray[i],out MyColor[i]);
                }
            }
            if (tables[18].Contains("["))
            {
                OtherColor = JsonMapper.ToObject<int[]>(tables[18]);
            }
            else
            {
                string[] OtherColorStringArray = tables[18].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                OtherColor = new int[OtherColorStringArray.Length];
                for (int i=0;i<OtherColorStringArray.Length;i++)
                {
                     int.TryParse(OtherColorStringArray[i],out OtherColor[i]);
                }
            }
        }
        catch (Exception exception)
        {
Main/Core/NetworkPackage/ClientPack/CB3_PlayerSocial.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: de8c89f12e8d05e478b12e42319b8816
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/ClientPack/CB3_PlayerSocial/CB320_tagCSTalk.cs
New file
@@ -0,0 +1,22 @@
using UnityEngine;
using System.Collections;
// B3 20 聊天 #tagCSTalk
public class CB320_tagCSTalk : GameNetPackBasic {
    public byte ChannelType;    // 频道
    public ushort Len;
    public string Content;    //size = Len
    public CB320_tagCSTalk () {
        combineCmd = (ushort)0x03FE;
        _cmd = (ushort)0xB320;
    }
    public override void WriteToBytes () {
        WriteBytes (ChannelType, NetDataType.BYTE);
        WriteBytes (Len, NetDataType.WORD);
        WriteBytes (Content, NetDataType.Chars, Len);
    }
}
Main/Core/NetworkPackage/ClientPack/CB3_PlayerSocial/CB320_tagCSTalk.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/Core/NetworkPackage/ClientPack/CB3_PlayerSocial/CB320_tagCSTalk.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: 4fe1fd39a69d5e2428c82f5617b67ced
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1a5cf4dc5e4cd954487fd817d8d1711e
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB310_tagMCTalk.cs
New file
@@ -0,0 +1,12 @@
using UnityEngine;
using System.Collections;
// B3 10 聊天信息 #tagMCTalk
public class DTCB310_tagMCTalk : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        HB310_tagMCTalk vNetData = vNetPack as HB310_tagMCTalk;
        ChatManager.Instance.UpdateTalk(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB310_tagMCTalk.cs.meta
File was renamed from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: ed90c24391805854cb9c7ee1c19d4d8d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB311_tagMCTalkCacheList.cs
New file
@@ -0,0 +1,12 @@
using UnityEngine;
using System.Collections;
// B3 11 聊天缓存通知 #tagMCTalkCacheList
public class DTCB311_tagMCTalkCacheList : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        HB311_tagMCTalkCacheList vNetData = vNetPack as HB311_tagMCTalkCacheList;
        ChatManager.Instance.UpdateTalkCacheList(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB311_tagMCTalkCacheList.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/Core/NetworkPackage/DTCFile/ServerPack/HB3_PlayerSocial/DTCB311_tagMCTalkCacheList.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: c919e25ea044110488ef1d7a7bdf0905
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
@@ -126,12 +126,12 @@
        Register(typeof(HA921_tagSCRenameResult), typeof(DTCA921_tagSCRenameResult));
        Register(typeof(HA303_tagSCHorseClassInfo), typeof(DTCA303_tagSCHorseClassInfo));
        Register(typeof(HA304_tagSCHorseSkinInfo), typeof(DTCA304_tagSCHorseSkinInfo));
        Register(typeof(HB119_tagSCModelInfo), typeof(DTCB119_tagSCModelInfo));
        Register(typeof(HB126_tagSCTitleInfo), typeof(DTCB126_tagSCTitleInfo));
        Register(typeof(HB127_tagSCChatBoxInfo), typeof(DTCB127_tagSCChatBoxInfo));
        Register(typeof(HB431_tagSCTurnFightRet), typeof(DTCB431_tagSCTurnFightRet));
        Register(typeof(HB310_tagMCTalk), typeof(DTCB310_tagMCTalk));
        Register(typeof(HB311_tagMCTalkCacheList), typeof(DTCB311_tagMCTalkCacheList));
    }
    //主工程注册封包
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0201_tagTalkGong.cs
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0201_tagTalkGong.cs.meta
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0203_tagTalkBang.cs
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0203_tagTalkBang.cs.meta
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0205_tagTalkDui.cs
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0205_tagTalkDui.cs.meta
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0206_tagTalkMi.cs
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0206_tagTalkMi.cs.meta
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0207_tagTalkArea.cs
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0207_tagTalkArea.cs.meta
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0208_tagTalkCountry.cs
File was deleted
Main/Core/NetworkPackage/ServerPack/H02_PlayerTalk/H0208_tagTalkCountry.cs.meta
File was deleted
Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB310_tagMCTalk.cs
New file
@@ -0,0 +1,43 @@
using UnityEngine;
using System.Collections;
// B3 10 聊天信息 #tagMCTalk
public class HB310_tagMCTalk : GameNetPackBasic {
    public byte ChannelType;    // 0-世界;1-跨服;3- 仙盟
    public byte NameLen;
    public string Name;    //size = NameLen
    public uint PlayerID;
    public ushort Len;
    public string Content;    //size = Len
    public uint BubbleBox;    //聊天气泡框
    public ushort LV;    //等级
    public byte Job;    //职业
    public byte RealmLV;    //境界
    public uint Face;    //基本脸型
    public uint FacePic;    //头像框
    public uint TitleID;    //佩戴的称号
    public uint ServerID;    //所属区服ID
    public HB310_tagMCTalk () {
        _cmd = (ushort)0xB310;
    }
    public override void ReadFromBytes (byte[] vBytes) {
        TransBytes (out ChannelType, vBytes, NetDataType.BYTE);
        TransBytes (out NameLen, vBytes, NetDataType.BYTE);
        TransBytes (out Name, vBytes, NetDataType.Chars, NameLen);
        TransBytes (out PlayerID, vBytes, NetDataType.DWORD);
        TransBytes (out Len, vBytes, NetDataType.WORD);
        TransBytes (out Content, vBytes, NetDataType.Chars, Len);
        TransBytes (out BubbleBox, vBytes, NetDataType.DWORD);
        TransBytes (out LV, vBytes, NetDataType.WORD);
        TransBytes (out Job, vBytes, NetDataType.BYTE);
        TransBytes (out RealmLV, vBytes, NetDataType.BYTE);
        TransBytes (out Face, vBytes, NetDataType.DWORD);
        TransBytes (out FacePic, vBytes, NetDataType.DWORD);
        TransBytes (out TitleID, vBytes, NetDataType.DWORD);
        TransBytes (out ServerID, vBytes, NetDataType.DWORD);
    }
}
Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB310_tagMCTalk.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB310_tagMCTalk.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: 8be0d9f4eef74044aabaf35d2bd624fb
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB311_tagMCTalkCacheList.cs
New file
@@ -0,0 +1,55 @@
using UnityEngine;
using System.Collections;
// B3 11 聊天缓存通知 #tagMCTalkCacheList
public class HB311_tagMCTalkCacheList : GameNetPackBasic {
    public byte ChannelType;    // 0-世界;1-跨服;3- 仙盟
    public byte Count;
    public  tagMCTalkCacheInfo[] InfoList;    //size = Count
    public HB311_tagMCTalkCacheList () {
        _cmd = (ushort)0xB311;
    }
    public override void ReadFromBytes (byte[] vBytes) {
        TransBytes (out ChannelType, vBytes, NetDataType.BYTE);
        TransBytes (out Count, vBytes, NetDataType.BYTE);
        InfoList = new tagMCTalkCacheInfo[Count];
        for (int i = 0; i < Count; i ++) {
            InfoList[i] = new tagMCTalkCacheInfo();
            TransBytes (out InfoList[i].NameLen, vBytes, NetDataType.BYTE);
            TransBytes (out InfoList[i].Name, vBytes, NetDataType.Chars, InfoList[i].NameLen);
            TransBytes (out InfoList[i].PlayerID, vBytes, NetDataType.DWORD);
            TransBytes (out InfoList[i].Len, vBytes, NetDataType.WORD);
            TransBytes (out InfoList[i].Content, vBytes, NetDataType.Chars, InfoList[i].Len);
            TransBytes (out InfoList[i].BubbleBox, vBytes, NetDataType.DWORD);
            TransBytes (out InfoList[i].LV, vBytes, NetDataType.WORD);
            TransBytes (out InfoList[i].Job, vBytes, NetDataType.BYTE);
            TransBytes (out InfoList[i].RealmLV, vBytes, NetDataType.BYTE);
            TransBytes (out InfoList[i].Face, vBytes, NetDataType.DWORD);
            TransBytes (out InfoList[i].FacePic, vBytes, NetDataType.DWORD);
            TransBytes (out InfoList[i].TitleID, vBytes, NetDataType.DWORD);
            TransBytes (out InfoList[i].ServerID, vBytes, NetDataType.DWORD);
            TransBytes (out InfoList[i].TalkTime, vBytes, NetDataType.DWORD);
        }
    }
    public class tagMCTalkCacheInfo {
        public byte NameLen;
        public string Name;        //size = NameLen
        public uint PlayerID;
        public ushort Len;
        public string Content;        //size = Len
        public uint BubbleBox;        //聊天气泡框
        public ushort LV;        //等级
        public byte Job;        //职业
        public byte RealmLV;        //境界
        public uint Face;        //基本脸型
        public uint FacePic;        //头像框
        public uint TitleID;        //佩戴的称号
        public uint ServerID;        //所属区服ID
        public uint TalkTime;        //该聊天发送时间戳
    }
}
Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB311_tagMCTalkCacheList.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/Core/NetworkPackage/ServerPack/HB3_PlayerSocial/HB311_tagMCTalkCacheList.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: 100b4f6d5e20875418941246adf7f06f
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Core/ResModule/GameObjectPoolManager.cs
@@ -248,7 +248,7 @@
    /// </summary>
    /// <param name="prefab"></param>
    /// <returns></returns>
    public GameObjectPool RequestPool(GameObject prefab)
    public GameObjectPool GetPool(GameObject prefab)
    {
        if (prefab == null)
        {
@@ -286,7 +286,7 @@
            return;
        }
        RequestPool(prefab).Cache(count, _prefabActive);
        GetPool(prefab).Cache(count, _prefabActive);
    }
@@ -304,7 +304,7 @@
            return null;
        }
#endif
        return RequestPool(prefab).Request();
        return GetPool(prefab).Request();
    }
Main/Main.cs
@@ -65,7 +65,6 @@
        managers.Add(CustomizedRechargeModel.Instance);
        managers.Add(CustomizedGiftModel.Instance);
        managers.Add(ChatManager.Instance);
        managers.Add(ChatBubbleManager.Instance);
        managers.Add(OfficialRankManager.Instance);
        managers.Add(RankModel.Instance);
        managers.Add(PlayerMainDate.Instance);
Main/System/Battle/BattleHUDWin.cs
@@ -78,7 +78,7 @@
    private void InitializePools()
    {
        damagePrefabPool = GameObjectPoolManager.Instance.RequestPool(UILoader.LoadPrefab("DamageContent"));
        damagePrefabPool = GameObjectPoolManager.Instance.GetPool(UILoader.LoadPrefab("DamageContent"));
    }
    public void SetBattleField(BattleField _battleField)
Main/System/Chat/ChatBubbleBehaviour.cs
@@ -1,257 +1,223 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
    [DisallowMultipleComponent]
    [RequireComponent(typeof(RectTransform))]
    [ExecuteAlways]
    public class ChatBubbleBehaviour : MonoBehaviour
[DisallowMultipleComponent]
[RequireComponent(typeof(RectTransform))]
[ExecuteAlways]
public class ChatBubbleBehaviour : MonoBehaviour
{
    [SerializeField] Text m_Target;
    [SerializeField] RectOffset m_Padding;
    [SerializeField] ImageEx m_BubbleIcon;
    [SerializeField] UIFrame m_UIFrame;
    [SerializeField] bool left = false;
    [SerializeField] bool m_PreferredWidth = false;
    RectTransform m_Rect;
    RectTransform rect
    {
        [SerializeField] Text m_Target;
        [SerializeField] RectOffset m_Padding;
        [SerializeField] FlipImage m_Flip;
        [SerializeField] Image m_BubbleIcon;
        [SerializeField] bool left = false;
        [SerializeField] bool m_PreferredWidth = false;
        [SerializeField] RectTransform m_ContainerVoice;
        const float space = 5.0f;
        private int bubbleId = 0;
        RectTransform m_Rect;
        RectTransform rect
        get
        {
            get
            if (m_Rect == null)
            {
                if (m_Rect == null)
                {
                    m_Rect = transform as RectTransform;
                }
                return m_Rect;
                m_Rect = transform as RectTransform;
            }
            return m_Rect;
        }
    }
    public RectOffset padding
    {
        get { return m_Padding; }
    }
    private void OnEnable()
    {
        Refresh();
    }
    public void DisplayContent(string content, bool _left = false)
    {
        if (m_Target == null)
        {
            return;
        }
        left = _left;
        m_PreferredWidth = true;
        var richText = m_Target as RichText;
        if (richText != null && !left)
        {
            richText.AutoNewLine = false;
        }
        m_Target.text = content;
        if (richText != null)
        {
            richText.AutoNewLine = true;
        }
        public RectOffset padding
        Refresh();
    }
    public void DisplaySysBubble(int id, Color color)
    {
        ChatBubbleData bubble;
        if (ChatManager.Instance.TryGetBubble(id, out bubble))
        {
            get { return m_Padding; }
        }
            var bubblePadding = left ? bubble.leftPadding : bubble.rifhtPadding;
            padding.top = bubblePadding.top;
            padding.left = bubblePadding.left;
            padding.right = bubblePadding.right;
            padding.bottom = bubblePadding.bottom;
        private void OnEnable()
        {
            Refresh();
        }
        public void DisplayContent(string content, bool _left = false)
        {
            if (m_Target == null)
            int resourceType = PhantasmPavilionManager.Instance.GetResourceType(PhantasmPavilionType.ChatBox, id);
            string resourceValue = PhantasmPavilionManager.Instance.GetResourceValue(PhantasmPavilionType.ChatBox, id);
            PhantasmPavilionManager.Instance.ShowChatBox(m_BubbleIcon, m_UIFrame, resourceType, resourceValue);
            m_Target.color = color;
            var position = rect.anchoredPosition;
            if (rect.anchoredPosition != position)
            {
                return;
            }
            left = _left;
            m_PreferredWidth = true;
            var targetRect = m_Target.rectTransform;
            var richText = m_Target as RichText;
            if (richText != null && !left)
            {
                richText.AutoNewLine = false;
            }
            m_Target.text = content;
            if (!left)
            {
                if (m_Target.preferredWidth > targetRect.rect.width)
                {
                    m_Target.alignment = TextAnchor.UpperLeft;
                    m_PreferredWidth = false;
                }
                else
                {
                    m_Target.alignment = TextAnchor.UpperRight;
                }
            }
            if (richText != null)
            {
                richText.AutoNewLine = true;
                rect.anchoredPosition = position;
            }
            Refresh();
        }
        public void DisplayBubble(int id)
    }
    public void DisplayBubble(int id, int playerId)
    {
        ChatBubbleData bubble;
        if (ChatManager.Instance.TryGetBubble(id, out bubble))
        {
            bubbleId = id;
            ChatBubbleManager.ChatBubble bubble;
            if (ChatBubbleManager.Instance.TryGetBubble(id, out bubble))
            var bubblePadding = left ? bubble.leftPadding : bubble.rifhtPadding;
            padding.top = bubblePadding.top;
            padding.left = bubblePadding.left;
            padding.right = bubblePadding.right;
            padding.bottom = bubblePadding.bottom;
            int resourceType = PhantasmPavilionManager.Instance.GetResourceType(PhantasmPavilionType.ChatBox, id);
            string resourceValue = PhantasmPavilionManager.Instance.GetResourceValue(PhantasmPavilionType.ChatBox, id);
            PhantasmPavilionManager.Instance.ShowChatBox(m_BubbleIcon, m_UIFrame, resourceType, resourceValue);
            if (playerId == PlayerDatas.Instance.PlayerId)
            {
                var bubblePadding = left ? bubble.leftPadding : bubble.rifhtPadding;
                padding.top = bubblePadding.top;
                padding.left = bubblePadding.left;
                padding.right = bubblePadding.right;
                padding.bottom = bubblePadding.bottom;
                bool requireFlip = false;
                var iconKey = bubble.GetBubbleIcon(left, ref requireFlip);
                //m_BubbleIcon.SetSprite(iconKey);
                UIFrame frame = m_BubbleIcon.GetComponent<UIFrame>();
                if (UIFrameMgr.Inst.ContainsDynamicImage(iconKey))
                {
                    if (frame == null)
                        frame = m_BubbleIcon.gameObject.AddComponent<UIFrame>();
                    frame.ResetFrame(iconKey);
                    frame.enabled = true;
                }
                else
                {
                    if (frame != null)
                        frame.enabled = false;
                    m_BubbleIcon.SetSprite(iconKey);
                }
                m_Flip.flipHorizontal = requireFlip;
                m_Target.color = bubble.color;
                var position = rect.anchoredPosition;
                // TODO YYL
                // ChatBubbleBoxConfig config = ChatBubbleBoxConfig.Get(bubbleId);
                // position.y = -config.top;
                if (rect.anchoredPosition != position)
                {
                    rect.anchoredPosition = position;
                }
                Refresh();
            }
        }
        [ExecuteAlways]
        private void LateUpdate()
        {
            Refresh();
        }
        void Refresh()
        {
            if (m_Target == null)
            {
                return;
            }
            bool nullContent = string.IsNullOrEmpty(m_Target.text);
            var targetRect = m_Target.rectTransform;
            var sizeDelta = targetRect.sizeDelta;
            var width = m_PreferredWidth || !Application.isPlaying ? m_Target.preferredWidth : sizeDelta.x;
            if (nullContent)
            {
                width = 0f;
            }
            var height = sizeDelta.y;
            if (nullContent)
            {
                height = 0;
            }
            if (m_ContainerVoice != null)
            {
                width = Mathf.Max(m_ContainerVoice.sizeDelta.x, width);
                height += m_ContainerVoice.sizeDelta.y;
                if (!nullContent)
                {
                    height += space;
                }
            }
            sizeDelta.x = width + m_Padding.left + m_Padding.right;
            sizeDelta.y = height + m_Padding.top + m_Padding.bottom;
            if (sizeDelta != rect.sizeDelta)
            {
                rect.sizeDelta = sizeDelta;
            }
            SetAnchor(m_Target.rectTransform);
            if (m_ContainerVoice != null)
            {
                SetAnchor(m_ContainerVoice);
            }
            float top = padding.top;
            Vector2 position = Vector2.zero;
            position.x = left ? padding.left : -padding.right;
            if (m_ContainerVoice != null)
            {
                position.y = -top;
                if (m_ContainerVoice.anchoredPosition != position)
                {
                    m_ContainerVoice.anchoredPosition = position;
                }
                top = top + m_ContainerVoice.sizeDelta.y;
                top += space;
            }
            position.y = -top;
            if (targetRect.anchoredPosition != position)
            {
                targetRect.anchoredPosition = position;
            }
        }
        void SetAnchor(RectTransform targetRect)
        {
            if (!left)
            {
                if (targetRect.anchorMin != Vector2.one)
                {
                    targetRect.anchorMin = Vector2.one;
                }
                if (targetRect.anchorMax != Vector2.one)
                {
                    targetRect.anchorMax = Vector2.one;
                }
                if (targetRect.pivot != Vector2.one)
                {
                    targetRect.pivot = Vector2.one;
                }
                m_Target.color = bubble.myColor;
            }
            else
            {
                if (targetRect.anchorMin != Vector2.up)
                {
                    targetRect.anchorMin = Vector2.up;
                }
                if (targetRect.anchorMax != Vector2.up)
                {
                    targetRect.anchorMax = Vector2.up;
                }
                if (targetRect.pivot != Vector2.up)
                {
                    targetRect.pivot = Vector2.up;
                }
                m_Target.color = bubble.otherColor;
            }
            var position = rect.anchoredPosition;
            position.y = -bubble.top;
            if (rect.anchoredPosition != position)
            {
                rect.anchoredPosition = position;
            }
            Refresh();
        }
    }
    [ExecuteAlways]
    private void LateUpdate()
    {
        Refresh();
    }
    void Refresh()
    {
        if (m_Target == null)
        {
            return;
        }
        bool nullContent = string.IsNullOrEmpty(m_Target.text);
        var targetRect = m_Target.rectTransform;
        var sizeDelta = targetRect.sizeDelta;
        var width = m_PreferredWidth || !Application.isPlaying ? m_Target.preferredWidth : sizeDelta.x;
        if (nullContent)
        {
            width = 0f;
        }
        var height = sizeDelta.y;
        if (nullContent)
        {
            height = 0;
        }
        public float GetBubbleHeight(string content, ArrayList list)
        sizeDelta.x = width + m_Padding.left + m_Padding.right;
        sizeDelta.y = height + m_Padding.top + m_Padding.bottom;
        if (sizeDelta != rect.sizeDelta)
        {
            if (m_Target is RichText)
            {
                (m_Target as RichText).SetExtenalData(list);
            }
            m_Target.text = content;
            var height = m_Target.preferredHeight;
            bool nullContent = string.IsNullOrEmpty(content);
            if (nullContent)
            {
                height = 0f;
            }
            if (m_ContainerVoice != null)
            {
                height += m_ContainerVoice.sizeDelta.y;
                if (!nullContent)
                {
                    height += space;
                }
            }
            return height + padding.top + padding.bottom;
            rect.sizeDelta = sizeDelta;
        }
    }
        SetAnchor(m_Target.rectTransform);
        float top = padding.top;
        Vector2 position = Vector2.zero;
        position.x = left ? padding.left : -padding.right;
        position.y = -top;
        if (targetRect.anchoredPosition != position)
        {
            targetRect.anchoredPosition = position;
        }
    }
    void SetAnchor(RectTransform targetRect)
    {
        if (!left)
        {
            if (targetRect.anchorMin != Vector2.one)
            {
                targetRect.anchorMin = Vector2.one;
            }
            if (targetRect.anchorMax != Vector2.one)
            {
                targetRect.anchorMax = Vector2.one;
            }
            if (targetRect.pivot != Vector2.one)
            {
                targetRect.pivot = Vector2.one;
            }
        }
        else
        {
            if (targetRect.anchorMin != Vector2.up)
            {
                targetRect.anchorMin = Vector2.up;
            }
            if (targetRect.anchorMax != Vector2.up)
            {
                targetRect.anchorMax = Vector2.up;
            }
            if (targetRect.pivot != Vector2.up)
            {
                targetRect.pivot = Vector2.up;
            }
        }
    }
    public float GetBubbleHeight(string content, ArrayList list)
    {
        if (m_Target is RichText)
        {
            (m_Target as RichText).SetExtenalData(list);
        }
        m_Target.text = content;
        var height = m_Target.preferredHeight;
        bool nullContent = string.IsNullOrEmpty(content);
        if (nullContent)
        {
            height = 0f;
        }
        //Debug.Log($"GetBubbleHeight {height + padding.top + padding.bottom} height {height} padding.top {padding.top} padding.bottom {padding.bottom}");
        return height + padding.top + padding.bottom;
    }
}
Main/System/Chat/ChatBubbleManager.cs
File was deleted
Main/System/Chat/ChatBubbleManager.cs.meta
File was deleted
Main/System/Chat/ChatBulletItem.cs
New file
@@ -0,0 +1,128 @@
using System;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using System.Collections;
[RequireComponent(typeof(RectTransform))]
public class ChatBulletItem : MonoBehaviour
{
    [SerializeField] ImageEx contentImage;
    [SerializeField] RichText contentText;
    [SerializeField] ImageEx sysImage;
    [SerializeField] RichText sysText;
    // 背景图左右的额外留白 (x:左边距, y:右边距)
    [SerializeField] Vector2 padding = new Vector2(50f, 50f);
    [SerializeField] Vector2 sysPadding = new Vector2(70f, 70f);
    private RectTransform rectTrans;
    private RectTransform imageRect;
    private RectTransform imageSysRect;
    private GameObject sourcePrefab;
    private Tweener moveTweener;
    private Action<ChatBulletItem> onFinishCallback;
    private void Awake()
    {
        rectTrans = GetComponent<RectTransform>();
        imageRect = contentImage.rectTransform;
        imageSysRect = sysImage.rectTransform;
    }
    public void Init(bool isSys, string content, ArrayList infoList, Color color, float speed, Vector2 startPos, float leftBoundary, GameObject prefab, Action<ChatBulletItem> onFinish)
    {
        if (rectTrans == null)
            rectTrans = GetComponent<RectTransform>();
        contentImage.SetActive(!isSys);
        sysImage.SetActive(isSys);
        if (infoList != null)
        {
            contentText.SetExtenalData(infoList);
            sysText.SetExtenalData(infoList);
        }
        contentText.text = content;
        sysText.text = content;
        contentText.color = color;
        sysText.color = color;
        sourcePrefab = prefab;
        onFinishCallback = onFinish;
        // 获取文本内容的理想宽度
        //LayoutRebuilder.ForceRebuildLayoutImmediate(contentText.rectTransform);
        float txtWidth = contentText.preferredWidth;
        float totalWidth = 0;
        float currentHeight = 0;
        if (!isSys)
        {
            // 计算背景图的总宽度 = 文本宽 + 左右Padding
            totalWidth = txtWidth + padding.x + padding.y;
            // 保持原有的高度
            currentHeight = rectTrans.sizeDelta.y;
            // 设置背景图(ContentImage)的大小
            if (imageRect != null)
            {
                imageRect.sizeDelta = new Vector2(totalWidth, currentHeight);
            }
        }
        else
        {
            // 计算背景图的总宽度 = 文本宽 + 左右Padding
            totalWidth = txtWidth + sysPadding.x + sysPadding.y;
            // 图片的高度
            currentHeight = 39;
            // 设置背景图(ContentImage)的大小
            if (imageSysRect != null)
            {
                imageSysRect.sizeDelta = new Vector2(totalWidth, currentHeight);
            }
        }
        // 设置根节点的大小
        rectTrans.sizeDelta = new Vector2(totalWidth, currentHeight);
        // 设置初始位置
        rectTrans.anchoredPosition = startPos;
        // 此时 rectTrans.rect.width 已经是我们手动设置的 totalWidth 了
        float endX = leftBoundary - totalWidth;
        float distance = Mathf.Abs(startPos.x - endX);
        float duration = distance / speed;
        // 5. 开始移动
        moveTweener?.Kill();
        moveTweener = rectTrans.DOAnchorPosX(endX, duration)
            .SetEase(Ease.Linear)
            .OnComplete(Recycle);
    }
    //ChatBulletItem最右侧的X坐标
    public float GetRightEdgeX()
    {
        return rectTrans.anchoredPosition.x + rectTrans.rect.width;
    }
    private void Recycle()
    {
        moveTweener?.Kill();
        moveTweener = null;
        onFinishCallback?.Invoke(this);
        if (GameObjectPoolManager.Instance != null && sourcePrefab != null)
        {
            GameObjectPoolManager.Instance.GetPool(sourcePrefab).Release(this.gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
    private void OnDestroy()
    {
        moveTweener?.Kill();
    }
}
Main/System/Chat/ChatBulletItem.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/System/Chat/ChatBulletItem.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: 351513bf72cdb364994c539f4dfae3bd
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Chat/ChatBulletView.cs
New file
@@ -0,0 +1,130 @@
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class ChatBulletView : MonoBehaviour
{
    [SerializeField] GameObject bulletPrefab; // 必须赋值预制体
    [SerializeField] RectTransform bulletContainer;
    [SerializeField] float bulletSpeed = 150f;
    [SerializeField] float position1Y = 220f;
    [SerializeField] Vector2 randomYRange = new Vector2(200f, -200);
    [SerializeField] float safeSpacing = 20f;
    [SerializeField] Color defaultColor = Color.white;
    private ChatBulletItem lastItemOnPos1;
    private float screenWidth;
    private float screenLeftX;
    ChatManager manager { get { return ChatManager.Instance; } }
    private void Awake()
    {
        screenWidth = 750;
        screenLeftX = -screenWidth / 2;
        GameObjectPoolManager.Instance.CacheGameObject(bulletPrefab, 10, false);
    }
    private void Start()
    {
        ChatManager.Instance.OnUpdateTalkEvent += OnUpdateTalkEvent;
    }
    private void OnDestroy()
    {
        ChatManager.Instance.OnUpdateTalkEvent -= OnUpdateTalkEvent;
    }
    private void OnUpdateTalkEvent(ChatChannel channel, TalkData data)
    {
        if (!ChatManager.Instance.GetBulletSetting(channel))
            return;
        // 0-系统 1-日期 2-自己 3-其他玩家
        int type = manager.GetTalkDataType(data);
        if (type == 1)
            return;
        SpawnBullet(channel, type == 0, type == 0 ? data.Content : GetBulletContent(channel, data), data.InfoList);
    }
    private string GetBulletContent(ChatChannel channel, TalkData data)
    {
        switch (channel)
        {
            case ChatChannel.World:
                string serverName = ServerListCenter.Instance.GetServerName((int)data.ServerID);
                return Language.Get("Chat16", serverName, data.Name, data.Content);
            case ChatChannel.Guild:
                return Language.Get("Chat16", Language.Get("ChatTab1"), data.Name, data.Content);
            default:
                return data.Content;
        }
    }
    private void SpawnBullet(ChatChannel channel, bool isSys, string content, ArrayList infoList)
    {
        if (bulletPrefab == null || GameObjectPoolManager.Instance == null)
            return;
        // 【关键修改】从特定对象池获取对象
        GameObject obj = GameObjectPoolManager.Instance.RequestGameObject(bulletPrefab);
        // 【重要】对象池管理器会把父节点设为null,这里必须重设回UI容器
        obj.transform.SetParent(bulletContainer, false);
        obj.transform.localScale = Vector3.one;
        obj.SetActive(true);
        ChatBulletItem item = obj.GetComponent<ChatBulletItem>();
        if (item == null)
            item = obj.AddComponent<ChatBulletItem>();
        Color color = GetChannelColor(channel);
        float spawnY = CalculateSpawnY();
        Vector2 startPos = new Vector2(screenWidth / 2, spawnY);
        item.Init(isSys, content, infoList, color, bulletSpeed, startPos, screenLeftX, bulletPrefab, OnBulletFinish);
        if (Mathf.Abs(spawnY - position1Y) < 1f)
        {
            lastItemOnPos1 = item;
        }
    }
    private float CalculateSpawnY()
    {
        bool pos1Available = true;
        if (lastItemOnPos1 != null && lastItemOnPos1.gameObject.activeSelf)
        {
            // 上一条弹幕还没完全离开右侧安全区
            float spawnX = screenWidth / 2;
            if (lastItemOnPos1.GetRightEdgeX() > spawnX - safeSpacing)
            {
                pos1Available = false;
            }
        }
        if (pos1Available)
        {
            return position1Y;
        }
        else
        {
            return Random.Range(randomYRange.x, randomYRange.y);
        }
    }
    private void OnBulletFinish(ChatBulletItem item)
    {
        if (item == lastItemOnPos1)
        {
            lastItemOnPos1 = null;
        }
    }
    private Color GetChannelColor(ChatChannel channel)
    {
        if (!manager.chatChannelBulletColorDict.TryGetValue(channel, out var color))
            return defaultColor;
        return color;
    }
}
Main/System/Chat/ChatBulletView.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/System/Chat/ChatBulletView.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: 1bc8c847ece8a9440bb8422960593260
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Chat/ChatCenter.cs
File was deleted
Main/System/Chat/ChatCenter.cs.meta
File was deleted
Main/System/Chat/ChatChannelCell.cs
File was deleted
Main/System/Chat/ChatChannelCell.cs.meta
File was deleted
Main/System/Chat/ChatContentBehaviour.cs
File was deleted
Main/System/Chat/ChatContentBehaviour.cs.meta
File was deleted
Main/System/Chat/ChatData.cs
File was deleted
Main/System/Chat/ChatData.cs.meta
File was deleted
Main/System/Chat/ChatDateCell.cs
New file
@@ -0,0 +1,21 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class ChatDateCell : MonoBehaviour
{
    [SerializeField] Text txtDate;
    ChatManager manager { get { return ChatManager.Instance; } }
    public void Refresh(CellView cell)
    {
        if (!manager.TryGetChatData(ChatManager.Instance.nowChatChannel, cell.index, out TalkData data))
            return;
        txtDate.text = data.Content;
    }
    public float GetHeight(string content, ArrayList list)
    {
        return 30;
    }
}
Main/System/Chat/ChatDateCell.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/System/Chat/ChatDateCell.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: f5a85178c18f4d24f99a1791bc22b0e3
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Chat/ChatExtraOneCell.cs
File was deleted
Main/System/Chat/ChatExtraOneCell.cs.meta
File was deleted
Main/System/Chat/ChatFloatWin.cs
File was deleted
Main/System/Chat/ChatFloatWin.cs.meta
File was deleted
Main/System/Chat/ChatFriend.cs
File was deleted
Main/System/Chat/ChatFriend.cs.meta
File was deleted
Main/System/Chat/ChatFriendCell.cs
File was deleted
Main/System/Chat/ChatFriendCell.cs.meta
File was deleted
Main/System/Chat/ChatFriendTipCell.cs
File was deleted
Main/System/Chat/ChatItemCell.cs
File was deleted
Main/System/Chat/ChatItemCell.cs.meta
File was deleted
Main/System/Chat/ChatManager.cs
@@ -3,1447 +3,660 @@
using UnityEngine;
using System.Text;
using System;
using System.Text.RegularExpressions;
using LitJson;
using System.Linq;
public class ChatManager : GameSystemManager<ChatManager>
public partial class ChatManager : GameSystemManager<ChatManager>
{
    public int CHAT_INFO_CNT = 50;
    public ChatChannel nowChatChannel;
    public const int CHAT_TIP_CNT = 4;
    public const int CHAT_CELL_CNT = 6;
    public const int CHAT_INFO_LIMIT = 50;
    //<ChannelType,TalkData>
    public Dictionary<ChatChannel, List<TalkData>> talkDict = new Dictionary<ChatChannel, List<TalkData>>();
    public Dictionary<int, ChatBubbleData> chatBubbles = new Dictionary<int, ChatBubbleData>();
    //<ChannelType,时间戳>
    public Dictionary<int, int> chatChannelSendTime = new Dictionary<int, int>();
    public event Action<ChatChannel, TalkData> OnUpdateTalkEvent;
    public event Action OnUpdateTalkCacheListEvent;
    public readonly int BugleItem = 0;//喇叭物品id
    public Dictionary<int, int> chatChannelCD = new Dictionary<int, int>();
    public int[] areaMyColorArr;
    public Color32 areaMyColor;
    public int[] areaOtherColorArr;
    public Color32 areaOtherColor;
    public Dictionary<int, int[]> chatChannelBulletColorArrDict = new Dictionary<int, int[]>();
    public Dictionary<ChatChannel, Color32> chatChannelBulletColorDict = new Dictionary<ChatChannel, Color32>();
    static StringBuilder sb = new StringBuilder();
    /// <summary>
    /// 聊天信息
    /// </summary>
    private bool _lockUpdate = true;
    public bool lockUpdate {
        get {
            return _lockUpdate;
        }
        set {
            _lockUpdate = value;
            if (ChatFriend != null && ChatFriend.IsOpen)
            {
                ChatFriend.OnSetLock();
            }
        }
    }
    public int[] defaultChannelBulletColorArr;
    public Color32 defaultChannelBulletColor;
    public int characterLimit;
    public int sysBubbleID;
    public int[] sysBubbleColorArr;
    public Color32 sysBubbleColor;
    private Dictionary<ChatInfoType, List<ChatData>> chatDics = new Dictionary<ChatInfoType, List<ChatData>>();
    private Dictionary<ChatInfoType, bool> chatOpenDics = new Dictionary<ChatInfoType, bool>();
    public List<ChatData> chatDisplayList = new List<ChatData>();
    #region 私聊
    private Dictionary<int, List<ChatFriendData>> pteChatDics = new Dictionary<int, List<ChatFriendData>>();
    public event Action OnPteChatChangeEvent;
    public event Action<int> SelectRecentlyEvent;
    public static event OnChatPteRefresh OnRefreshPteChat;
    private int pteChatId = 0;
    public int PteChatID {
        get { return pteChatId; }
        set {
            if (pteChatId == value)
            {
                return;
            }
            pteChatId = value;
            if (OnPteChatChangeEvent != null)
            {
                OnPteChatChangeEvent();
            }
            if (ChatFriend != null && ChatFriend.IsOpen)
            {
                ChatFriend.RefreshChatInfo();
            }
        }
    }
    private string pteChatName = string.Empty;
    public string PteChatName {
        get { return pteChatName; }
        set { pteChatName = value; }
    }
    private ChatFriend m_ChatFriend;
    public ChatFriend ChatFriend {
        get { return m_ChatFriend; }
    }
    public void ClearPteChat(int playerId)
    public override void Init()
    {
        var id = (int)PlayerDatas.Instance.PlayerId + playerId;
        if (pteChatDics.ContainsKey(id))
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += OnBeforePlayerDataInitializeEvent;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
        GuildManager.Instance.EnterOrQuitGuildEvent += EnterOrQuitGuildEvent;
        var config = FuncConfigConfig.Get("TalkCD");
        chatChannelCD = ConfigParse.ParseIntDict(config.Numerical1);
        config = FuncConfigConfig.Get("TalkColor");
        areaMyColorArr = ConfigParse.GetMultipleStr<int>(config.Numerical1);
        areaMyColor = new Color32()
        {
            pteChatDics.Remove(id);
            if (OnRefreshPteChat != null)
            r = (byte)(areaMyColorArr.Length > 0 ? areaMyColorArr[0] : 0),
            g = (byte)(areaMyColorArr.Length > 1 ? areaMyColorArr[1] : 0),
            b = (byte)(areaMyColorArr.Length > 2 ? areaMyColorArr[2] : 0),
            a = (byte)(areaMyColorArr.Length > 3 ? areaMyColorArr[3] : 255),
        };
        areaOtherColorArr = ConfigParse.GetMultipleStr<int>(config.Numerical2);
        areaOtherColor = new Color32()
        {
            r = (byte)(areaOtherColorArr.Length > 0 ? areaOtherColorArr[0] : 0),
            g = (byte)(areaOtherColorArr.Length > 1 ? areaOtherColorArr[1] : 0),
            b = (byte)(areaOtherColorArr.Length > 2 ? areaOtherColorArr[2] : 0),
            a = (byte)(areaOtherColorArr.Length > 3 ? areaOtherColorArr[3] : 255),
        };
        chatChannelBulletColorArrDict = ConfigParse.ParseIntArrayDict(config.Numerical3);
        foreach (var kv in chatChannelBulletColorArrDict)
        {
            if (!IsValidChatChannel(kv.Key))
                continue;
            chatChannelBulletColorDict[(ChatChannel)kv.Key] = new Color32()
            {
                OnRefreshPteChat(null);
            }
                r = (byte)(kv.Value.Length > 0 ? kv.Value[0] : 0),
                g = (byte)(kv.Value.Length > 1 ? kv.Value[1] : 0),
                b = (byte)(kv.Value.Length > 2 ? kv.Value[2] : 0),
                a = (byte)(kv.Value.Length > 3 ? kv.Value[3] : 0),
            };
        }
        defaultChannelBulletColorArr = ConfigParse.GetMultipleStr<int>(config.Numerical4);
        defaultChannelBulletColor = new Color32()
        {
            r = (byte)(defaultChannelBulletColorArr.Length > 0 ? defaultChannelBulletColorArr[0] : 0),
            g = (byte)(defaultChannelBulletColorArr.Length > 1 ? defaultChannelBulletColorArr[1] : 0),
            b = (byte)(defaultChannelBulletColorArr.Length > 2 ? defaultChannelBulletColorArr[2] : 0),
            a = (byte)(defaultChannelBulletColorArr.Length > 3 ? defaultChannelBulletColorArr[3] : 255),
        };
        config = FuncConfigConfig.Get("TalkLimit");
        characterLimit = int.Parse(config.Numerical1);
        config = FuncConfigConfig.Get("TalkBubble");
        sysBubbleID = int.Parse(config.Numerical1);
        sysBubbleColorArr = ConfigParse.GetMultipleStr<int>(config.Numerical2);
        sysBubbleColor = new Color32()
        {
            r = (byte)(sysBubbleColorArr.Length > 0 ? sysBubbleColorArr[0] : 0),
            g = (byte)(sysBubbleColorArr.Length > 1 ? sysBubbleColorArr[1] : 0),
            b = (byte)(sysBubbleColorArr.Length > 2 ? sysBubbleColorArr[2] : 0),
            a = (byte)(sysBubbleColorArr.Length > 3 ? sysBubbleColorArr[3] : 255),
        };
    }
    public override void Release()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent -= OnBeforePlayerDataInitializeEvent;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk;
        GuildManager.Instance.EnterOrQuitGuildEvent -= EnterOrQuitGuildEvent;
    }
    //被踢出/退出工会时,切换聊天频道
    private void EnterOrQuitGuildEvent(bool obj)
    {
        if (!obj)
        {
            nowChatChannel = ChatChannel.World;
            nowChatTab = ChatTab.World;
        }
    }
    public void SelectRecentlyChat(int playerId)
    private void OnBeforePlayerDataInitializeEvent()
    {
        if (SelectRecentlyEvent != null)
        {
            SelectRecentlyEvent(playerId);
        }
    }
    #endregion
    private List<ChatData> chatlist = new List<ChatData>();
    private List<ChatData> chatUpList = new List<ChatData>();
    public delegate void OnChatRefresh(ChatInfoType type);
    public static event OnChatRefresh OnRefreshChat;
    public delegate void OnChatSelfRefresh(ChatData data);
    public static event OnChatSelfRefresh OnRefreshSelf;
    public delegate void OnChatPteRefresh(ChatFriendData data);
    public ChatInfoType presentChatType {
        get; set;
        talkDict.Clear();
        ParseChatBubbleConfig();
        nowChatChannel = ChatChannel.World;
        nowChatTab = ChatTab.World;
    }
    public event Action OnClickCloseChatEvent;
    public event Action<bool> OnChatExtentOpenEvent;
    public event Action<ChatData> chatFloatUpdate;
    // TeamModel teamModel {
    //     get {
    //         return ModelCenter.Instance.GetModel<TeamModel>();
    //     }
    // }
    // FriendsModel friendModel {
    //     get { return ModelCenter.Instance.GetModel<FriendsModel>(); }
    // }
    // EquipGemModel equipGemModel { get { return ModelCenter.Instance.GetModel<EquipGemModel>(); } }
    // EquipStrengthModel equipStrengthModel { get { return ModelCenter.Instance.GetModel<EquipStrengthModel>(); } }
    // EquipStarModel equipStarModel { get { return ModelCenter.Instance.GetModel<EquipStarModel>(); } }
    // EquipTrainModel equipTrainModel { get { return ModelCenter.Instance.GetModel<EquipTrainModel>(); } }
    // EquipModel equipModel { get { return ModelCenter.Instance.GetModel<EquipModel>(); } }
    PackManager packManager => PackManager.Instance;
    // DungeonAssistModel dungeonAssistModel { get { return ModelCenter.Instance.GetModel<DungeonAssistModel>(); } }
    public Dictionary<ChatInfoType, List<string>> achievementRandoms = new Dictionary<ChatInfoType, List<string>>();
    List<string> assistThankLanguages = new List<string>();
    Int2 assistThankLevelLimit = new Int2(150, 200);
    public ChatManager()
    private void OnPlayerLoginOk()
    {
        chatOpenDics.Add(ChatInfoType.System, true);
        chatOpenDics.Add(ChatInfoType.World, true);
        chatOpenDics.Add(ChatInfoType.CrossServer, true);
        chatOpenDics.Add(ChatInfoType.Area, true);
        chatOpenDics.Add(ChatInfoType.Team, true);
        chatOpenDics.Add(ChatInfoType.Invite, true);
        chatOpenDics.Add(ChatInfoType.Trumpet, true);
        chatOpenDics.Add(ChatInfoType.Fairy, true);
        chatOpenDics.Add(ChatInfoType.default1, true);
        lockUpdate = true;
        presentChatType = ChatInfoType.World;
        IsExtentOpen = false;
        // var _funcCfg = FuncConfigConfig.Get("BugleItem");
        // BugleItem = int.Parse(_funcCfg.Numerical1);
        DTC0102_tagCDBPlayer.switchAccountEvent += SwitchAccountEvent;
        // TODO YYL
        // StageLoad.Instance.onStageLoadFinish += OnStageLoadFinish;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += PlayerLoginOkEvent;
        InitChatRedpoints();
        // FuncConfigConfig _cfg = FuncConfigConfig.Get("RandomWord");
        // try
        // {
        //     achievementRandoms.Add(ChatInfoType.World, new List<string>(ConfigParse.GetMultipleStr(_cfg.Numerical1)));
        //     achievementRandoms.Add(ChatInfoType.Fairy, new List<string>(ConfigParse.GetMultipleStr(_cfg.Numerical2)));
        //     var json = LitJson.JsonMapper.ToObject(_cfg.Numerical3);
        //     foreach (var key in json.Keys)
        //     {
        //         var type = int.Parse(key);
        //         m_TaskRandomChats.Add(type, new List<string>(LitJson.JsonMapper.ToObject<string[]>(json[key].ToJson())));
        //     }
        //     assistThankLanguages.AddRange(ConfigParse.GetMultipleStr(_cfg.Numerical4));
        //     if (!string.IsNullOrEmpty(_cfg.Numerical5))
        //     {
        //         var levelArray = ConfigParse.GetMultipleStr<int>(_cfg.Numerical5);
        //         assistThankLevelLimit = new Int2(levelArray[0], levelArray[1]);
        //     }
        // }
        // catch (Exception e)
        // {
        //     Debug.LogError(e.Message);
        // }
        LoadBulletSettings();
    }
    private void PlayerLoginOkEvent()
    public void AddChatChannelSendTime(ChatChannel chatChannel, int time)
    {
        UpdateRedpoint(ChatInfoType.Team);
        UpdateRedpoint(ChatInfoType.Fairy);
        chatChannelSendTime[(int)chatChannel] = time;
    }
    private void SwitchAccountEvent()
    public bool TryGetChatChannelSendTime(ChatChannel chatChannel, out int time)
    {
        ClearAllChatInfo();
        return chatChannelSendTime.TryGetValue((int)chatChannel, out time);
    }
    void ChatReport(ChatInfoType chatType, string content, string toPlayer)
    public bool TryGetChatChannelSendCD(ChatChannel chatChannel, out int cd)
    {
        try
        {
            if (ChatCenter.Instance.IsChatBanned || ChatCenter.Instance.IsClientBan(chatType) ||
                IsInviteChat(content) || KillRegex.IsMatch(content))
            {
                return;
            }
            var channelName = string.Empty;
            switch (chatType)
            {
                case ChatInfoType.World:
                    channelName = Language.Get("ChatType_World");
                    break;
                case ChatInfoType.Area:
                    channelName = Language.Get("ChatType_Area");
                    break;
                case ChatInfoType.CrossServer:
                    channelName = Language.Get("ChatType_CrossServer");
                    break;
                case ChatInfoType.Team:
                    channelName = Language.Get("ChatType_Team");
                    break;
                case ChatInfoType.Invite:
                    channelName = Language.Get("ChatType_Invite");
                    break;
                case ChatInfoType.Trumpet:
                    channelName = Language.Get("ChatType_Trumpet");
                    break;
                case ChatInfoType.Fairy:
                    channelName = Language.Get("ChatType_Fairy");
                    break;
                case ChatInfoType.Friend:
                    channelName = Language.Get("PlayerDetail_PrivateChat");
                    break;
                case ChatInfoType.default1:
                    channelName = Language.Get("ChatType_default1");
                    break;
                default:
                    return;
            }
            // TODO YYL
            // OperationLogCollect.Instance.ChatReport(content, channelName, chatType == ChatInfoType.Friend ? toPlayer : string.Empty, chatType);
        }
        catch (Exception e)
        {
            Debug.Log(e.StackTrace + e.Message);
        }
        return chatChannelCD.TryGetValue((int)chatChannel, out cd);
    }
    public void SendChatInfo(ChatInfoType type, string msg, ChatExtraData? info = null , bool isDirtyWord = true,  bool isChatInfoCount = true)
    public bool IsCanSend(ChatChannel chatChannel, out int remainingSeconds)
    {
        bool isDirty = false;
        bool isVoice = ChatCenter.s_VoiceRegex.IsMatch(msg);
        if (CheckEmptyChat(msg))
        {
            SysNotifyMgr.Instance.ShowTip("CanootTalk14");
            return;
        }
        ChatReport(type, msg, PteChatName);
        if (!isVoice && !InviteRegex.IsMatch(msg))
        {
            if (isDirtyWord)
            {
                isDirty = DirtyWordConfig.IsDirtWord(msg);
                if (isDirty)
                    msg = DirtyWordConfig.IsDirtWord(msg, '*');
            }
            var length = GetChatMessageLength(msg);
            if (isChatInfoCount)
            {
                if (length > CHAT_INFO_CNT)
                {
                    ServerTipDetails.DisplayNormalTip(Language.Get("L1014"));
                    return;
                }
            }
            if (itemPlaceList.Count > 5)
            {
                SysNotifyMgr.Instance.ShowTip("ChatSendItemLimit");
                return;
            }
        }
        LanguageVerify.toPlayer = (uint)PteChatID;
        LanguageVerify.toPlayerName = PteChatName;
        LanguageVerify.Instance.VerifyChat(msg, type, (bool ok, string result) =>
         {
             if (ok)
             {
                 ChatCenter.RecentlyChat _recentlyChat = null;
                 if (!isDirty && !isVoice)
                 {
                     _recentlyChat = ChatCenter.Instance.SaveRecentlyChat(result);
                 }
                 msg = CheckHasItem(result, _recentlyChat);
                 ChatCenter.Instance.recentlyChat = null;
                 if (ChatCenter.Instance.IsChatBanned || ChatCenter.Instance.IsClientBan(type))
                 {
                     var toPlayer = PteChatID;
                     if (info.HasValue && info.Value.infoint1 == 0)
                     {
                         toPlayer = info.Value.infoint1;
                     }
                     ChatCenter.Instance.HandleChatBanned(type, msg, toPlayer);
                     return;
                 }
                 switch (type)
                 {
                     case ChatInfoType.World:
                     case ChatInfoType.Fairy:
                         if (IsAssitChat(msg) != 0)
                         {
                            //  TODO YYL
                            // teamModel.RequestAssistAutoMatch();
                         }
                         break;
                 }
                 switch (type)
                 {
                     case ChatInfoType.World:
                         {
                             C0201_tagCTalkGong chatPack = new C0201_tagCTalkGong();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.Area:
                         {
                             C0207_tagCTalkArea chatPack = new C0207_tagCTalkArea();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.CrossServer:
                         {
                             C0208_tagCTalkCountry chatPack = new C0208_tagCTalkCountry();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.Team:
                         {
                             C0205_tagCTalkDui chatPack = new C0205_tagCTalkDui();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.Friend:
                         {
                             var _toPlayer = PteChatID;
                             if (info.HasValue && info.Value.infoint1 == 0)
                             {
                                 _toPlayer = info.Value.infoint1;
                             }
                             if (_toPlayer == 0)
                             {
                                 SysNotifyMgr.Instance.ShowTip("NoChatTarget");
                                 return;
                             }
                             SendFriendChat(msg, _toPlayer);
                         }
                         break;
                     case ChatInfoType.Fairy:
                         {
                             C0203_tagCTalkBang chatPack = new C0203_tagCTalkBang();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.Trumpet:
                         {
                             if (info.HasValue)
                             {
                                 CA217_tagCMPYSpeaker _pak = new CA217_tagCMPYSpeaker();
                                 _pak.SpeakerType = 1;
                                 _pak.IsUseGold = 0;
                                 _pak.ItemIndex = (byte)info.Value.infoint1;
                                 _pak.TextLen = (ushort)GetUTF8InfoLen(msg);
                                 _pak.Text = msg;
                                 GameNetSystem.Instance.SendInfo(_pak);
                             }
                         }
                         break;
                     case ChatInfoType.default1:
                         {
                             if (PlayerDatas.Instance.baseData.faction == 0)
                             {
                                 SysNotifyMgr.Instance.ShowTip("FactionChatLimit");
                                 return;
                             }
                             CA216_tagCMPyTalk _pak = new CA216_tagCMPyTalk();
                             _pak.TalkType = 100;
                             _pak.Len = (ushort)GetUTF8InfoLen(msg);
                             _pak.Content = msg;
                             GameNetSystem.Instance.SendInfo(_pak);
                         }
                         break;
                 }
             }
         });
    }
    int GetChatMessageLength(string message)
    {
        message = WordAnalysis.Color_Start_Regex.Replace(message, string.Empty);
        message = WordAnalysis.Color_End_Regex.Replace(message, string.Empty);
        return message.Length;
    }
    bool CheckEmptyChat(string msg)
    {
        if (string.IsNullOrEmpty(msg.Replace(" ", string.Empty)))
        {
        remainingSeconds = 0;
        // 没有配置的不限制
        if (!TryGetChatChannelSendCD(chatChannel, out int cd))
            return true;
        }
        // 没有发送过
        if (!TryGetChatChannelSendTime(chatChannel, out int time) || time <= 0)
            return true;
        DateTime endDateTime = TimeUtility.GetTime((uint)(cd + time + 1));
        TimeSpan remainingTime = endDateTime - TimeUtility.ServerNow;
        remainingSeconds = (int)remainingTime.TotalSeconds;
        if (remainingSeconds <= 0)
            return true;
        return false;
    }
    /// <summary>
    /// 世界频道
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H0201_tagTalkGong vNetData, uint time = 0)
    // 0-系统 1-日期 2-自己 3-其他玩家
    public int GetTalkDataType(TalkData talkData)
    {
        //  TODO YYL
        // if (friendModel.GetFirendInfo(vNetData.PlayerID, (byte)GroupType.Balcklist) != null)//黑名单拦截
        // {
        //     return;
        // }
        ChatData chatData = null;
        var content = vNetData.Content;
        if (IsInviteChat(vNetData.Content))
        if (talkData.isSystem)
        {
            content = InviteRegex.Replace(vNetData.Content, "");
            // if (teamModel.myTeam.GetIndexOfMember((int)vNetData.PlayerID) != -1)
            // {
            //     content = StringUtility.Contact("<color=#f8983b>", vNetData.Name, "</color>", ":", content);
            // }
            // else
            {
                content = StringUtility.Contact("<color=#f8983b>", vNetData.Name, "</color>", ":", content, string.Format("<color=#00ff00><a>{0}|invite={1}</a></color>", Language.Get("L1013"), vNetData.PlayerID));
            }
            chatData = new ChatInviteData(content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
            KeepChatInfo(chatData);
            return;
            return 0;
        }
        if (IsAssitChat(content, true) == 2 && vNetData.PlayerID != PlayerDatas.Instance.baseData.PlayerID)
        else if (talkData.isDate)
        {
            content = StringUtility.Contact(content, Language.Get("InviteTeam", vNetData.PlayerID));
            return 1;
        }
        chatData = new ChatWorldData(content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
        if (time != 0)
        else if (talkData.PlayerID == PlayerDatas.Instance.PlayerId)
        {
            chatData.createTime = TimeUtility.GetTime(time);
        }
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
    }
    /// <summary>
    /// 区域频道
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H0207_tagTalkArea vNetData)
    {
        //  TODO YYL
        // if (friendModel.GetFirendInfo(vNetData.PlayerID, (byte)GroupType.Balcklist) != null)//黑名单拦截
        // {
        //     return;
        // }
        ChatData chatData = new ChatAreaData(vNetData.Content, (int)vNetData.PlayerID, vNetData.SrcName, vNetData.Extras);
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
    }
    // 阵营频道
    public void RevChatInfo(HA707_tagMCPyTalk vNetData)
    {
        //  TODO YYL
        // if (friendModel.GetFirendInfo(vNetData.PlayerID, (byte)GroupType.Balcklist) != null)//黑名单拦截
        // {
        //     return;
        // }
        ChatData chatData = new ChatFactionData(vNetData.Content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
    }
    /// <summary>
    /// 跨服聊天
    /// </summary>
    /// <param name="package"></param>
    public void RevChatInfo(H0208_tagTalkCountry package)
    {
        //  TODO YYL
        // if (friendModel.GetFirendInfo(package.PlayerID, (byte)GroupType.Balcklist) != null)//黑名单拦截
        // {
        //     return;
        // }
        if (!FuncOpen.Instance.IsFuncOpen(162))
        {
            return;
        }
        ChatData chatData = new ChatCrossServerData(package.Content, (int)package.PlayerID, package.Name, package.Extras);
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
    }
    /// <summary>
    /// 喇叭喊话
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(HA9A3_tagGCPYSpeakerContent vNetData)
    {
        // ChatData chatData = new ChatTrumpetData(vNetData.Text, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras, vNetData.SpeakerType, vNetData.AccID);
        // LocalChatHistory.Save(chatData as ChatUeseData);
        // KeepChatInfo(chatData);
        // ServerTipDetails.ShowTrumpetTip(chatData as ChatTrumpetData);
    }
    /// <summary>
    /// 家族频道
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H0203_tagTalkBang vNetData, uint time = 0)
    {
        var content = vNetData.Content;
        if (IsAssitChat(content, true) == 1 && vNetData.PlayerID != PlayerDatas.Instance.baseData.PlayerID)
        {
            content = StringUtility.Contact(content, Language.Get("InviteTeam", vNetData.PlayerID));
        }
        ChatData chatData = new ChatFamilyData(content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
        if (time != 0)
        {
            chatData.createTime = TimeUtility.GetTime(time);
        }
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
        ReceiveNewChat(ChatInfoType.Fairy);
    }
    /// <summary>
    /// 后端缓存
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(HB311_tagGCTalkCache package)
    {
        for (int i = 0; i < package.Count; i++)
        {
            var data = package.InfoList[i];
            if (data.ChannelType == 1)
            {
                RevChatInfo(new H0201_tagTalkGong()
                {
                    socketType = package.socketType,
                    Content = data.Content,
                    Extras = data.Extras,
                    ExtraValue = 0,
                    Name = data.Name,
                    NameLen = data.NameLen,
                    Len = data.Len,
                    PlayerID = data.PlayerID,
                }, data.Time);
            }
            else if (data.ChannelType == 2)
            {
                RevChatInfo(new H0203_tagTalkBang()
                {
                    socketType = package.socketType,
                    Content = data.Content,
                    Extras = data.Extras,
                    ExtraValue = 0,
                    Len = data.Len,
                    Name = data.Name,
                    NameLen = data.NameLen,
                    PlayerID = data.PlayerID,
                });
            }
        }
    }
    /// <summary>
    /// 好友私聊
    /// </summary>
    /// <param name="vNetData"></param>
    public void SetChatFreind(ChatFriend inst)
    {
        m_ChatFriend = inst;
    }
    public void RevChatInfo(H0206_tagTalkMi vNetData)
    {
        if (Regex.IsMatch(vNetData.Content, KILL_IDENTIFY))
        {
            if (vNetData.PlayerID == PlayerDatas.Instance.baseData.PlayerID)
            {
                return;
            }
        }
        ChatFriendData chatData = new ChatFriendData(vNetData.Content, (int)vNetData.PlayerID, vNetData.SrcName, vNetData.Extras, vNetData.ToName, vNetData.TalkType, vNetData.ToPlayerID);
        FitterChat(chatData);
        AddPteChat(chatData, false);
        LocalChatHistory.Save(chatData as ChatUeseData);
        if (chatData.player == PlayerDatas.Instance.baseData.PlayerID)
        {
            if (OnRefreshSelf != null)
            {
                OnRefreshSelf(chatData);
            }
        }
        if (chatData.IsSound)
        {
            // ChatCenter.Instance.CheckAutoPlayVoice(chatData);
        }
    }
    private void AddPteChat(ChatFriendData chatData, bool isLocal)
    {
        List<ChatFriendData> list = null;
        pteChatDics.TryGetValue(chatData.player + chatData.toPlayer, out list);
        if (list != null)
        {
            if (list.Count > CHAT_INFO_CNT)
            {
                ChatUeseData outData = list[0];
                list.RemoveAt(0);
                outData = null;
            }
            list.Add(chatData);
            return 2;
        }
        else
        {
            list = new List<ChatFriendData>();
            list.Add(chatData);
            pteChatDics.Add(chatData.player + chatData.toPlayer, list);
        }
        if (OnRefreshPteChat != null && !isLocal)
        {
            OnRefreshPteChat(chatData);
        }
    }
    /// <summary>
    /// 队伍频道
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H0205_tagTalkDui vNetData)
    {
        ChatData chatData = new ChatTeamData(vNetData.Content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
        KeepChatInfo(chatData);
        ReceiveNewChat(ChatInfoType.Team);
    }
    /// <summary>
    /// GM
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H3202_tagServerResponse vNetData)
    {
        ChatData chatData = new ChatSystemData(vNetData.Message);
        KeepChatInfo(chatData);
    }
    /// <summary>
    /// 系统提示
    /// </summary>
    /// <param name="msg"></param>
    public void RevChatInfo(string msg)
    {
        ChatData chatData = new ChatSystemData(msg);
        KeepChatInfo(chatData);
    }
    public void RevChatInfo(string msg, ArrayList infoList, ChatInfoType type = ChatInfoType.System)
    {
        ChatData chatData = null;
        switch (type)
        {
            case ChatInfoType.System:
                {
                    chatData = new ChatSystemData(msg);
                    chatData.infoList.AddRange(infoList);
                }
                break;
            case ChatInfoType.FairyTip:
            case ChatInfoType.FairyQuestion:
                {
                    chatData = new ChatFamilyData(msg, 0, string.Empty, string.Empty, type);
                    chatData.infoList.AddRange(infoList);
                }
                break;
            case ChatInfoType.TeamTip:
                {
                    chatData = new ChatTeamData(msg, 0, string.Empty, string.Empty, type);
                    chatData.infoList.AddRange(infoList);
                }
                break;
            case ChatInfoType.default1:
            case ChatInfoType.default2:
                {
                    chatData = new ChatFactionData(msg, 0, string.Empty, string.Empty, type);
                    chatData.infoList.AddRange(infoList);
                }
                break;
        }
        if (chatData != null)
        {
            KeepChatInfo(chatData);
        }
    }
    /// <summary>
    /// 获取频道聊天数据
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public List<ChatData> GetChatInfo(ChatInfoType type)
    {
        List<ChatData> list = null;
        chatDics.TryGetValue(type, out list);
        return list;
    }
    /// <summary>
    /// 获取好友聊天数据
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public List<ChatFriendData> GetChatInfo(int player)
    {
        List<ChatFriendData> list = null;
        pteChatDics.TryGetValue(player + (int)PlayerDatas.Instance.baseData.PlayerID, out list);
        return list;
    }
    /// <summary>
    /// 获取主界面的聊天数据
    /// </summary>
    /// <returns></returns>
    public List<ChatData> GetChatInfo()
    {
        return chatlist;
    }
    public List<ChatData> GetChatUpInfo()
    {
        return chatUpList;
    }
    public ChatData GetChatInfo(ChatInfoType type, int index)
    {
        List<ChatData> list = null;
        chatDics.TryGetValue(type, out list);
        if (list != null)
        {
            return list[index];
        }
        return null;
    }
    public void KeepLocalChat(ChatData chat)
    {
        if (chat.type == ChatInfoType.Friend)
        {
            FitterChat(chat);
            AddPteChat(chat as ChatFriendData, true);
        }
        else
        {
            KeepChatInfo(chat);
            return 3;
        }
    }
    private void KeepChatInfo(ChatData data)
    {
        KeepAllTypeChat(data);
        List<ChatData> list = null;
        chatDics.TryGetValue(data.type, out list);
        if (list != null)
        {
            if (list.Count >= CHAT_INFO_CNT)
            {
                ChatData outData = list[0];
                list.RemoveAt(0);
                outData = null;
            }
            list.Add(data);
        }
        else
        {
            list = new List<ChatData>();
            list.Add(data);
            chatDics.Add(data.type, list);
        }
        if (chatFloatUpdate != null)
        {
            chatFloatUpdate(data);
        }
        if (OnRefreshChat != null)
            OnRefreshChat(data.type);
        if (data.type != ChatInfoType.System && (data as ChatUeseData).player == PlayerDatas.Instance.baseData.PlayerID)
        {
            if (OnRefreshSelf != null)
                OnRefreshSelf(data);
        }
        // if ((data is ChatUeseData)
        //     && (data as ChatUeseData).IsSound)
        // {
        //     ChatCenter.Instance.CheckAutoPlayVoice(data);
        // }
    }
    private void KeepAllTypeChat(ChatData data)
    {
        if (!FitterChat(data))
        {
            if (chatlist.Count >= CHAT_TIP_CNT)
            {
                ChatData outData = chatlist[0];
                chatlist.RemoveAt(0);
            }
            if (chatUpList.Count >= CHAT_INFO_CNT)
            {
                ChatData outData = chatUpList[0];
                chatUpList.RemoveAt(0);
            }
            chatlist.Add(data);
            chatUpList.Add(data);
        }
    }
    private bool FitterChat(ChatData data)
    {
        if (data.type == ChatInfoType.System)
        {
            return true;
        }
        if (IsItemChat(data.content))
        {
            string content = data.content;
            MatchCollection matchArray = HrefAnalysis.EquipDetailRegex.Matches(content);
            int index = 0;
            for (int i = 0; i < matchArray.Count; i++)
            {
                data.richText.Append(content.Substring(index, matchArray[i].Index - index));
                index = matchArray[i].Index + matchArray[i].Length;
                string detail = matchArray[i].Groups[1].Value;
                var itemplusArray = detail.Split('|');
                if (itemplusArray.Length > 1)
                {
                    var itemId = int.Parse(itemplusArray[0]);
                    ItemConfig itemConfig = ItemConfig.Get(itemId);
                    if (itemConfig != null)
                    {
                        try
                        {
                            int[] equipGems = null;
                            if (itemplusArray.Length >= 8)
                            {
                                equipGems = new int[4];
                                for (int j = 0; j < equipGems.Length; j++)
                                {
                                    equipGems[j] = int.Parse(itemplusArray[4 + j]);
                                }
                            }
                            ItemTipUtility.CustomEquipWash equipWash = new ItemTipUtility.CustomEquipWash();
                            if (itemplusArray.Length >= 15)
                            {
                                equipWash.LV = int.Parse(itemplusArray[11]);
                                equipWash.Value = new int[3];
                                equipWash.Value[0] = int.Parse(itemplusArray[12]);
                                equipWash.Value[1] = int.Parse(itemplusArray[13]);
                                equipWash.Value[2] = int.Parse(itemplusArray[14]);
                            }
                            List<int> suitPlaces = null;
                            if (itemplusArray.Length > 15)
                            {
                                suitPlaces = new List<int>();
                                for (int j = 0; j < 8; j++)
                                {
                                    var place = int.Parse(itemplusArray[15 + j]);
                                    if (place != 0)
                                    {
                                        suitPlaces.Add(place);
                                    }
                                }
                            }
                            int[] suitLevels = null;
                            if (itemplusArray.Length > 15)
                            {
                                suitLevels = new int[3];
                                for (int j = 0; j < 3; j++)
                                {
                                    suitLevels[j] = int.Parse(itemplusArray[23 + j]);
                                }
                            }
                            int[] placeStars = null;
                            if (itemplusArray.Length > 15)
                            {
                                placeStars = new int[8];
                                for (int j = 0; j < 8; j++)
                                {
                                    placeStars[j] = int.Parse(itemplusArray[26 + j]);
                                }
                            }
                            ItemTipUtility.CustomItemPlus itemplus = new ItemTipUtility.CustomItemPlus()
                            {
                                ItemID = itemId,
                                count = int.Parse(itemplusArray[1]),
                                Equipped = int.Parse(itemplusArray[2]),
                                UserData = itemplusArray[3],
                                Stone = equipGems,
                                PlusLV = itemplusArray.Length >= 9 ? int.Parse(itemplusArray[8]) : 0,
                                Star = itemplusArray.Length >= 10 ? int.Parse(itemplusArray[9]) : 0,
                                EvolveLV = itemplusArray.Length >= 11 ? int.Parse(itemplusArray[10]) : 0,
                                Wash = equipWash,
                                Equips = null,
                                suitPlaces = suitPlaces == null ? null : suitPlaces.ToArray(),
                                suitLevels = suitLevels,
                                placeStars = placeStars,
                            };
                            string append = string.Format("<a><Word info=item id={0} itemplus={1} chatsend=1/>|showitem={0} itemplus={1}</a>",
                                      itemId, LitJson.JsonMapper.ToJson(itemplus));
                            append = UIHelper.AppendColor(itemConfig.ItemColor, append);
                            data.richText.Append(append);
                        }
                        catch (Exception e)
                        {
                            data.richText.Append(itemConfig.ItemName);
                            Debug.Log(e.Message);
                        }
                    }
                }
                else
                {
                    data.richText.Append(matchArray[i].Value);
                }
            }
            data.richText.Append(content.Substring(index, content.Length - index));
        }
        if (data.type == ChatInfoType.Friend)
        {
            return true;
        }
        if (data.content.Equals(string.Empty))
        {
            return true;
        }
        return !chatOpenDics[data.type];
    }
    public Dictionary<ChatInfoType, bool> GetChatOpen()
    {
        return chatOpenDics;
    }
    #region 组队邀请
    private const string INVITE_IDENTIFY = "<i>";
    public static Regex InviteRegex = new Regex(@INVITE_IDENTIFY, RegexOptions.Singleline);
    public void SendInvite(string msg)
    {
        SendChatInfo(ChatInfoType.World, StringUtility.Contact(msg, INVITE_IDENTIFY));
    }
    public bool IsInviteChat(string msg)
    {
        return InviteRegex.IsMatch(msg);
    }
    #endregion
    #region 发送物品
    public List<ItemModel> itemPlaceList = new List<ItemModel>();
    public bool IsItemChat(string msg)
    {
        return HrefAnalysis.EquipDetailRegex.IsMatch(msg);
    }
    public string CheckHasItem(string msg, ChatCenter.RecentlyChat _recently)
    {
        return string.Empty;
        // if (!HrefAnalysis.EquipRegex.IsMatch(msg))
        // {
        //     return msg;
        // }
        // sb.Length = 0;
        // MatchCollection matchArray = HrefAnalysis.EquipRegex.Matches(msg);
        // int index = 0;
        // for (int i = 0; i < matchArray.Count; i++)
        // {
        //     sb.Append(msg.Substring(index, matchArray[i].Index - index));
        //     if (ChatCenter.Instance.recentlyChat != null)
        //     {
        //         if (i < ChatCenter.Instance.recentlyChat.itemIndexs.Count)
        //         {
        //             var _index = ChatCenter.Instance.recentlyChat.itemIndexs[i];
        //             sb.Append(" ");
        //             sb.Append(ChatCenter.Instance.recentlyChat.itemInfos[_index]);
        //             sb.Append(" ");
        //         }
        //         index = matchArray[i].Index + matchArray[i].Length;
        //         continue;
        //     }
        //     var _length = sb.Length;
        //     if (i < itemPlaceList.Count)
        //     {
        //         var itemConfig = ItemConfig.Get((int)itemPlaceList[i].itemId);
        //         if (itemConfig.ItemName == matchArray[i].Groups[1].Value)
        //         {
        //             bool equip = itemPlaceList[i].packType == PackType.Equip;
        //             sb.Append("#item#");
        //             AppendValue(sb, itemPlaceList[i].itemId);
        //             AppendValue(sb, itemPlaceList[i].count);
        //             AppendValue(sb, equip ? 1 : 0);
        //             AppendValue(sb, itemPlaceList[i].itemInfo.userData);
        //             if (equip)
        //             {
        //                 var position = new Int2(itemConfig.LV, itemConfig.EquipPlace);
        //                 int[] equipGems;
        //                 equipGemModel.TryGetEquipGems(itemPlaceList[i].gridIndex, out equipGems);
        //                 for (int j = 0; j < EquipGemModel.EQUIPGEM_HOLE_COUNT; j++)
        //                 {
        //                     AppendValue(sb, equipGems != null && j < equipGems.Length ? equipGems[j] : 0);
        //                 }
        //                 var strengthLevel = equipStrengthModel.GetStrengthLevel(itemConfig.LV, itemConfig.EquipPlace);
        //                 AppendValue(sb, strengthLevel);
        //                 var starLevel = equipStarModel.GetEquipStarLevel(new Int2(itemConfig.LV, itemConfig.EquipPlace));
        //                 AppendValue(sb, starLevel);
        //                 var evolveLevel = equipStrengthModel.GetStrengthEvolveLevel(position.x, position.y);
        //                 AppendValue(sb, evolveLevel);
        //                 AppendValue(sb, equipTrainModel.GetTrainLevel(position));
        //                 var property = equipTrainModel.GetTrainedProperties(position);
        //                 AppendValue(sb, property.x);
        //                 AppendValue(sb, property.y);
        //                 AppendValue(sb, property.z);
        //                 for (int place = 1; place <= 12; place++)
        //                 {
        //                     if (place > 8)
        //                     {
        //                         continue;
        //                     }
        //                     var equipGuid = equipModel.GetEquip(new Int2(position.x, place));
        //                     if (!string.IsNullOrEmpty(equipGuid))
        //                     {
        //                         var equipItem = packManager.GetItemByGuid(equipGuid);
        //                         if (equipItem.config.SuiteiD > 0)
        //                         {
        //                             AppendValue(sb, place);
        //                             continue;
        //                         }
        //                     }
        //                     AppendValue(sb, 0);
        //                 }
        //                 AppendValue(sb, equipModel.GetSuitLevel(itemConfig.LV, EquipSuitType.TwoSuit));
        //                 AppendValue(sb, equipModel.GetSuitLevel(itemConfig.LV, EquipSuitType.FiveSuit));
        //                 AppendValue(sb, equipModel.GetSuitLevel(itemConfig.LV, EquipSuitType.EightSuit));
        //                 for (int place = 1; place <= 8; place++)
        //                 {
        //                     var equipGuid = equipModel.GetEquip(new Int2(position.x, place));
        //                     if (!string.IsNullOrEmpty(equipGuid))
        //                     {
        //                         var equipItem = packManager.GetItemByGuid(equipGuid);
        //                         if (ItemLogicUtility.Instance.IsSuitEquip(equipItem.itemId))
        //                         {
        //                             AppendValue(sb, equipStarModel.GetStarLevel(new Int2(position.x, place)));
        //                         }
        //                         else
        //                         {
        //                             AppendValue(sb, -1);
        //                         }
        //                     }
        //                     else
        //                     {
        //                         AppendValue(sb, -1);
        //                     }
        //                 }
        //             }
        //             sb.Remove(sb.Length - 1, 1);
        //             sb.Append("#item#");
        //             if (_recently != null)
        //             {
        //                 _recently.Add(itemConfig.ItemName, sb.ToString().Substring(_length));
        //             }
        //         }
        //         else
        //         {
        //             sb.Append(matchArray[i].Value);
        //         }
        //     }
        //     else
        //     {
        //         sb.Append(matchArray[i].Value);
        //     }
        //     index = matchArray[i].Index + matchArray[i].Length;
        // }
        // sb.Append(msg.Substring(index, msg.Length - index));
        // return sb.ToString();
    }
    void AppendValue(StringBuilder sb, object _object)
    {
        sb.Append(_object);
        sb.Append('|');
    }
    #endregion
    #region 好友私聊
    public static Regex KillRegex = new Regex(@KILL_IDENTIFY, RegexOptions.Singleline);
    public const string KILL_IDENTIFY = "<k>";
    public void SendFriendChat(string msg, int player)
    {
        C0209_tagCTalkMiFix chatPack = new C0209_tagCTalkMiFix();
        chatPack.TalkType = 1;
        chatPack.PlayerID = (uint)player;
        chatPack.Len = (ushort)GetUTF8InfoLen(msg);
        chatPack.Content = msg;
        GameNetSystem.Instance.SendInfo(chatPack);
    }
    #endregion
    /// <summary>
    /// 清屏
    /// </summary>
    public void ClearAllChatInfo()
    {
        chatDics.Clear();
        chatlist.Clear();
        chatUpList.Clear();
        pteChatDics.Clear();
        if (OnRefreshChat != null)
        {
            OnRefreshChat(presentChatType);
        }
    }
    public void ClearChatInfo(ChatInfoType type)
    {
        List<ChatData> list = null;
        if (chatDics.TryGetValue(type, out list))
        {
            list.Clear();
            if (OnRefreshChat != null)
            {
                OnRefreshChat(type);
            }
        }
    }
    public static int GetUTF8InfoLen(string msg)
    {
        return Encoding.UTF8.GetBytes(msg).Length;
    }
    public void CloseChatBtnClick()
    public bool TryGetBubble(int id, out ChatBubbleData bubble)
    {
        if (OnClickCloseChatEvent != null)
        {
            OnClickCloseChatEvent();
        }
        return chatBubbles.TryGetValue(id, out bubble);
    }
    public bool IsExtentOpen { get; set; }
    public void OpenChatExtent(bool open)
    void ParseChatBubbleConfig()
    {
        if (OnChatExtentOpenEvent != null)
        var configs = ChatBubbleBoxConfig.GetValues();
        for (int i = 0; i < configs.Count; i++)
        {
            OnChatExtentOpenEvent(open);
        }
    }
    #region 陌生人聊天
    public event Action OpenPteChatEvent;
    public void OpenFriendWin()
    {
        if (OpenPteChatEvent != null)
        {
            OpenPteChatEvent();
        }
    }
    #endregion
    #region 主界面聊天频道显示设置
    public void SetChatChannelShow(ChatInfoType type, bool open)
    {
        if (chatOpenDics.ContainsKey(type))
        {
            chatOpenDics[type] = open;
        }
    }
    public void GetChatChannelShow()
    {
        chatOpenDics[ChatInfoType.World] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelWorld);
        chatOpenDics[ChatInfoType.Area] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelArea);
        chatOpenDics[ChatInfoType.Fairy] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelGrad);
        chatOpenDics[ChatInfoType.Invite] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelATeam);
        chatOpenDics[ChatInfoType.System] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelSystem);
        chatOpenDics[ChatInfoType.Team] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelTeam);
        chatOpenDics[ChatInfoType.Trumpet] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelBugle);
        chatOpenDics[ChatInfoType.CrossServer] = true;
        chatOpenDics[ChatInfoType.default1] = true;
    }
    #endregion
    #region 日常跳转
    private string[] realmRandomChats = new string[2] { "DailyQuestRealmTalk1", "DailyQuestRealmTalk2" };
    private string[] dungeonRandomChats = new string[2] { "DailyQuestAssitmTalk1", "DailyQuestAssitmTalk2" };
    public bool openFromDaily { get; set; }
    public string GetAssitRandomChat(ChatInfoType _type)
    {
        int _index = UnityEngine.Random.Range(0, 2);
        switch (_type)
        {
            case ChatInfoType.World:
                if (_index == 0)
                {
                    return Language.Get(dungeonRandomChats[0]);
                }
                else
                {
                    return Language.Get(dungeonRandomChats[1], PlayerDatas.Instance.baseData.FightPower);
                }
            case ChatInfoType.Fairy:
                return Language.Get(realmRandomChats[_index]);
        }
        return string.Empty;
    }
    public bool openFromFairyTask { get; set; }
    Dictionary<int, List<string>> m_TaskRandomChats = new Dictionary<int, List<string>>();
    public string GetTaskRandomChat(ChatInfoType _type)
    {
        if (m_TaskRandomChats.ContainsKey((int)_type))
        {
            var list = m_TaskRandomChats[(int)_type];
            var index = UnityEngine.Random.Range(0, list.Count);
            return Language.Get(list[index]);
        }
        return string.Empty;
    }
    public bool needCheckAssitChat { get; set; }
    public int IsAssitChat(string message, bool force = false)
    {
        int assitChat = 0;
        if (needCheckAssitChat || force)
        {
            for (int i = 0; i < 2; i++)
            var config = configs[i];
            if (chatBubbles.ContainsKey(config.ID))
            {
                if (message.Equals(Language.Get(realmRandomChats[i])))
                continue;
            }
            var bubble = new ChatBubbleData();
            bubble.id = config.ID;
            bubble.leftPadding = new RectOffset()
            {
                left = config.LeftOffset.Length > 0 ? config.LeftOffset[0] : 0,
                right = config.LeftOffset.Length > 1 ? config.LeftOffset[1] : 0,
                top = config.LeftOffset.Length > 2 ? config.LeftOffset[2] : 0,
                bottom = config.LeftOffset.Length > 3 ? config.LeftOffset[3] : 0,
            };
            bubble.rifhtPadding = new RectOffset()
            {
                left = config.RightOffset.Length > 0 ? config.RightOffset[0] : 0,
                right = config.RightOffset.Length > 1 ? config.RightOffset[1] : 0,
                top = config.RightOffset.Length > 2 ? config.RightOffset[2] : 0,
                bottom = config.RightOffset.Length > 3 ? config.RightOffset[3] : 0,
            };
            bubble.myColor = new Color32()
            {
                r = (byte)(config.MyColor.Length > 0 ? config.MyColor[0] : 0),
                g = (byte)(config.MyColor.Length > 1 ? config.MyColor[1] : 0),
                b = (byte)(config.MyColor.Length > 2 ? config.MyColor[2] : 0),
                a = (byte)(config.MyColor.Length > 3 ? config.MyColor[3] : 255),
            };
            bubble.otherColor = new Color32()
            {
                r = (byte)(config.OtherColor.Length > 0 ? config.OtherColor[0] : 0),
                g = (byte)(config.OtherColor.Length > 1 ? config.OtherColor[1] : 0),
                b = (byte)(config.OtherColor.Length > 2 ? config.OtherColor[2] : 0),
                a = (byte)(config.OtherColor.Length > 3 ? config.OtherColor[3] : 255),
            };
            bubble.top = config.Top;
            chatBubbles.Add(config.ID, bubble);
        }
    }
    public bool SatisfyNameLength(string name, out int error)
    {
        error = 0;
        int length = Encoding.Default.GetBytes(name).Length;
        int maxlength = characterLimit;  //纯中文字数
        //var minlength = 3;
        if (length > maxlength)
        {
            error = 1;
        }
        // else if (length < minlength)
        // {
        //     error = 2;
        // }
        return error == 0;
    }
    public bool CheckChatLimit(string info, out int errorCode)
    {
        errorCode = 0;
        if (string.IsNullOrEmpty(info))
        {
            errorCode = 0;
            return false;
        }
        if (!SatisfyNameLength(info, out errorCode))
        {
            return false;
        }
        if (DirtyWordConfig.IsDirtWord(info) || UIHelper.HasSpecialCharac(info)
            || DirtyNameConfig.IsDirtName(info))
        {
            errorCode = 3;
            return false;
        }
        return true;
    }
    public void ShowChatErrorTip(int _errorCode)
    {
        switch (_errorCode)
        {
            case 0:
                //空
                SysNotifyMgr.Instance.ShowTip("ChatInfoNoNull");
                break;
            case 1:
                // 长度过长
                SysNotifyMgr.Instance.ShowTip("NameError2", 7);
                break;
            case 3:
                // 脏字
                SysNotifyMgr.Instance.ShowTip("NameSensitive");
                break;
        }
    }
    public int GetJumpIndex(ChatChannel type)
    {
        if (!TryGetTalkData(type, out List<TalkData> datas))
            return 0;
        return Mathf.Max(datas.Count - 1, 0);
    }
    public bool IsValidChatChannel(int channelType)
    {
        return Enum.IsDefined(typeof(ChatChannel), channelType);
    }
    public bool TryGetChatData(ChatChannel type, int index, out TalkData data)
    {
        data = null;
        if (!TryGetTalkData(type, out List<TalkData> datas) || datas == null)
            return false;
        if (index < 0 || index >= datas.Count)
            return false;
        data = datas[index];
        return true;
    }
    public bool TryGetTalkData(ChatChannel type, out List<TalkData> datas)
    {
        return talkDict.TryGetValue(type, out datas);
    }
    public void SendChatInfo(ChatChannel type, string content)
    {
        SendChatPack((int)type, content);
    }
    public void SendChatPack(int channelType, string content)
    {
        CB320_tagCSTalk pack = new CB320_tagCSTalk();
        pack.ChannelType = (byte)channelType;
        pack.Content = content;
        pack.Len = (ushort)GetUTF8InfoLen(content);
        GameNetSystem.Instance.SendInfo(pack);
    }
    public readonly int maxTalkCount = 1000;  //聊天数量上限
    public readonly int deleteTalkCount = 300;  //聊天数量上限时删除前多少条
    void TryDeleteTalkData(ChatChannel type)
    {
        if (!TryGetTalkData(type, out List<TalkData> datas))
            return;
        if (datas.Count < maxTalkCount)
            return;
        datas.RemoveRange(0, deleteTalkCount);
    }
    public int currentDay = -1;
    public void AddTalkData(ChatChannel type, TalkData data)
    {
        //如果超过限制先删除旧数据
        TryDeleteTalkData(type);
        if (!TryGetTalkData(type, out List<TalkData> datas))
        {
            talkDict[type] = new List<TalkData>();
        }
        talkDict[type].Add(data);
        OnUpdateTalkEvent?.Invoke(type, data);
    }
    public bool TryAddDate(int allSeconds, ChatChannel type)
    {
        DateTime talkTime = TimeUtility.GetTime((uint)allSeconds);
        if (talkTime.Day != currentDay)
        {
            currentDay = talkTime.Day;
            AddTalkData(type, new TalkData()
            {
                ChannelType = (byte)type,
                isDate = true,
                Content = Language.Get("Chat09", talkTime.Month, talkTime.Day),
                TalkTime = (uint)allSeconds,
            });
            return true;
        }
        return false;
    }
    public void AddSysData(string msg, ArrayList infoList, ChatChannel type)
    {
        int allSeconds = TimeUtility.AllSeconds;
        // 如果隔天,增加日期行
        TryAddDate(allSeconds, type);
        if (!talkDict.ContainsKey(type))
        {
            talkDict[type] = new List<TalkData>();
        }
        AddTalkData(type, new TalkData()
        {
            ChannelType = (byte)type,
            isSystem = true,
            Content = msg,
            BubbleBox = 1,
            TalkTime = (uint)allSeconds,
            InfoList = new ArrayList(infoList),
        });
    }
    public void UpdateTalk(HB310_tagMCTalk vNetData)
    {
        if (!IsValidChatChannel(vNetData.ChannelType))
            return;
        ChatChannel type = (ChatChannel)vNetData.ChannelType;
        if (!talkDict.ContainsKey(type))
        {
            talkDict[type] = new List<TalkData>();
        }
        int allSeconds = TimeUtility.AllSeconds;
        // 如果隔天,增加日期行
        TryAddDate(allSeconds, type);
        TalkData talkData = new TalkData()
        {
            ChannelType = vNetData.ChannelType,
            Name = UIHelper.ServerStringTrim(vNetData.Name),
            PlayerID = vNetData.PlayerID,
            Content = UIHelper.ServerStringTrim(vNetData.Content),
            BubbleBox = vNetData.BubbleBox,
            LV = vNetData.LV,
            RealmLV = vNetData.RealmLV,
            TitleID = vNetData.TitleID,
            Job = vNetData.Job,
            Face = vNetData.Face,
            FacePic = vNetData.FacePic,
            ServerID = vNetData.ServerID,
            TalkTime = (uint)allSeconds,
        };
        AddTalkData(type, talkData);
    }
    public void UpdateTalkCacheList(HB311_tagMCTalkCacheList vNetData)
    {
        if (!IsValidChatChannel(vNetData.ChannelType))
            return;
        ChatChannel type = (ChatChannel)vNetData.ChannelType;
        if (!talkDict.ContainsKey(type))
        {
            talkDict[type] = new List<TalkData>();
        }
        if (vNetData.InfoList.IsNullOrEmpty())
            return;
        foreach (var info in vNetData.InfoList)
        {
            // 如果隔天,增加日期行
            TryAddDate((int)info.TalkTime, type);
            AddTalkData(type, new TalkData()
            {
                ChannelType = vNetData.ChannelType,
                Name = info.Name,
                PlayerID = info.PlayerID,
                Content = info.Content,
                BubbleBox = info.BubbleBox,
                LV = info.LV,
                Job = info.Job,
                RealmLV = info.RealmLV,
                TitleID = info.TitleID,
                Face = info.Face,
                FacePic = info.FacePic,
                ServerID = info.ServerID,
                TalkTime = info.TalkTime,
            });
        }
        OnUpdateTalkCacheListEvent?.Invoke();
    }
    #region 标签页
    // 当前展示的频道入口
    private ChatTab m_NowChatTab;
    public ChatTab nowChatTab
    {
        get { return m_NowChatTab; }
        set
        {
            if (m_NowChatTab == value)
                return;
            m_NowChatTab = value;
            OnChatTabChangeEvent?.Invoke(value);
        }
    }
    public event Action<ChatTab> OnChatTabChangeEvent;
    // 频道入口的展示顺序
    public readonly List<ChatTab> tabShowList = new List<ChatTab>()
    {
        ChatTab.World,
        ChatTab.Guild,
        // ChatTab.Person,
        // ChatTab.BlackList,
    };
    public bool IsTabOpen(ChatTab chatTab, bool isTip = false)
    {
        if (!tabShowList.Contains(chatTab))
            return false;
        switch (chatTab)
        {
            case ChatTab.World:
                return true;
            case ChatTab.Guild:
                //没有公会
                if (!PlayerDatas.Instance.fairyData.HasFairy)
                {
                    assitChat = 1;
                    break;
                    if (isTip)
                        SysNotifyMgr.Instance.ShowTip("NoGuild");
                    return false;
                }
                if (message.Equals(Language.Get(dungeonRandomChats[i])))
                {
                    assitChat = 2;
                    break;
                }
                return true;
            default:
                return false;
        }
    }
    public bool IsSelectChatTab(ChatTab chatTab)
    {
        return nowChatTab == chatTab;
    }
    public bool IsValidChatTab(int chatTab)
    {
        return Enum.IsDefined(typeof(ChatTab), chatTab);
    }
    public string GetChatTabName(ChatTab chatTab)
    {
        return Language.Get(StringUtility.Contact("ChatTab", (int)chatTab));
    }
    public string GetChatTabSelectIcon(ChatTab chatTab, bool isSelect)
    {
        return StringUtility.Contact(isSelect ? "ChatTabSelect" : "ChatTabUnSelect", (int)chatTab);
    }
    #endregion
    #region 弹幕设置
    private Dictionary<ChatChannel, bool> bulletSettingDict = new Dictionary<ChatChannel, bool>();
    private string settingsKey { get { return StringUtility.Contact("BulletChatSettings_", PlayerDatas.Instance.PlayerId); } }
    // 设置特定频道的弹幕开关状态
    public void SetBulletSetting(ChatChannel channelType, bool isEnabled)
    {
        bulletSettingDict[channelType] = isEnabled;
        SaveBulletSettings();
    }
    // 获取特定频道的弹幕开关状态
    public bool GetBulletSetting(ChatChannel channelType)
    {
        if (bulletSettingDict.TryGetValue(channelType, out bool value))
        {
            return value;
        }
        return true;
    }
    // 保存弹幕设置到本地
    private void SaveBulletSettings()
    {
        Dictionary<int, int> saveDict = new Dictionary<int, int>();
        foreach (var kvp in bulletSettingDict)
        {
            saveDict[(int)kvp.Key] = kvp.Value ? 1 : 0;
        }
        string jsonStr = JsonMapper.ToJson(saveDict);
        LocalSave.SetString(settingsKey, jsonStr);
    }
    // 从本地读取弹幕设置
    private void LoadBulletSettings()
    {
        bulletSettingDict.Clear();
        // 使用 LocalSave 读取 JSON 字符串
        string jsonStr = LocalSave.GetString(settingsKey);
        if (string.IsNullOrEmpty(jsonStr) || jsonStr == "{}")
        {
            // 如果没有保存的数据,设置默认值
            InitializeDefaultBulletSettings();
            return;
        }
        Dictionary<int, int> loadDict = ConfigParse.ParseIntDict(jsonStr);
        foreach (var kvp in loadDict)
        {
            if (Enum.IsDefined(typeof(ChatChannel), kvp.Key))
            {
                bulletSettingDict[(ChatChannel)kvp.Key] = kvp.Value == 1;
            }
        }
        return assitChat;
    }
    // 初始化默认弹幕设置 默认所有频道开启
    private void InitializeDefaultBulletSettings()
    {
        foreach (ChatChannel channelType in Enum.GetValues(typeof(ChatChannel)))
        {
            bulletSettingDict[channelType] = true;
        }
        SaveBulletSettings();
    }
    #endregion
    #region 宝石炫耀跳转
    public bool openFromGem { get; set; }
    public int flauntGemId { get; set; }
    public bool flauntGemBind { get; set; }
    public string GetGemFlauntChat()
    {
        var config = ItemConfig.Get(flauntGemId);
        if (config != null)
        {
            var itemInfo = new ItemInfo();
            itemInfo.itemId = flauntGemId;
            var item = new ItemModel(PackType.Item, itemInfo);
            var tip = string.Format("[{0}]", config.ItemName);
            itemPlaceList.Add(item);
            return Language.Get("GemLookTalk", tip);
        }
        return string.Empty;
    }
    #endregion
    #region 仙缘红点
    Dictionary<ChatInfoType, Redpoint> chatSocialRedpoints = new Dictionary<ChatInfoType, Redpoint>();
    Dictionary<ChatInfoType, int> unReadChatCounts = new Dictionary<ChatInfoType, int>();
    public void InitChatRedpoints()
    {
        chatSocialRedpoints.Add(ChatInfoType.Fairy, new Redpoint(MainRedDot.RedPoint_FriendChatKey, 2502));
        chatSocialRedpoints.Add(ChatInfoType.Team, new Redpoint(MainRedDot.RedPoint_FriendChatKey, 2503));
        unReadChatCounts.Add(ChatInfoType.Fairy, 0);
        unReadChatCounts.Add(ChatInfoType.Team, 0);
    }
    public void ViewChat(ChatInfoType type)
    {
        if (unReadChatCounts.ContainsKey(type))
        {
            unReadChatCounts[type] = 0;
            UpdateRedpoint(type);
        }
    }
    void ReceiveNewChat(ChatInfoType type)
    {
        // TODO YYL
        // switch (type)
        // {
        //     case ChatInfoType.Team:
        //         if (!UIManager.Instance.IsOpened<TeamChatWin>()
        //             && (!UIManager.Instance.IsOpened<ChatWin>() || presentChatType != ChatInfoType.Team))
        //         {
        //             unReadChatCounts[ChatInfoType.Team] = Mathf.Min(unReadChatCounts[ChatInfoType.Team] + 1, 99);
        //         }
        //         break;
        //     case ChatInfoType.Fairy:
        //         if (!UIManager.Instance.IsOpened<FairyChatWin>()
        //             && (!UIManager.Instance.IsOpened<ChatWin>() || presentChatType != ChatInfoType.Fairy))
        //         {
        //             unReadChatCounts[ChatInfoType.Fairy] = Mathf.Min(unReadChatCounts[ChatInfoType.Fairy] + 1, 99);
        //         }
        //         break;
        // }
        UpdateRedpoint(type);
    }
    public void UpdateRedpoint(ChatInfoType type)
    {
        if (chatSocialRedpoints.ContainsKey(type))
        {
            var redpoint = chatSocialRedpoints[type];
            if (unReadChatCounts[type] > 0)
            {
                redpoint.state = RedPointState.Quantity;
                redpoint.count = unReadChatCounts[type];
            }
            else
            {
                redpoint.state = RedPointState.None;
            }
        }
        var socialRed = MainRedDot.Instance.redPointFriendChat;
        if (chatSocialRedpoints[ChatInfoType.Fairy].state == RedPointState.Quantity
            || chatSocialRedpoints[ChatInfoType.Team].state == RedPointState.Quantity)
        {
            socialRed.count = unReadChatCounts[ChatInfoType.Fairy] > 0 ?
                unReadChatCounts[ChatInfoType.Fairy] : unReadChatCounts[ChatInfoType.Team];
        }
        else
        {
            socialRed.count = 0;
        }
    }
    public RedPointState GetSocialChatRedpoint(ChatInfoType type)
    {
        if (chatSocialRedpoints.ContainsKey(type))
        {
            return chatSocialRedpoints[type].state;
        }
        return RedPointState.None;
    }
    #endregion
    #region 协助感谢
    public void SendThank2AssistPlayer(int playerId)
    {
        // TODO YYL
        // if (PlayerDatas.Instance.baseData.LV >= assistThankLevelLimit.x)
        // {
        //     return;
        // }
        // var assistPlayerInfo = dungeonAssistModel.GetAssistPlayerInfo(playerId);
        // if (assistPlayerInfo != null)
        // {
        //     if (assistPlayerInfo.LV >= assistThankLevelLimit.y)
        //     {
        //         return;
        //     }
        // }
        // var languageKeyIndex = UnityEngine.Random.Range(0, assistThankLanguages.Count);
        // if (assistThankLanguages.Count > 0)
        // {
        //     SendFriendChat(Language.Get(assistThankLanguages[languageKeyIndex]), playerId);
        // }
    }
    #endregion
}
public struct ChatExtraData
public enum ChatTab
{
    public int infoint1;
    public ChatExtraData(int info1)
    {
        this.infoint1 = info1;
    }
    public static ChatExtraData Default {
        get {
            return new ChatExtraData(0);
        }
    }
    World = 0,      //世界
    Guild = 1,      //公会
    Person = 2,     //私聊
    BlackList = 3,  //黑名单
}
public enum ChatInfoType
public enum ChatChannel
{
    System,//系统消息
    World, //世界频道
    Area,  //区域频道
    Team,  //队伍
    Invite,//组队
    Trumpet,//喇叭
    Fairy,//仙盟
    Friend,//私聊
    CrossServer,//跨服
    FairyQuestion,
    FairyTip,
    TeamTip,
    //后续IL开发添加预设
    default1,   //阵营(在跨服地图往跨服发)
    default2,   //阵营信息提示
    default3,
    default4,
    default5,
    default6,
    default7,
    default8,
    default9,
    default10,
    World = 0,          //世界
    CrossServer = 1,    //跨服
    Area = 2,           // 区域
    Guild = 3,          // 公会
    Friend = 4,             // 好友
    Team = 5,               //队伍
    Person = 6,             //私聊
}
public class ChatBubbleData
{
    public int id;
    public RectOffset leftPadding;
    public RectOffset rifhtPadding;
    public Color32 myColor;
    public Color32 otherColor;
    public int top;
}
public class TalkData
{
    public byte ChannelType;    // 0-世界;1-跨服;3- 仙盟
    public bool isSystem = false;       //系统消息
    public bool isDate = false;          //分割日期
    public byte NameLen;
    public string Name;
    public uint PlayerID;
    public string Content;
    public uint BubbleBox;    //聊天气泡框
    public ushort LV;    //等级
    public byte Job;    //职业
    public byte RealmLV;    //境界
    public uint TitleID;    //称号
    public uint Face;    //基本脸型
    public uint FacePic;    //头像框
    public uint ServerID;    //所属区服ID
    public uint TalkTime;        //该聊天发送时间戳
    public ArrayList InfoList;     //附加信息
}
Main/System/Chat/ChatPlayerMineCell.cs
@@ -1,71 +1,50 @@
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using EnhancedUI.EnhancedScroller;
    public class ChatPlayerMineCell : ScrollerUI
public class ChatPlayerMineCell : MonoBehaviour
{
    [SerializeField] AvatarCell avatarCell;
    [SerializeField] RectTransform m_avatarRect;
    [SerializeField] OfficialTitleCell title;
    [SerializeField] Text m_PlayerName;
    [SerializeField] ChatBubbleBehaviour m_ChatBubble;
    [SerializeField] float spacing = 5.0f;
    ChatManager manager { get { return ChatManager.Instance; } }
    public void Refresh(CellView cell)
    {
        // [SerializeField] AvatarCell avatarCell;
        [SerializeField] RectTransform m_avatarRect;
        [SerializeField] Text m_VipLv;
        [SerializeField] Text m_PlayerName;
        [SerializeField] Text m_ChatTime;
        [SerializeField] ChatBubbleBehaviour m_ChatBubble;
        [SerializeField] float spacing = 5.0f;
        if (!manager.TryGetChatData(ChatManager.Instance.nowChatChannel, cell.index, out TalkData data))
            return;
        // PhantasmPavilionModel phantasmPavilionModel { get { return ModelCenter.Instance.GetModel<PhantasmPavilionModel>(); } }
        public override void Refresh(CellView cell)
        m_ChatBubble.DisplayContent(data.Content);
        int bubbleID = ChatBubbleHelper.GetMyChatBubbleID();
        m_ChatBubble.DisplayBubble(bubbleID, (int)PlayerDatas.Instance.baseData.PlayerID);
        avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)PlayerDatas.Instance.baseData.PlayerID,
                                                PlayerDatas.Instance.baseData.face,
                                                PlayerDatas.Instance.baseData.facePic));
        avatarCell.button.SetListener(() =>
        {
            ChatData data = ChatCenter.Instance.GetChatData(ChatManager.Instance.presentChatType, cell.index);
            if (data == null)
            {
                return;
            }
            var chatUserData = data as ChatUeseData;
            m_ChatBubble.DisplayContent(data.content);
        });
            //  TODO YYL
            // int bubbleID = phantasmPavilionModel.GetNowChatBubbleID();
            // m_ChatBubble.DisplayBubble(bubbleID);
            // avatarCell.InitUI(AvatarHelper.GetMyAvatarModel());
            // avatarCell.button.SetListener(() =>
            // {
            //     OnFunc(cell);
            // });
            m_PlayerName.text = chatUserData.name;
            m_ChatTime.text = data.createTime.ToString("yyyy-MM-dd HH:mm");
            if (chatUserData.vipLv > 0)
            {
                m_VipLv.text = "VIP";//string.Format("V{0}", chatUserData.vipLv);
            }
            else
            {
                m_VipLv.text = string.Empty;
            }
        title.InitUI(PlayerDatas.Instance.baseData.realmLevel, PlayerDatas.Instance.baseData.TitleID);
        if (manager.nowChatTab == ChatTab.World)
        {
            string serverName = ServerListCenter.Instance.GetServerName(UIHelper.GetServerIDByAccount(PlayerDatas.Instance.baseData.AccID));
            m_PlayerName.text = Language.Get("Chat08", serverName, PlayerDatas.Instance.baseData.PlayerName);
        }
        private void OnFunc(CellView cell)
        else
        {
            int index = cell.index;
            ChatData _data = ChatCenter.Instance.GetChatData(ChatManager.Instance.presentChatType, index);
            if (_data == null)
            {
                return;
            }
            ChatUeseData user = _data as ChatUeseData;
            if (user.player == PlayerDatas.Instance.baseData.PlayerID)
            {
                return;
            }
            HrefAnalysis.Inst.ExcuteHrefEvent(string.Format("<a>showplayer={0}</a>", user.player));
        }
        public float GetHeight(string content, ArrayList list)
        {
            var minHeight = m_avatarRect.sizeDelta.y;
            var chatHeight = m_ChatBubble.GetBubbleHeight(content, list) + Mathf.Abs(m_ChatBubble.transform.localPosition.y);
            return (chatHeight > minHeight ? chatHeight : minHeight) + spacing;
            int fmlv = PlayerDatas.Instance.fairyData.mine.FmLV;
            m_PlayerName.text = Language.Get("Chat08", RichTextMsgReplaceConfig.GetRichReplace("FAMILY", fmlv), PlayerDatas.Instance.baseData.PlayerName);
        }
    }
    public float GetHeight(string content, ArrayList list)
    {
        var minHeight = m_avatarRect.sizeDelta.y;
        var chatHeight = m_ChatBubble.GetBubbleHeight(content, list) + Mathf.Abs(m_ChatBubble.transform.localPosition.y);
        return (chatHeight > minHeight ? chatHeight : minHeight) + spacing;
    }
}
Main/System/Chat/ChatPlayerOtherCell.cs
@@ -1,81 +1,56 @@
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using EnhancedUI.EnhancedScroller;
    public class ChatPlayerOtherCell : ScrollerUI
public class ChatPlayerOtherCell : MonoBehaviour
{
    [SerializeField] AvatarCell avatarCell;
    [SerializeField] RectTransform m_avatarRect;
    [SerializeField] OfficialTitleCell title;
    [SerializeField] Text m_PlayerName;
    [SerializeField] ChatBubbleBehaviour m_ChatBubble;
    [SerializeField] float spacing = 5.0f;
    ChatManager manager { get { return ChatManager.Instance; } }
    public void Refresh(CellView cell)
    {
        // [SerializeField] AvatarCell avatarCell;
        [SerializeField] RectTransform m_avatarRect;
        [SerializeField] Text m_VipLv;
        [SerializeField] Text m_PlayerName;
        [SerializeField] Text m_ChatTime;
        [SerializeField] ChatBubbleBehaviour m_ChatBubble;
        [SerializeField] float spacing = 5.0f;
        // PhantasmPavilionModel phantasmPavilionModel { get { return ModelCenter.Instance.GetModel<PhantasmPavilionModel>(); } }
        public override void Refresh(CellView cell)
        if (!manager.TryGetChatData(ChatManager.Instance.nowChatChannel, cell.index, out TalkData data))
            return;
        avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)data.PlayerID, (int)data.Face, (int)data.PlayerID, data.Job));
        avatarCell.button.SetListener(() =>
        {
            var data = ChatCenter.Instance.GetChatData(ChatManager.Instance.presentChatType, cell.index);
            if (data == null)
            {
                return;
            }
            var chatUserData = data as ChatUeseData;
        });
        title.InitUI(data.RealmLV, (int)data.TitleID);
            // AvatarModel avatarModel = AvatarHelper.GetOtherAvatarModel(chatUserData.face, chatUserData.facePic, chatUserData.job);
            // avatarCell.InitUI(avatarModel);
            // avatarCell.button.SetListener(() =>
            // {
            //     OnFunc(cell);
            // });
            m_PlayerName.text = chatUserData.name;
            m_ChatTime.text = data.createTime.ToString("yyyy-MM-dd HH:mm");
            if (chatUserData.vipLv > 0)
            {
                m_VipLv.text = "VIP";//string.Format("V{0}", chatUserData.vipLv);
            }
            else
            {
                m_VipLv.text = string.Empty;
            }
            m_ChatBubble.DisplayContent(data.content, true);
            // int bubbleID = phantasmPavilionModel.GetNowOtherChatBubbleID(chatUserData.bubbleId);
            // m_ChatBubble.DisplayBubble(bubbleID);
        m_ChatBubble.DisplayContent(data.Content, true);
        int bubbleID = ChatBubbleHelper.GetOtherChatBubbleID(data.Job, (int)data.BubbleBox);
        m_ChatBubble.DisplayBubble(bubbleID, (int)data.PlayerID);
        if (manager.nowChatTab == ChatTab.World)
        {
            string serverName = ServerListCenter.Instance.GetServerName((int)data.ServerID);
            m_PlayerName.text = Language.Get("Chat08", serverName, data.Name);
        }
        private void OnFunc(CellView cell)
        else
        {
            int index = cell.index;
            ChatData _data = ChatCenter.Instance.GetChatData(ChatManager.Instance.presentChatType, index);
            if (_data == null)
            FairyMember fairyMember = PlayerDatas.Instance.fairyData.GetMember((int)data.PlayerID);
            int fmlv = 0;
            if (fairyMember != null)
            {
                return;
                fmlv = fairyMember.FmLV;
            }
            ChatUeseData user = _data as ChatUeseData;
            if (user.player == PlayerDatas.Instance.baseData.PlayerID)
            {
                return;
            }
            if (user.serverGroupId == 0 || user.serverGroupId == PlayerDatas.Instance.baseData.ServerGroupId)
            {
                // TODO YYL
                // PlayerDetails.ShowPlayerDetails(user.player, null);
            }
            else
            {
                // TODO YYL
                // PlayerDetails.ShowCrossServerChatPlayer(user);
            }
        }
        public float GetHeight(string content, ArrayList list)
        {
            var minHeight = m_avatarRect.sizeDelta.y;
            var chatHeight = m_ChatBubble.GetBubbleHeight(content, list) + Mathf.Abs(m_ChatBubble.transform.localPosition.y);
            return (chatHeight > minHeight ? chatHeight : minHeight) + spacing;
            m_PlayerName.text = Language.Get("Chat08", RichTextMsgReplaceConfig.GetRichReplace("FAMILY", fmlv), data.Name);
        }
    }
    public float GetHeight(string content, ArrayList list)
    {
        var minHeight = m_avatarRect.sizeDelta.y;
        var chatHeight = m_ChatBubble.GetBubbleHeight(content, list) + Mathf.Abs(m_ChatBubble.transform.localPosition.y);
        return (chatHeight > minHeight ? chatHeight : minHeight) + spacing;
    }
}
Main/System/Chat/ChatRecently.cs
File was deleted
Main/System/Chat/ChatRecently.cs.meta
File was deleted
Main/System/Chat/ChatRecentlyCell.cs
File was deleted
Main/System/Chat/ChatRecentlyCell.cs.meta
File was deleted
Main/System/Chat/ChatSendComponent.cs
File was deleted
Main/System/Chat/ChatSendComponent.cs.meta
File was deleted
Main/System/Chat/ChatSettingButton.cs
New file
@@ -0,0 +1,27 @@
using UnityEngine;
public class ChatSettingButton : MonoBehaviour
{
    [SerializeField] ButtonEx btnSelect;
    [SerializeField] ImageEx imgSelect;
    private ChatChannel channelType;
    private bool isAvtive;
    ChatManager manager { get { return ChatManager.Instance; } }
    void Awake()
    {
        btnSelect.SetListener(() =>
        {
            isAvtive = !isAvtive;
            manager.SetBulletSetting(channelType, isAvtive);
            imgSelect.SetActive(isAvtive);
        });
    }
    public void SetChannelType(ChatChannel type)
    {
        channelType = type;
        isAvtive = manager.GetBulletSetting(channelType);
        imgSelect.SetActive(isAvtive);
    }
}
Main/System/Chat/ChatSettingButton.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/System/Chat/ChatSettingButton.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: 0f526b90dcff2ff4db9163d67839e2f1
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Chat/ChatSysCell.cs
New file
@@ -0,0 +1,32 @@
using System.Collections;
using UnityEngine;
public class ChatSysCell : MonoBehaviour
{
    [SerializeField] ChatBubbleBehaviour m_ChatBubble;
    [SerializeField] RichText chatText;
    ChatManager manager { get { return ChatManager.Instance; } }
    public void Refresh(CellView cell)
    {
        if (!manager.TryGetChatData(manager.nowChatChannel, cell.index, out TalkData data))
            return;
        if (data.InfoList != null)
        {
            chatText.SetExtenalData(data.InfoList);
        }
        m_ChatBubble.DisplaySysBubble(manager.sysBubbleID, manager.sysBubbleColor);
        m_ChatBubble.DisplayContent(data.Content, true);
    }
    public float GetHeight(string content, ArrayList list)
    {
        var chatHeight = m_ChatBubble.GetBubbleHeight(content, list);
        if (string.IsNullOrEmpty(content))
        {
            chatHeight = 0;
        }
        //Debug.Log($"GetHeight {chatHeight} chatHeight {chatHeight} GetBubbleHeight {m_ChatBubble.GetBubbleHeight(content, list)} Abs {Mathf.Abs(m_ChatBubble.transform.localPosition.y)}");
        return chatHeight;
    }
}
Main/System/Chat/ChatSysCell.cs.meta
Main/System/Chat/ChatTabCell.cs
New file
@@ -0,0 +1,46 @@
using UnityEngine;
using UnityEngine.UI;
public class ChatTabCell : MonoBehaviour
{
    [SerializeField] ImageEx imgSelectBg;
    [SerializeField] ImageEx imgSelect;
    [SerializeField] ImageEx imgUnSelect;
    [SerializeField] TextEx txtTitle;
    [SerializeField] Button btnSelect;
    [SerializeField] Color selectedColor;
    [SerializeField] Color unSelectColor;
    ChatManager manager { get { return ChatManager.Instance; } }
    public void Display(int index, CellView cellView)
    {
        int tab = cellView.info.Value.infoInt1;
        if (!manager.IsValidChatTab(tab))
            return;
        ChatTab chatTab = (ChatTab)tab;
        bool isSelect = manager.IsSelectChatTab(chatTab);
        imgSelectBg.SetActive(isSelect);
        imgSelect.SetActive(isSelect);
        imgUnSelect.SetActive(!isSelect);
        imgSelect.SetSprite(manager.GetChatTabSelectIcon(chatTab, true));
        imgUnSelect.SetSprite(manager.GetChatTabSelectIcon(chatTab, false));
        imgSelectBg.SetNativeSize();
        imgSelect.SetNativeSize();
        imgUnSelect.SetNativeSize();
        txtTitle.text = manager.GetChatTabName(chatTab);
        txtTitle.color = isSelect ? selectedColor : unSelectColor;
        btnSelect.SetListener(() =>
        {
            if (!manager.IsTabOpen(chatTab, true))
                return;
            manager.nowChatTab = chatTab;
        });
    }
}
Main/System/Chat/ChatTabCell.cs.meta
copy from Main/System/Chat/ChatFriendTipCell.cs.meta copy to Main/System/Chat/ChatTabCell.cs.meta
File was copied from Main/System/Chat/ChatFriendTipCell.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 68376cb82e4739943aecb4dbbd81a757
guid: 3c621c88da6d81042baee40698e785ce
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Chat/ChatTrailCell.cs
File was deleted
Main/System/Chat/ChatWin.cs
@@ -1,279 +1,514 @@
//--------------------------------------------------------
//    [Author]:           玩个游戏
//    [  Date ]:           Monday, April 09, 2018
//--------------------------------------------------------
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ChatWin : UIBase
{
    [SerializeField, Header("锁定当前区域比例"), Range(0, 1)] float m_Percent = 0.3f;
    [SerializeField] ButtonEx btnSetting;
    [SerializeField] ButtonEx btnClose;
    public class ChatWin : UIBase
    [SerializeField] ScrollerController scrChatTab;
    [SerializeField] ChatPlayerMineCell m_ChatMineCell;
    [SerializeField] ChatPlayerOtherCell m_ChatOtherCell;
    [SerializeField] ChatSysCell m_ChatSysCell;
    [SerializeField] Transform transInput;
    [SerializeField] InputField inputChat;
    [SerializeField] ButtonEx btnSendChat;
    [SerializeField] ImageEx imgSendChat;
    [SerializeField] TextEx txtSendChat;
    [SerializeField] ScrollerController scrWorld;
    [SerializeField] ScrollerController scrGuild;
    private int unreadMsgCount = 0;
    [SerializeField] ButtonEx btnNewMsgTip;
    [SerializeField] TextEx txtNewMsgTip;
    [SerializeField, Header("聊天弹幕设置")] Transform transSettings;
    [SerializeField] ClickScreenOtherSpace clickScreenOtherSpace;
    bool isSettingOpen = false;
    [SerializeField] ChatSettingButton btnWorldSetting;
    [SerializeField] ChatSettingButton btnGuildSetting;
    ChatManager manager { get { return ChatManager.Instance; } }
    protected override void InitComponent()
    {
        [SerializeField] Button m_CloseBtn;
        [SerializeField] Text m_ChatTip;
        [SerializeField] ChatSendComponent m_ChatSend;
        [SerializeField] ScrollerController m_ChannelControl;
        [SerializeField] ChatContentBehaviour m_ChatContent;
        [SerializeField] ChatRecently m_ChatRecently;
        [SerializeField] Button m_ChannelBtn;
        [SerializeField] Text m_ChannelInfo;
        protected override void InitComponent()
        base.InitComponent();
        btnClose.SetListener(CloseWindow);
        btnSendChat.SetListener(() =>
        {
            m_CloseBtn.onClick.AddListener(OnClose);
            m_ChannelBtn.onClick.AddListener(OnChannelBtn);
        }
        protected override void OnPreOpen()
        {
            if (!IsSatisfyShowChannel(ChatManager.Instance.presentChatType))
            // 如果在聊天输入界面无输入文字点击发送,则关闭聊天输入界面
            if (string.IsNullOrEmpty(inputChat.text))
            {
                ChatManager.Instance.presentChatType = ChatInfoType.System;
                CloseWindow();
                return;
            }
            // m_ChatSend.parent = this;
            ChatManager.Instance.lockUpdate = false;
            ChatManager.Instance.OnPteChatChangeEvent += OnPteChatChangeEvent;
            ChatManager.Instance.OpenPteChatEvent += OpenPteChatEvent;
            DisplayChannel();
            OnChatTypeChange();
            ChatCenter.Instance.UpdateChatContentPos();
        }
        protected override void OnOpen()
        {
            AchievementRandomWord();
            if (ChatManager.Instance.openFromDaily)
            if (!manager.CheckChatLimit(inputChat.text, out int errorCode))
            {
                AssitRandomChat();
                ChatManager.Instance.needCheckAssitChat = true;
                manager.ShowChatErrorTip(errorCode);
                return;
            }
            else if (ChatManager.Instance.openFromGem)
            manager.SendChatInfo(manager.nowChatChannel, inputChat.text);
            manager.AddChatChannelSendTime(manager.nowChatChannel, TimeUtility.AllSeconds);
            inputChat.text = string.Empty;
            UpdateSendButton();
        });
        btnNewMsgTip.SetListener(() =>
        {
            if (manager.nowChatChannel == ChatChannel.World)
            {
                string chat = Language.Get("ThanksGift" + UnityEngine.Random.Range(1, 4));
                if (chat != string.Empty)
                {
                    ChatCenter.Instance.ChangeChatValue(chat, false, true);
                }
                RefreshChat(manager.nowChatChannel, scrWorld);
                ScrollerJump(scrWorld, ChatChannel.World);
            }
            else if (ChatManager.Instance.openFromFairyTask)
            else if (manager.nowChatChannel == ChatChannel.Guild)
            {
                TaskRandomChat();
                RefreshChat(manager.nowChatChannel, scrGuild);
                ScrollerJump(scrGuild, ChatChannel.Guild);
            }
            ChatManager.Instance.openFromDaily = false;
            ChatManager.Instance.openFromGem = false;
            ChatManager.Instance.openFromFairyTask = false;
        }
        protected override void OnPreClose()
            ClearUnreadMsg();
        });
        btnSetting.SetListener(() =>
        {
            ChatManager.Instance.OnPteChatChangeEvent -= OnPteChatChangeEvent;
            ChatManager.Instance.OpenPteChatEvent -= OpenPteChatEvent;
            ChatManager.Instance.openFromDaily = false;
            ChatManager.Instance.openFromGem = false;
            ChatManager.Instance.needCheckAssitChat = false;
            ChatManager.Instance.openFromFairyTask = false;
            //ChatCenter.Instance.ChangeChatValue(string.Empty, false, true);
            //ChatManager.Instance.itemPlaceList.Clear();
        }
            isSettingOpen = !isSettingOpen;
            transSettings.SetActive(isSettingOpen);
        });
        protected override void OnClose()
        {
        }
    }
        private void OpenPteChatEvent()
        {
            OnChannelSelect(ChatInfoType.Friend);
        }
    // 清理未读消息状态
    private void ClearUnreadMsg()
    {
        unreadMsgCount = 0;
        btnNewMsgTip.SetActive(false);
    }
        private void DisplayChannel()
        {
            m_ChannelControl.Refresh();
            for (int i = 0; i < ChatCenter.Instance.chatChannels.Count; i++)
            {
                if (IsSatisfyShowChannel(ChatCenter.Instance.chatChannels[i]))
                {
                    m_ChannelControl.AddCell(ScrollerDataType.Normal, (int)ChatCenter.Instance.chatChannels[i], OnChannelSelect);
                }
            }
            m_ChannelControl.Restart();
    // 更新未读消息UI
    private void UpdateUnreadMsgUI()
    {
        btnNewMsgTip.SetActive(unreadMsgCount > 0);
        txtNewMsgTip.text = Language.Get("Chat10", unreadMsgCount);
    }
            m_ChannelControl.JumpIndex(Math.Min((int)ChatManager.Instance.presentChatType, m_ChannelControl.GetNumberOfCells(m_ChannelControl.m_Scorller) - 1));
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        manager.OnChatTabChangeEvent += OnChatTabChange;
        manager.OnUpdateTalkEvent += OnUpdateTalkEvent;
        manager.OnUpdateTalkCacheListEvent += OnUpdateTalkCacheList;
        }
        scrChatTab.OnRefreshCell += OnRefreshChatTabCell;
        scrWorld.OnGetDynamicSize += OnGetWorldChatDynamicSize;
        scrWorld.OnRefreshCell += OnRefreshWorldCell;
        scrWorld.mScrollRect.onValueChanged.AddListener(OnWorldScrollValChange);
        scrGuild.OnGetDynamicSize += OnGetWorldChatDynamicSize;
        scrGuild.OnRefreshCell += OnRefreshGuildCell;
        scrGuild.mScrollRect.onValueChanged.AddListener(OnGuildScrollValChange);
        private bool IsSatisfyShowChannel(ChatInfoType channel)
        {
        clickScreenOtherSpace.AddListener(OnClickScreenOtherSpace);
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
            return true;
        }
        private void OnPteChatChangeEvent()
        {
            if (ChatManager.Instance.presentChatType == ChatInfoType.Friend)
            {
                OnChatTypeChange();
            }
        }
        private void OnChatTypeChange()
        {
            m_ChannelBtn.SetActive(false);
            m_ChannelInfo.SetActive(false);
            m_ChatSend.placeholder.text= Language.Get("L1111");
            m_CloseBtn.SetActive(ChatManager.Instance.presentChatType != ChatInfoType.Friend);
            switch (ChatManager.Instance.presentChatType)
            {
                case ChatInfoType.Trumpet:
                    m_ChatRecently.SetActive(false);
                    m_ChatTip.SetActive(false);
                    m_ChatSend.SetActive(true);
                    m_ChatSend.placeholder.text = Language.Get("L1012");
                    break;
                case ChatInfoType.Invite:
                case ChatInfoType.System:
                    m_ChatRecently.SetActive(false);
                    m_ChatTip.text = Language.Get("L1109");
                    m_ChatTip.SetActive(true);
                    m_ChatSend.SetActive(false);
                    break;
                case ChatInfoType.World:
                case ChatInfoType.Area:
                case ChatInfoType.CrossServer:
                case ChatInfoType.default1:
                    m_ChatRecently.SetActive(false);
                    m_ChatTip.SetActive(false);
                    m_ChatSend.SetActive(true);
                    break;
                case ChatInfoType.Team:
                    // TODO YYL
                    // m_ChatRecently.SetActive(false);
                    // var _team = ModelCenter.Instance.GetModel<TeamModel>().myTeam;
                    // m_ChatSend.SetActive(_team.teamId > 0);
                    // m_ChatTip.SetActive(_team.teamId <= 0);
                    // m_ChatTip.text = Language.Get("L1110");
                    break;
                case ChatInfoType.Fairy:
                    // TODO YYL
                    // m_ChatRecently.SetActive(false);
                    // m_ChannelBtn.SetActive(!PlayerDatas.Instance.fairyData.HasFairy);
                    // m_ChannelInfo.SetActive(!PlayerDatas.Instance.fairyData.HasFairy);
                    // m_ChatTip.SetActive(!PlayerDatas.Instance.fairyData.HasFairy);
                    // m_ChatSend.SetActive(PlayerDatas.Instance.fairyData.HasFairy);
                    // if (!PlayerDatas.Instance.fairyData.HasFairy)
                    // {
                    //     m_ChatTip.text = Language.Get("L1011");
                    // }
                    break;
                case ChatInfoType.Friend:
                    m_ChatSend.SetActive(true);
                    m_ChatTip.SetActive(false);
                    if (!m_ChatRecently.gameObject.activeSelf)
                    {
                        m_ChatRecently.SetActive(true);
                    }
                    break;
            }
            m_ChatContent.chatType = ChatManager.Instance.presentChatType;
            ChatManager.Instance.ViewChat(ChatManager.Instance.presentChatType);
        }
        private void OnChannelSelect(CellView _cell)
        {
            if ((ChatInfoType)_cell.index == ChatInfoType.CrossServer)
            {
                if (!FuncOpen.Instance.IsFuncOpen(162))
                {
                    SysNotifyMgr.Instance.ShowTip("CrossServerHint");
                    return;
                }
            }
            OnChannelSelect((ChatInfoType)_cell.index);
        }
        private void OnChannelSelect(ChatInfoType _type)
        {
            ChatManager.Instance.presentChatType = _type;
            m_ChannelControl.m_Scorller.RefreshActiveCellViews();
            if (_type == ChatInfoType.Friend)
            {
                ChatManager.Instance.lockUpdate = true;
            }
            else
            {
                ChatManager.Instance.lockUpdate = false;
            }
            OnChatTypeChange();
            ChatCenter.Instance.UpdateChatContentPos();
        }
        private void OnChannelBtn()
        {
            switch (ChatManager.Instance.presentChatType)
            {
                case ChatInfoType.Fairy:
                    {
                        int limit = FuncOpenLVConfig.Get((int)FuncOpenEnum.Guild).LimitLV;
                        if (PlayerDatas.Instance.baseData.LV < limit)
                        {
                            ServerTipDetails.DisplayNormalTip(Language.Get("L1136", limit));
                            return;
                        }
                        CloseWindow();
                        // TODO YYL
                        // WindowJumpMgr.Instance.WindowJumpTo(JumpUIType.UnionFunc3);
                    }
                    break;
            }
        }
        private void AchievementRandomWord()
        {
            // TODO YYL
            // if (AchievementGoto.achievementType != AchievementGoto.RandomFairyChat
            //     && AchievementGoto.achievementType != AchievementGoto.RandomWorldChat)
            // {
            //     return;
            // }
            // AchievementGoto.achievementType = 0;
            // if (AchievementGoto.achievementType == AchievementGoto.RandomFairyChat && !PlayerDatas.Instance.fairyData.HasFairy)
            // {
            //     return;
            // }
            // if (ChatManager.Instance.achievementRandoms.ContainsKey(ChatManager.Instance.presentChatType))
            // {
            //     var _list = ChatManager.Instance.achievementRandoms[ChatManager.Instance.presentChatType];
            //     int _index = UnityEngine.Random.Range(0, _list.Count);
            //     ChatCenter.Instance.ChangeChatValue(Language.Get(_list[_index]), false, true);
            // }
            // var _effect = AchievementGuideEffectPool.Require(1);
            // _effect.transform.SetParentEx(m_ChatSend.sendBtn.transform, Vector3.zero, Vector3.zero, Vector3.one);
        }
        private void AssitRandomChat()
        {
            ChatCenter.Instance.ChangeChatValue(ChatManager.Instance.GetAssitRandomChat(ChatManager.Instance.presentChatType), false, true);
        }
        private void TaskRandomChat()
        {
            ChatCenter.Instance.ChangeChatValue(ChatManager.Instance.GetTaskRandomChat(ChatManager.Instance.presentChatType), false, true);
        }
        private void GemFlauntChat()
        {
            // TODO YYL
            // ChatCenter.Instance.ChangeChatValue(ChatManager.Instance.GetGemFlauntChat(), false, true);
            // var _effect = AchievementGuideEffectPool.Require(1);
            // _effect.transform.SetParentEx(m_ChatSend.sendBtn.transform, Vector3.zero, Vector3.zero, Vector3.one);
        }
        isSettingOpen = false;
        transSettings.SetActive(isSettingOpen);
        btnWorldSetting.SetChannelType(ChatChannel.World);
        btnGuildSetting.SetChannelType(ChatChannel.Guild);
        inputChat.characterLimit = ChatManager.Instance.characterLimit;
        CreaterAll(manager.nowChatTab);
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        manager.OnChatTabChangeEvent -= OnChatTabChange;
        manager.OnUpdateTalkEvent -= OnUpdateTalkEvent;
        manager.OnUpdateTalkCacheListEvent -= OnUpdateTalkCacheList;
        scrChatTab.OnRefreshCell -= OnRefreshChatTabCell;
        scrWorld.OnGetDynamicSize -= OnGetWorldChatDynamicSize;
        scrWorld.OnRefreshCell -= OnRefreshWorldCell;
        scrWorld.mScrollRect.onValueChanged.RemoveListener(OnWorldScrollValChange);
        scrGuild.OnGetDynamicSize -= OnGetGuildChatDynamicSize;
        scrGuild.OnRefreshCell -= OnRefreshGuildCell;
        scrGuild.mScrollRect.onValueChanged.RemoveListener(OnGuildScrollValChange);
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        clickScreenOtherSpace.RemoveAllListeners();
    }
    private void OnRefreshGuildCell(ScrollerDataType type, CellView cell)
    {
        OnRefreshCell(type, cell);
    }
    private void OnRefreshWorldCell(ScrollerDataType type, CellView cell)
    {
        OnRefreshCell(type, cell);
    }
    private void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        if (type == ScrollerDataType.Header)
        {
            var _cell = cell.GetComponent<ChatPlayerMineCell>();
            _cell?.Refresh(cell);
        }
        else if (type == ScrollerDataType.Normal)
        {
            var _cell = cell.GetComponent<ChatPlayerOtherCell>();
            _cell?.Refresh(cell);
        }
        else if (type == ScrollerDataType.Tail)
        {
            var _cell = cell.GetComponent<ChatSysCell>();
            _cell?.Refresh(cell);
        }
        else if (type == ScrollerDataType.Extra1)
        {
            var _cell = cell.GetComponent<ChatDateCell>();
            _cell?.Refresh(cell);
        }
    }
    private void OnSecondEvent()
    {
        UpdateSendButton();
    }
    private void OnClickScreenOtherSpace()
    {
        isSettingOpen = !isSettingOpen;
        transSettings.SetActive(isSettingOpen);
    }
    private bool m_isJumpArea;
    public bool isJumpArea
    {
        get { return m_isJumpArea; }
        set
        {
            if (m_isJumpArea == value)
                return;
            m_isJumpArea = value;
            // isJumpArea = true (用户在底部):开启锁定底部
            if (value)
            {
                if (scrWorld.lockType != EnhanceLockType.LockVerticalBottom)
                    scrWorld.lockType = EnhanceLockType.LockVerticalBottom;
                // 如果回到底部,直接清零消息
                ClearUnreadMsg();
            }
            // isJumpArea = false (用户看历史):保持视觉位置
            else
            {
                if (scrWorld.lockType != EnhanceLockType.KeepVertical)
                    scrWorld.lockType = EnhanceLockType.KeepVertical;
            }
        }
    }
    private void OnWorldScrollValChange(Vector2 _pos)
    {
        OnScrollValChange(scrWorld, _pos);
    }
    private void OnGuildScrollValChange(Vector2 _pos)
    {
        OnScrollValChange(scrGuild, _pos);
    }
    private void OnScrollValChange(ScrollerController scorller, Vector2 _pos)
    {
        if (scorller.m_Scorller._ScrollSize <= 0)
            return;
        float _value = m_Percent / scorller.m_Scorller._ScrollSize * scorller.m_Scorller.ScrollRectSize;
        isJumpArea = _pos.y <= _value;
        // 检查滚动位置是否覆盖了新消息
        if (unreadMsgCount <= 0)
            return;
        int totalCount = scorller.GetNumberOfCells(scorller.m_Scorller);
        int thresholdIndex = totalCount - unreadMsgCount;
        // 获取当前视窗最底部显示的 Item 索引
        int currentBottomIndex = scorller.m_Scorller.EndDataIndex;
        // 如果当前看到的底部索引 超过了 新消息的起始线(减1是为了容错)
        if (currentBottomIndex >= thresholdIndex - 1)
        {
            ClearUnreadMsg();
        }
    }
    private void OnChatTabChange(ChatTab entrance)
    {
        CreaterAll(entrance);
    }
    private void OnUpdateTalkEvent(ChatChannel type, TalkData data)
    {
        RefreshAll(type, playerId: data.PlayerID);
    }
    private void OnUpdateTalkCacheList()
    {
        RefreshAll(manager.nowChatChannel);
    }
    private void CreaterAll(ChatTab chatTab)
    {
        if (chatTab == ChatTab.World)
        {
            manager.nowChatChannel = ChatChannel.World;
        }
        else if (chatTab == ChatTab.Guild)
        {
            manager.nowChatChannel = ChatChannel.Guild;
        }
        UpdateSendButton();
        // 打开界面时默认到底部,无未读
        isJumpArea = true;
        scrWorld.lockType = EnhanceLockType.LockVerticalBottom; // 初始锁定底部
        scrWorld.SetActive(chatTab == ChatTab.World);
        scrGuild.lockType = EnhanceLockType.LockVerticalBottom; // 初始锁定底部
        scrGuild.SetActive(chatTab == ChatTab.Guild);
        transInput.SetActive(chatTab == ChatTab.World || chatTab == ChatTab.Guild);
        CreateChatTabScroller();
        switch (chatTab)
        {
            case ChatTab.World:
                manager.nowChatChannel = ChatChannel.World;
                CreateScroller(scrWorld, ChatChannel.World);
                ScrollerJump(scrWorld, ChatChannel.World);
                ClearUnreadMsg();
                break;
            case ChatTab.Guild:
                manager.nowChatChannel = ChatChannel.Guild;
                CreateScroller(scrGuild, ChatChannel.Guild);
                ScrollerJump(scrGuild, ChatChannel.Guild);
                ClearUnreadMsg();
                break;
        }
    }
    float KeepArea(ScrollerController scroller)
    {
        if (scroller.GetNumberOfCells(scroller.m_Scorller) >= manager.maxTalkCount)
        {
            float totalRemovedSize = 0f;
            for (int i = 0; i < manager.deleteTalkCount; i++)
            {
                float cellSize = scroller.GetCellSize(i);
                totalRemovedSize += cellSize + scroller.m_Scorller.spacing;
            }
            return totalRemovedSize;
        }
        return 0;
    }
    private void RefreshAll(ChatChannel type, uint playerId = 0)
    {
        scrChatTab.m_Scorller.RefreshActiveCellViews();
        if (type == ChatChannel.World)
        {
            RefreshChat(type, scrWorld, playerId);
        }
        else if (type == ChatChannel.Guild)
        {
            RefreshChat(type, scrGuild, playerId);
        }
    }
    private void RefreshChat(ChatChannel type, ScrollerController scroller, uint playerId = 0)
    {
        if (!manager.TryGetTalkData(type, out List<TalkData> datas) || datas == null)
            return;
        int numberOfCells = scroller.GetNumberOfCells(scroller.m_Scorller);
        if (numberOfCells < manager.maxTalkCount)
        {
            for (int i = numberOfCells; i < datas.Count; i++)
            {
                TalkData data = datas[i];
                ScrollerDataType scrollerDataType = GetCellType(data);
                scroller.AddCell(scrollerDataType, i);
                int talkDataType = manager.GetTalkDataType(data);
                float height = GetHeight(talkDataType, data.Content, data.InfoList);
                scroller.m_Scorller.AddHeight(true, height);
                //Debug.Log($"ChatWin AddCell i {i} AddHeight {height}");
            }
        }
        else
        {
            scroller.m_Scorller.RefreshActiveCellViews();
        }
        if (type == ChatChannel.World && manager.nowChatTab != ChatTab.World)
            return;
        if (type == ChatChannel.Guild && manager.nowChatTab != ChatTab.Guild)
            return;
        // 1. 自己发送的消息 -> 强制跳转到底部 + 清零
        if (playerId == PlayerDatas.Instance.PlayerId)
        {
            isJumpArea = true;
            scroller.lockType = EnhanceLockType.LockVerticalBottom;
            ScrollerJump(scroller, type);
            ClearUnreadMsg(); // 自己发的消息不用提示
        }
        // 2. 别人发消息 & 当前在底部 -> 自动跟随 + 清零
        else if (isJumpArea)
        {
            scroller.lockType = EnhanceLockType.LockVerticalBottom;
            ScrollerJump(scroller, type);
            ClearUnreadMsg(); // 在底部看着不用提示
        }
        // 3. 别人发消息 & 当前在看历史 -> 保持位置 + 增加未读计数
        else
        {
            scroller.lockType = EnhanceLockType.KeepVertical;
            float offset = KeepArea(scroller);
            if (Math.Abs(offset) > 0.001f)
            {
                ScrollerJump(scroller, offset);
            }
            if (type == manager.nowChatChannel)
            {
                unreadMsgCount++;
                UpdateUnreadMsgUI();
            }
            // 特殊情况:如果未读数量巨大(超过了总显示数量),说明整个列表都被刷新了,直接消零
            if (unreadMsgCount >= scrWorld.GetNumberOfCells(scrWorld.m_Scorller))
            {
                ClearUnreadMsg();
            }
        }
    }
    private bool OnGetWorldChatDynamicSize(ScrollerDataType _type, int _index, out float height)
    {
        return OnGetChatDynamicSize(_type, _index, out height);
    }
    private bool OnGetGuildChatDynamicSize(ScrollerDataType _type, int _index, out float height)
    {
        return OnGetChatDynamicSize(_type, _index, out height);
    }
    private bool OnGetChatDynamicSize(ScrollerDataType _type, int _index, out float height)
    {
        height = 0;
        if (!manager.TryGetChatData(manager.nowChatChannel, _index, out TalkData data) || data == null)
            return false;
        switch (_type)
        {
            case ScrollerDataType.Header:
                height = m_ChatMineCell.GetHeight(data.Content, data.InfoList);
                return true;
            case ScrollerDataType.Normal:
                height = m_ChatOtherCell.GetHeight(data.Content, data.InfoList);
                return true;
            case ScrollerDataType.Tail:
                height = m_ChatSysCell.GetHeight(data.Content, data.InfoList);
                return true;
            case ScrollerDataType.Extra1:
                height = 30;
                return true;
        }
        return true;
    }
    private void OnRefreshChatTabCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<ChatTabCell>();
        _cell?.Display(cell.index, cell);
    }
    private void CreateChatTabScroller()
    {
        scrChatTab.Refresh();
        for (int i = 0; i < manager.tabShowList.Count; i++)
        {
            CellInfo cellInfo = new CellInfo();
            cellInfo.infoInt1 = (int)manager.tabShowList[i];
            scrChatTab.AddCell(ScrollerDataType.Header, i, cellInfo);
        }
        scrChatTab.Restart();
    }
    private void CreateScroller(ScrollerController scroller, ChatChannel type)
    {
        scroller.Refresh();
        if (manager.TryGetTalkData(type, out List<TalkData> datas))
        {
            for (int i = 0; i < datas.Count; i++)
            {
                TalkData data = datas[i];
                ScrollerDataType scrollerDataType = GetCellType(data);
                scroller.AddCell(scrollerDataType, i);
            }
        }
        scroller.Restart();
    }
    private void ScrollerJump(ScrollerController scroller, ChatChannel type)
    {
        scroller.lockType = EnhanceLockType.LockVerticalBottom;
        scroller.ResetScrollPos();
        int jumpIndex = manager.GetJumpIndex(type);
        scroller.JumpIndex(jumpIndex);
        //Debug.Log($"ChatWin JumpIndex {jumpIndex}");
        isJumpArea = true;
    }
    private void ScrollerJump(ScrollerController scroller, float _offset)
    {
        scroller.JumpIndex(-_offset, 0, EnhancedUI.EnhancedScroller.EnhancedScroller.TweenType.immediate);
    }
    // 0-系统 1-日期 2-自己 3-其他玩家
    private ScrollerDataType GetCellType(TalkData data)
    {
        int type = manager.GetTalkDataType(data);
        if (type == 0)
            return ScrollerDataType.Tail;
        else if (type == 1)
            return ScrollerDataType.Extra1;
        else if (type == 2)
            return ScrollerDataType.Header;
        else
            return ScrollerDataType.Normal;
    }
    public float GetHeight(int type, string content, ArrayList list)
    {
        switch (type)
        {
            case 0:
                return m_ChatSysCell.GetHeight(content, list);
            case 1:
                return 30;
            case 2:
                return m_ChatMineCell.GetHeight(content, list);
            case 3:
                return m_ChatOtherCell.GetHeight(content, list);
        }
        return 0;
    }
    private void UpdateSendButton()
    {
        bool isCanSend = manager.IsCanSend(manager.nowChatChannel, out int remainingSeconds);
        btnSendChat.interactable = isCanSend;
        imgSendChat.gray = !isCanSend;
        txtSendChat.text = isCanSend ? Language.Get("Chat02") : Language.Get("Chat14", remainingSeconds);
        txtSendChat.colorType = isCanSend ? TextColType.NavyBrown : TextColType.LightWhite;
    }
}
Main/System/Chat/LocalChatHistory.cs
File was deleted
Main/System/Chat/LocalChatHistory.cs.meta
File was deleted
Main/System/Guild/GuildBaseWin.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using DG.Tweening;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
@@ -20,8 +21,9 @@
    [SerializeField] Text guildMemberCountText;
    [SerializeField] Button rankBtn;
    [SerializeField] Button storeBtn;
    [SerializeField] RichText talkInfoText;
    [SerializeField] Button talkBtn;
    [SerializeField] Button talkBtn1;
    [SerializeField] Button requestBtn;
@@ -56,6 +58,8 @@
            UIManager.Instance.OpenWindow<StoreBaseWin>();
        });
        InitHawker();
        talkBtn.AddListener(OnClickTalkButton);
        talkBtn1.AddListener(OnClickTalkButton);
    }
@@ -65,7 +69,7 @@
            return;
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        GlobalTimeEvent.Instance.fiveSecondEvent += OnFiveSecondEvent;
        ChatManager.Instance.OnUpdateTalkEvent += OnUpdateTalkEvent;
        Display();
    }
@@ -73,13 +77,21 @@
    {
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        GlobalTimeEvent.Instance.fiveSecondEvent -= OnFiveSecondEvent;
        ChatManager.Instance.OnUpdateTalkEvent -= OnUpdateTalkEvent;
    }
    private void OnUpdateTalkEvent(ChatChannel channel, TalkData data)
    {
        if (channel != ChatChannel.Guild)
            return;
        ShowTalkInfo();
    }
    void Display()
    {
        ShowGuildInfo();
        ShowHawkerTime();
        ShowTalkInfo();
    }
    void OnSecondEvent()
@@ -124,7 +136,7 @@
            if (modelPlay)
                HawkerMove(true);
        }
    }
@@ -159,8 +171,8 @@
            FuncNPCManager.Instance.isHawkerStandBy = isShow;
        };
    }
    //NPC对话相关
@@ -190,4 +202,60 @@
        var npc = funcNPCs[index].GetModel();
        npc.PlayAnimation("idle", true);
    }
    public void OnClickTalkButton()
    {
        ChatManager.Instance.nowChatTab = ChatTab.Guild;
        ChatManager.Instance.nowChatChannel = ChatChannel.Guild;
        UIManager.Instance.OpenWindow<ChatWin>();
    }
    public string GetGuildChatAreaContent(TalkData talkData)
    {
        int type = ChatManager.Instance.GetTalkDataType(talkData);
        if (type == 0)
        {
            return Language.Get("L1100", Language.Get("Chat13"), talkData.Content);
        }
        else if (type == 1)
        {
            return Language.Get("L1100", Language.Get("Chat13"), talkData.Content);
        }
        else
        {
            return Language.Get("L1100", talkData.Name, talkData.Content);
        }
    }
    public Color32 GetGuildChatAreaColor(TalkData talkData)
    {
        int type = ChatManager.Instance.GetTalkDataType(talkData);
        if (type == 2)
        {
            return ChatManager.Instance.areaMyColor;
        }
        else
        {
            return ChatManager.Instance.areaOtherColor;
        }
    }
    void ShowTalkInfo()
    {
        if (!ChatManager.Instance.TryGetTalkData(ChatChannel.Guild, out List<TalkData> datas) || datas.IsNullOrEmpty())
        {
            talkInfoText.text = string.Empty;
            return;
        }
        StringBuilder stringBuilder = new StringBuilder();
        int startIndex = Mathf.Max(datas.Count - 2, 0);
        for (int i = startIndex; i < datas.Count; i++)
        {
            TalkData data = datas[i];
            stringBuilder.Append(UIHelper.AppendColor(GetGuildChatAreaColor(data), GetGuildChatAreaContent(data)));
            stringBuilder.AppendLine();
        }
        talkInfoText.text = stringBuilder.ToString();
    }
}
Main/System/Hero/UIHeroController.cs
@@ -71,7 +71,7 @@
        }
        onComplete = _onComplete;
        pool = GameObjectPoolManager.Instance.RequestPool(UILoader.LoadPrefab("UIHero"));
        pool = GameObjectPoolManager.Instance.GetPool(UILoader.LoadPrefab("UIHero"));
        if (!transform.gameObject.activeSelf)
        {
Main/System/Horse/HorseController.cs
@@ -34,7 +34,7 @@
        this.transform.localScale = Vector3.one * scale;
        onComplete = _onComplete;
        pool = GameObjectPoolManager.Instance.RequestPool(UILoader.LoadPrefab("UIHorse"));
        pool = GameObjectPoolManager.Instance.GetPool(UILoader.LoadPrefab("UIHorse"));
        if (!transform.gameObject.activeSelf)
        {
Main/System/Main/HomeWin.cs
@@ -11,7 +11,6 @@
/// </summary>
public class HomeWin : UIBase
{
    //经验区
    [SerializeField] Text playerLevelText;
    [SerializeField] SmoothSlider expSlider;
@@ -68,7 +67,24 @@
    [SerializeField] Button horseBtn;
    [SerializeField] HorseController horseImg;
    [SerializeField] Text horseLVText;
    //聊天
    [SerializeField] Button chatBtn;
    [SerializeField] Transform transFastChat;
    [SerializeField] InputField inputFastChat;
    [SerializeField] ButtonEx btnFastChatSend;
    [SerializeField] ImageEx imgFastChatSend;
    [SerializeField] TextEx txtFastChatSend;
    [SerializeField] ButtonEx btnFastChatClose;
    [SerializeField] ButtonEx btnChatWin;
    [SerializeField] TextEx txtChatChannel;
    [SerializeField] Transform transChatBulletView;
    GameObject chatBulletViewPrefab;
    /// <summary>
    /// 初始化组件
    /// </summary>
@@ -122,12 +138,55 @@
        });
        restBtn.AddListener(GotoRest);
        funcColBtn.AddListener(()=>
        funcColBtn.AddListener(() =>
        {
            rightFuncInHome.ShowFuncCol(true);
        });
        horseBtn.AddListener(OpenHorse);
        chatBtn.SetListener(() =>
        {
            if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.Chat, true))
                return;
            transFastChat.SetActive(true);
            chatBtn.SetActive(false);
            UpdateChat(ChatManager.Instance.nowChatTab);
        });
        btnFastChatClose.SetListener(() =>
        {
            transFastChat.SetActive(false);
            chatBtn.SetActive(true);
        });
        btnChatWin.SetListener(() =>
        {
            transFastChat.SetActive(false);
            chatBtn.SetActive(true);
            UIManager.Instance.OpenWindow<ChatWin>();
        });
        btnFastChatSend.SetListener(() =>
        {
            // 如果在聊天输入界面无输入文字点击发送,则关闭聊天输入界面
            if (string.IsNullOrEmpty(inputFastChat.text))
            {
                transFastChat.SetActive(false);
                chatBtn.SetActive(true);
                return;
            }
            if (!ChatManager.Instance.CheckChatLimit(inputFastChat.text, out int errorCode))
            {
                ChatManager.Instance.ShowChatErrorTip(errorCode);
                return;
            }
            ChatManager.Instance.SendChatInfo(ChatManager.Instance.nowChatChannel, inputFastChat.text);
            ChatManager.Instance.AddChatChannelSendTime(ChatManager.Instance.nowChatChannel, TimeUtility.AllSeconds);
            UpdateSendButton();
            inputFastChat.text = string.Empty;
        });
    }
@@ -148,6 +207,12 @@
        officialTip.SetActive(OfficialRankManager.Instance.CanOfficialLVUP());
        DisplayHorse();
        DisplayChatBulletView();
        chatBtn.SetActive(true);
        transFastChat.SetActive(false);
        inputFastChat.characterLimit = ChatManager.Instance.characterLimit;
        UpdateSendButton();
    }
    protected override void OnPreOpen()
@@ -167,6 +232,8 @@
        FirstChargeManager.Instance.OnFirstChargeTaskUpdateEvent += OnFirstChargeTaskUpdateEvent;
        OfficialRankManager.Instance.RealmMissionRefreshEvent += OnOfficialCanLVUpEvent;
        HorseManager.Instance.OnHorseUpdateEvent += DisplayHorse;
        ChatManager.Instance.OnChatTabChangeEvent += OnChatTabChangeEvent;
        GuildManager.Instance.EnterOrQuitGuildEvent += EnterOrQuitGuildEvent;
        Display();
        DisplayFirstChargeBtn();
@@ -195,8 +262,54 @@
        FirstChargeManager.Instance.OnFirstChargeTaskUpdateEvent -= OnFirstChargeTaskUpdateEvent;
        OfficialRankManager.Instance.RealmMissionRefreshEvent -= OnOfficialCanLVUpEvent;
        HorseManager.Instance.OnHorseUpdateEvent -= DisplayHorse;
        ChatManager.Instance.OnChatTabChangeEvent -= OnChatTabChangeEvent;
        GuildManager.Instance.EnterOrQuitGuildEvent -= EnterOrQuitGuildEvent;
        //  关闭的时候把战斗界面也给关了 虽然是在外面开的
        UIManager.Instance.CloseWindow<BattleWin>();
    }
    private void UpdateSendButton()
    {
        bool isCanSend = ChatManager.Instance.IsCanSend(ChatManager.Instance.nowChatChannel, out int remainingSeconds);
        btnFastChatSend.interactable = isCanSend;
        imgFastChatSend.gray = !isCanSend;
        txtFastChatSend.text = isCanSend ? Language.Get("Chat11") : Language.Get("Chat14", remainingSeconds);
        txtFastChatSend.colorType = isCanSend ? TextColType.NavyBrown : TextColType.LightWhite;
    }
    private void EnterOrQuitGuildEvent(bool obj)
    {
        if (!obj)
        {
            UpdateChat(ChatManager.Instance.nowChatTab);
        }
    }
    private void OnChatTabChangeEvent(ChatTab tab)
    {
        UpdateChat(tab);
    }
    void UpdateChat(ChatTab tab)
    {
        txtChatChannel.text = ChatManager.Instance.GetChatTabName(tab);
    }
    void DisplayChatBulletView()
    {
        if (chatBulletViewPrefab == null)
        {
            chatBulletViewPrefab = UIUtility.CreateWidget("ChatBulletView", "ChatBulletView");
        }
        chatBulletViewPrefab.transform.SetParentEx(transChatBulletView.transform, Vector3.zero, Quaternion.identity, Vector3.one);
        // 新增:确保 ChatBulletView 组件启用
        // var chatBulletView = chatBulletViewPrefab.GetComponent<ChatBulletView>();
        // if (chatBulletView != null)
        // {
        //     chatBulletView.enabled = true;
        // }
    }
    void OnOfficialCanLVUpEvent()
@@ -308,7 +421,7 @@
        if (showEffect)
        {
            headEffect.transform.DOLocalMove(new Vector3(400 * expSlider.value - 200 - 24, 0, 0), 0.5f);
            headEffect.Play(closePMA:true);
            headEffect.Play(closePMA: true);
        }
    }
@@ -358,7 +471,7 @@
            GameNetSystem.Instance.SendInfo(getReward);
            return;
        }
        TaskManager.Instance.ClickTask();
    }
@@ -509,6 +622,7 @@
    private void OnSecondEvent()
    {
        DisplayFirstChargeBtn();
        UpdateSendButton();
    }
    void OnUnLockHeroCountEvent()
@@ -525,7 +639,7 @@
            //equipShowSwitch;//当前配置的坐骑外观ID存储在(最大支持 1~999)
            var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.GetUsingHorseSkinID(true));
            horseImg.Create(skinConfig.SkinID, 0, 0.6f);
            horseLVText.text = Language.Get("Horse8",HorseManager.Instance.classLV, HorseManager.Instance.horseLV);
            horseLVText.text = Language.Get("Horse8", HorseManager.Instance.classLV, HorseManager.Instance.horseLV);
        }
        else
        {
Main/System/Message/SysNotifyMgr.cs
@@ -111,7 +111,6 @@
#if UNITY_EDITOR
            string hint = Language.Get("L1093", key);
            ScrollTip.ShowTip(hint);
            ChatManager.Instance.RevChatInfo(hint);
#endif
        }
        else
@@ -234,20 +233,13 @@
                    ServerTipDetails.ShowServerTip(msg, tipInfoList, order);
                    break;
                case SysNotifyType.SysMarqueeTip:
                    ServerTipDetails.ShowMarquee(msg, tipInfoList,order);
                    ServerTipDetails.ShowMarquee(msg, tipInfoList, order);
                    break;
                case SysNotifyType.SysChanelTip:
                    ChatManager.Instance.RevChatInfo(msg, tipInfoList);
                case SysNotifyType.ChatChannelWorld:
                    ChatManager.Instance.AddSysData(msg, tipInfoList, ChatChannel.World);
                    break;
                case SysNotifyType.SysFairyQuestionTip:
                    ChatManager.Instance.RevChatInfo(msg, tipInfoList, ChatInfoType.FairyQuestion);
                    break;
                case SysNotifyType.SysFairyTip:
                    ChatManager.Instance.RevChatInfo(msg, tipInfoList, ChatInfoType.FairyTip);
                    break;
                case SysNotifyType.SysTeamTip:
                    ChatManager.Instance.RevChatInfo(msg, tipInfoList, ChatInfoType.TeamTip);
                case SysNotifyType.ChatChannelGuild:
                    ChatManager.Instance.AddSysData(msg, tipInfoList, ChatChannel.Guild);
                    break;
                case SysNotifyType.SysRealmTip:
                    if (OnSysTipEvent != null)
@@ -260,9 +252,7 @@
                // case SysNotifyType.SysEvenKill:
                //     BattleHint.Instance.ReceiveEvenKill(msg, tipInfoList);
                //     break;
                case SysNotifyType.SysFactionTip:
                    ChatManager.Instance.RevChatInfo(msg, tipInfoList, ChatInfoType.default2);
                    break;
            }
        }
@@ -282,7 +272,8 @@
        SysChatWin = 6, //聊天位置的提示
        SysEvenKill = 7,//上古战场连杀提示
        SysFixedTip1 = 11,//固定提示2
        SysChanelTip = 20,//系统频道显示
        ChatChannelWorld = 20,//聊天-世界频道
        ChatChannelGuild = 21,//聊天-公会频道
        SysFairyQuestionTip = 31,//仙盟频道问答提示
        SysFairyTip = 32,//仙盟频道信息提示
        SysTeamTip = 41,//队伍频道提示
Main/System/PhantasmPavilion/ChatBubbleHelper.cs
@@ -1,20 +1,18 @@
public static class ChatBubbleHelper
{
    public static int GetOtherChatBubbleID(int nowID)
    public static int GetOtherChatBubbleID(int job, int nowID)
    {
        return 0;//PhantasmPavilionModel.Instance.GetNowOtherChatBubbleID(nowID);
        int defaultID = GetChatBubbleDefaultID(job);
        return ChatBubbleBoxConfig.HasKey(nowID) ? nowID : defaultID;
    }
    public static int GetMyChatBubbleID()
    {
        return 0;//PhantasmPavilionModel.Instance.GetNowChatBubbleID();
        return PhantasmPavilionManager.Instance.TryGetNowShowID(PhantasmPavilionType.ChatBox, out int defaultID) ? defaultID : 0;
    }
    public static int GetChatBubbleDefaultID()
    public static int GetChatBubbleDefaultID(int job)
    {
        // PhantasmPavilionModel.Instance.TryGetDefaultID(PhantasmPavilionTab.ChatBubble, 0, out int defaultID);
        return 0;//defaultID;
        return PhantasmPavilionManager.Instance.TryGetDefaultID(PhantasmPavilionType.ChatBox, job, out int defaultID) ? defaultID : 0;
    }
}
Main/System/PhantasmPavilion/PhantasmPavilionManager.cs
@@ -289,6 +289,33 @@
        }
    }
    public void ShowChatBox(ImageEx imgFace, UIFrame uiFrame, int resourceType, string resourceValue)
    {
        switch (resourceType)
        {
            // 静态图
            case 1:
                imgFace.enabled = true;
                uiFrame.enabled = false;
                if (!IconConfig.HasKey(resourceValue))
                    return;
                imgFace.SetSprite(resourceValue);
                break;
            // 序列帧
            case 3:
                imgFace.enabled = true;
                uiFrame.enabled = true;
                imgFace.sprite = null;
                imgFace.overrideSprite = null;
                if (!UIFrameMgr.Inst.ContainsDynamicImage(resourceValue))
                    break;
                uiFrame.ResetFrame(resourceValue);
                uiFrame.enabled = true;
                break;
        }
    }
    public bool TryGetInfo(PhantasmPavilionType type, int id, out PhantasmPavilionData info)
    {
        info = null;
Main/System/SystemSetting/ChatSetting.cs
File was deleted
Main/System/SystemSetting/ChatSetting.cs.meta
File was deleted
Main/System/Tip/ScrollTip.cs
@@ -74,7 +74,7 @@
        if (pool == null)
        {
            var _prefab = UILoader.LoadPrefab("Tip");
            pool = GameObjectPoolManager.Instance.RequestPool(_prefab);
            pool = GameObjectPoolManager.Instance.GetPool(_prefab);
        }
        if (pool != null)
        {
Main/Utility/LanguageVerify.cs
@@ -42,33 +42,6 @@
        return;
    }
    public static uint toPlayer = 0;
    public static string toPlayerName = string.Empty;
    public static int toPlayerLevel = 0;
    public void VerifyChat(string content, ChatInfoType channelType, Action<bool, string> callback)
    {
        int channel = 0;
        var chatCenter = ChatCenter.Instance;
        if (chatCenter.IsChatBanned || chatCenter.IsClientBan(channelType))
        {
            if (!(IsFairyFeast(channelType) || IsSystemChat(content)))
            {
                return;
            }
        }
        if (!GetChannel(channelType, out channel))
        {
            return;
        }
        if (callback != null)
        {
            callback(true, content);
            callback = null;
        }
    }
    Dictionary<long, List<string>> transferContents = new Dictionary<long, List<string>>();
    Dictionary<long, List<int>> splitContents = new Dictionary<long, List<int>>();
@@ -116,12 +89,9 @@
        AddMathcs(WordAnalysis.Color_Start_Regex.Matches(content));
        AddMathcs(WordAnalysis.Color_End_Regex.Matches(content));
        AddMathcs(ImgAnalysis.FaceRegex.Matches(content));
        AddMathcs(ChatManager.InviteRegex.Matches(content));
        AddMathcs(WordAnalysis.Size_Start_Regex.Matches(content));
        AddMathcs(WordAnalysis.Size_End_Regex.Matches(content));
        AddMathcs(WordAnalysis.Space_Regex.Matches(content));
        AddMathcs(ChatManager.KillRegex.Matches(content));
        AddMathcs(ChatCenter.s_VoiceRegex.Matches(content));
        matchs.Sort((x, y) =>
        {
            return x.index.CompareTo(y.index);
@@ -137,12 +107,9 @@
        content = WordAnalysis.Color_Start_Regex.Replace(content, string.Empty);
        content = WordAnalysis.Color_End_Regex.Replace(content, string.Empty);
        content = ImgAnalysis.FaceRegex.Replace(content, string.Empty);
        content = ChatManager.InviteRegex.Replace(content, string.Empty);
        content = WordAnalysis.Size_Start_Regex.Replace(content, string.Empty);
        content = WordAnalysis.Size_End_Regex.Replace(content, string.Empty);
        content = WordAnalysis.Space_Regex.Replace(content, string.Empty);
        content = ChatManager.KillRegex.Replace(content, string.Empty);
        content = ChatCenter.s_VoiceRegex.Replace(content, string.Empty);
        return content;
    }
@@ -173,57 +140,6 @@
        transferContents.Remove(tick);
        splitContents.Remove(tick);
        return sb.ToString();
    }
    bool IsSystemChat(string content)
    {
        if (ChatManager.InviteRegex.IsMatch(content)
            || ChatManager.KillRegex.IsMatch(content))
        {
            return true;
        }
        return false;
    }
    bool GetChannel(ChatInfoType type, out int channel)
    {
        channel = 0;
        switch (type)
        {
            case ChatInfoType.World:
            case ChatInfoType.CrossServer:
                channel = 0;
                break;
            case ChatInfoType.Team:
                channel = 4;
                break;
            case ChatInfoType.Area:
            case ChatInfoType.Trumpet:
            case ChatInfoType.default1:
                channel = 5;
                break;
            case ChatInfoType.Fairy:
                channel = 2;
                break;
            case ChatInfoType.Friend:
                channel = 3;
                break;
            default:
                return false;
        }
        return true;
    }
    bool IsFairyFeast(ChatInfoType type)
    {
        // TODO YYL
        // var dailyQuestModel = ModelCenter.Instance.GetModel<DailyQuestModel>();
        // DailyQuestOpenTime dailyQuestOpenTime;
        // if (dailyQuestModel.TryGetOpenTime((int)DailyQuestType.FairyFeast, out dailyQuestOpenTime))
        // {
        //     return type == ChatInfoType.Fairy && dailyQuestOpenTime.InOpenTime();
        // }
        return false;
    }
    public class VerifyResponse