From cba51cf8ff175e277f628a2ac5a6aaa162ac472f Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期五, 13 九月 2024 16:27:57 +0800
Subject: [PATCH] 10261 【越南】【砍树】【英文】仙盟徽章增加定制仙盟ID支持; 增加聊天气泡,聊天表情;

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ChatBox.py             |  144 ++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerChatBox.py            |  404 ++++++++++++++++++
 ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py                                        |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_EmojiPack.py     |   30 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py                     |    3 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py                     |   52 ++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py                 |  210 ++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChItem.py                     |    1 
 ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py                                          |   52 ++
 ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py                                      |  210 ++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py              |    3 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamilyEmblem.py                            |   12 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                             |   18 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py                   |   70 +++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py                 |   57 --
 PySysDB/PySysDBPY.h                                                                                    |   25 +
 ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py                                          |    3 
 PySysDB/PySysDBG.h                                                                                     |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                        |   14 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_ChatBubbleBox.py |    5 
 20 files changed, 1,215 insertions(+), 103 deletions(-)

diff --git a/PySysDB/PySysDBG.h b/PySysDB/PySysDBG.h
index a29ebd1..a88d5b5 100644
--- a/PySysDB/PySysDBG.h
+++ b/PySysDB/PySysDBG.h
@@ -62,6 +62,7 @@
 	BYTE		_EmblemID;	//徽章ID
 	BYTE		UnlockFamilyLV;	//解锁仙盟等级,为0时代表其他方式解锁
 	DWORD		ExpireMinutes;	//有效时长,分钟
+	DWORD		CustomFamilyID;	//定制仙盟ID
 };
 
 //仙盟试炼伤血奖励表
diff --git a/PySysDB/PySysDBPY.h b/PySysDB/PySysDBPY.h
index ecd33fe..8c9896f 100644
--- a/PySysDB/PySysDBPY.h
+++ b/PySysDB/PySysDBPY.h
@@ -2233,6 +2233,31 @@
 	BYTE		_BoxID;	//气泡框ID
 	WORD		NeedLV;	//所需等级
 	BYTE		NeedVIPLVGift;	//所需购买VIP等级礼包
+	BYTE		UnlockDefault;	//是否默认激活
+    DWORD		ExpireMinutes;	//时效分钟,0永久
+	list		LightAttrType;	//点亮属性类型
+	list		LightAttrValue;	//点亮属性值
+    DWORD		LightFightPower;	//点亮附加战力
+};
+
+//聊天气泡升星表
+
+struct tagChatBubbleBoxStar
+{
+	BYTE		_BoxID;	//气泡框ID
+	BYTE		_BoxStar;	//气泡框星级
+	list		StarUpNeedItemList;	//升该星所需物品 [[物品ID,个数], ...]
+	list		StarAttrType;	//累计总属性类型
+	list		StarAttrValue;	//累计总属性值
+};
+
+//表情包表
+
+struct	tagEmojiPack
+{
+	DWORD		_EmojiPackID;	//表情包ID
+	BYTE		UnlockDefault;	//是否默认激活
+    DWORD		ExpireMinutes;	//时效分钟,0永久
 };
 
 //充值返利活动表
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
index e5d9468..aee33cf 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
@@ -6240,6 +6240,58 @@
 
 
 #------------------------------------------------------
+# A2 36 聊天气泡框升星 #tagCMChatBubbleBoxStarUP
+
+class  tagCMChatBubbleBoxStarUP(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("BoxID", c_ubyte),    #气泡ID
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xA2
+        self.SubCmd = 0x36
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xA2
+        self.SubCmd = 0x36
+        self.BoxID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCMChatBubbleBoxStarUP)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// A2 36 聊天气泡框升星 //tagCMChatBubbleBoxStarUP:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                BoxID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.BoxID
+                                )
+        return DumpString
+
+
+m_NAtagCMChatBubbleBoxStarUP=tagCMChatBubbleBoxStarUP()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMChatBubbleBoxStarUP.Cmd,m_NAtagCMChatBubbleBoxStarUP.SubCmd))] = m_NAtagCMChatBubbleBoxStarUP
+
+
+#------------------------------------------------------
 # A2 10 清除副本CD#tagCMClearFBCD
 
 class  tagCMClearFBCD(Structure):
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
index 52fd278..e617a17 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
@@ -30611,18 +30611,17 @@
 #------------------------------------------------------
 # A7 17 聊天气泡框状态 #tagMCChatBubbleBoxState
 
-class  tagMCChatBubbleBoxState(Structure):
+class  tagMCChatBubbleBox(Structure):
     _pack_ = 1
     _fields_ = [
-                  ("Cmd", c_ubyte),
-                  ("SubCmd", c_ubyte),
-                  ("BoxState", c_int),    # 按二进制位存储代表是否已开启,暂支持31位,以后有需要再加
+                  ("BoxID", c_ubyte),    #气泡ID
+                  ("State", c_ubyte),    #是否已激活
+                  ("EndTime", c_int),    #到期时间戳,0为永久
+                  ("Star", c_ubyte),    #星级
                   ]
 
     def __init__(self):
         self.Clear()
-        self.Cmd = 0xA7
-        self.SubCmd = 0x17
         return
 
     def ReadData(self, stringData, _pos=0, _len=0):
@@ -30631,33 +30630,98 @@
         return _pos + self.GetLength()
 
     def Clear(self):
-        self.Cmd = 0xA7
-        self.SubCmd = 0x17
-        self.BoxState = 0
+        self.BoxID = 0
+        self.State = 0
+        self.EndTime = 0
+        self.Star = 0
         return
 
     def GetLength(self):
-        return sizeof(tagMCChatBubbleBoxState)
+        return sizeof(tagMCChatBubbleBox)
 
     def GetBuffer(self):
         return string_at(addressof(self), self.GetLength())
 
     def OutputString(self):
         DumpString = '''// A7 17 聊天气泡框状态 //tagMCChatBubbleBoxState:
-                                Cmd:%s,
-                                SubCmd:%s,
-                                BoxState:%d
+                                BoxID:%d,
+                                State:%d,
+                                EndTime:%d,
+                                Star:%d
                                 '''\
                                 %(
-                                self.Cmd,
-                                self.SubCmd,
-                                self.BoxState
+                                self.BoxID,
+                                self.State,
+                                self.EndTime,
+                                self.Star
+                                )
+        return DumpString
+
+
+class  tagMCChatBubbleBoxState(Structure):
+    Head = tagHead()
+    Count = 0    #(BYTE Count)
+    BoxList = list()    #(vector<tagMCChatBubbleBox> BoxList)
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA7
+        self.Head.SubCmd = 0x17
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.Count):
+            temBoxList = tagMCChatBubbleBox()
+            _pos = temBoxList.ReadData(_lpData, _pos)
+            self.BoxList.append(temBoxList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA7
+        self.Head.SubCmd = 0x17
+        self.Count = 0
+        self.BoxList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        for i in range(self.Count):
+            length += self.BoxList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.Count)
+        for i in range(self.Count):
+            data = CommFunc.WriteString(data, self.BoxList[i].GetLength(), self.BoxList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                Count:%d,
+                                BoxList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.Count,
+                                "..."
                                 )
         return DumpString
 
 
 m_NAtagMCChatBubbleBoxState=tagMCChatBubbleBoxState()
-ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCChatBubbleBoxState.Cmd,m_NAtagMCChatBubbleBoxState.SubCmd))] = m_NAtagMCChatBubbleBoxState
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCChatBubbleBoxState.Head.Cmd,m_NAtagMCChatBubbleBoxState.Head.SubCmd))] = m_NAtagMCChatBubbleBoxState
 
 
 #------------------------------------------------------
@@ -30959,6 +31023,118 @@
 
 
 #------------------------------------------------------
+# A7 21 表情包信息 #tagMCEmojiPackInfo
+
+class  tagMCEmojiPack(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("PackID", c_ubyte),    #表情包ID
+                  ("State", c_ubyte),    #是否已激活
+                  ("EndTime", c_int),    #到期时间戳,0为永久
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.PackID = 0
+        self.State = 0
+        self.EndTime = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCEmojiPack)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// A7 21 表情包信息 //tagMCEmojiPackInfo:
+                                PackID:%d,
+                                State:%d,
+                                EndTime:%d
+                                '''\
+                                %(
+                                self.PackID,
+                                self.State,
+                                self.EndTime
+                                )
+        return DumpString
+
+
+class  tagMCEmojiPackInfo(Structure):
+    Head = tagHead()
+    Count = 0    #(BYTE Count)
+    EmojiPackList = list()    #(vector<tagMCEmojiPack> EmojiPackList)
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA7
+        self.Head.SubCmd = 0x21
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.Count):
+            temEmojiPackList = tagMCEmojiPack()
+            _pos = temEmojiPackList.ReadData(_lpData, _pos)
+            self.EmojiPackList.append(temEmojiPackList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA7
+        self.Head.SubCmd = 0x21
+        self.Count = 0
+        self.EmojiPackList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        for i in range(self.Count):
+            length += self.EmojiPackList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.Count)
+        for i in range(self.Count):
+            data = CommFunc.WriteString(data, self.EmojiPackList[i].GetLength(), self.EmojiPackList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                Count:%d,
+                                EmojiPackList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.Count,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagMCEmojiPackInfo=tagMCEmojiPackInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCEmojiPackInfo.Head.Cmd,m_NAtagMCEmojiPackInfo.Head.SubCmd))] = m_NAtagMCEmojiPackInfo
+
+
+#------------------------------------------------------
 # A7 15 通知仙盟抢Boss伤血信息 #tagMCFamilyBossHurtList
 
 class  tagMCFamilyBossHurt(Structure):
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py b/ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py
index d762969..1844e1f 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py
@@ -76,6 +76,7 @@
                         ("BYTE", "EmblemID", 1),
                         ("BYTE", "UnlockFamilyLV", 0),
                         ("DWORD", "ExpireMinutes", 0),
+                        ("DWORD", "CustomFamilyID", 0),
                         ),
 
                 "FamilyBossHurtAward":(
@@ -1258,7 +1259,8 @@
         
     def GetEmblemID(self): return self.attrTuple[0] # 徽章ID BYTE
     def GetUnlockFamilyLV(self): return self.attrTuple[1] # 解锁仙盟等级,为0时代表其他方式解锁 BYTE
-    def GetExpireMinutes(self): return self.attrTuple[2] # 有效时长,分钟 DWORD
+    def GetExpireMinutes(self): return self.attrTuple[2] # 有效时长,分钟 DWORD
+    def GetCustomFamilyID(self): return self.attrTuple[3] # 定制仙盟ID DWORD
 
 # 仙盟试炼伤血奖励表
 class IPY_FamilyBossHurtAward():
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamilyEmblem.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamilyEmblem.py
index da087bb..50df712 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamilyEmblem.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamilyEmblem.py
@@ -123,6 +123,9 @@
     ipyData = IpyGameDataPY.GetIpyGameData("FamilyEmblem", emblemID)
     if not ipyData:
         return
+    if ipyData.GetCustomFamilyID():
+        GameWorld.Log("仙盟定制徽章不需要添加! familyID=%s,emblemID=%s" % (familyID, emblemID))
+        return
     unlockFamilyLV = ipyData.GetUnlockFamilyLV()
     if unlockFamilyLV:
         GameWorld.Log("仙盟解锁的徽章不需要添加! familyID=%s,emblemID=%s" % (familyID, emblemID))
@@ -180,8 +183,15 @@
     if not ipyData:
         return
     unlockFamilyLV = ipyData.GetUnlockFamilyLV()
+    customFamilyID = ipyData.GetCustomFamilyID()
     
-    if unlockFamilyLV:
+    if customFamilyID:
+        if familyID != customFamilyID:
+            GameWorld.DebugLog("不是该定制徽章所属仙盟,无法使用该徽章! emblemID=%s,customFamilyID=%s != %s" 
+                               % (emblemID, customFamilyID, familyID), playerID)
+            return
+        
+    elif unlockFamilyLV:
         if curFamily.GetLV() < unlockFamilyLV:
             GameWorld.DebugLog("仙盟等级不足,无法使用该徽章! emblemID=%s,unlockFamilyLV=%s" 
                                % (emblemID, unlockFamilyLV), playerID)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
index 5e43d27..3de6385 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
@@ -1785,7 +1785,7 @@
 )=range(5)
 
 # 战斗力模块类型
-Def_MFPType_Max = 36
+Def_MFPType_Max = 37
 ModuleFightPowerTypeList = (
 Def_MFPType_Role, # 角色 0
 Def_MFPType_Equip, # 装备(装备本身) 1
@@ -1822,6 +1822,7 @@
 Def_MFPType_GatherTheSoul, # 聚魂新 32
 Def_MFPType_Face, # 头像 33
 Def_MFPType_FacePic, # 头像框 34
+Def_MFPType_ChatBox, # 气泡框 35
 Def_MFPType_Other, # 其他
 ) = range(Def_MFPType_Max)
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index 22eaa47..2172b98 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -323,6 +323,22 @@
 PacketSubCMD_4=0x29
 PacketCallFunc_4=OnFacePicStarUP
 
+;聊天框
+[PlayerChatBox]
+ScriptName = Player\PlayerChatBox.py
+Writer = hxp
+Releaser = hxp
+RegType = 0
+RegisterPackCount = 2
+
+PacketCMD_1=0xA2
+PacketSubCMD_1=0x30
+PacketCallFunc_1=OnSetChatBubbleBox
+
+PacketCMD_2=0xA2
+PacketSubCMD_2=0x36
+PacketCallFunc_2=OnChatBubbleBoxStarUP
+
 ;事件
 [EventShell]
 ScriptName = Event\EventShell.py
@@ -554,7 +570,7 @@
 
 PacketCMD_18=0xA2
 PacketSubCMD_18=0x30
-PacketCallFunc_18=OnSetChatBubbleBox
+PacketCallFunc_18=
 
 PacketCMD_19=0xA1
 PacketSubCMD_19=0x08
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index 8414694..9789450 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -527,6 +527,7 @@
 Def_Effect_TiandaoQiyun = 273   #增加天道树气运;A值-增加值   效果值B: 领取是否默认直接使用
 Def_Effect_Face = 274   #头像物品;A值-头像ID
 Def_Effect_FacePic = 275   #头像框物品;A值-头像框ID
+Def_Effect_EmojiPack = 276   #表情包物品;A值-表情包ID
 #----以下未使用或代码依然存在的---
 Def_Effect_ItemGiveGongXun = 1920        #使用道具给予功勋
 Def_Effect_ItemGiveRuneJH = 1925       #使用道具给予符印精华
@@ -4016,6 +4017,10 @@
 
 #聊天气泡
 Def_PDict_ChatBubbleBoxState = "ChatBubbleBoxState_%s"  # 聊天气泡状态, 参数(key编号)
+Def_PDict_ChatBoxEndTime = "ChatBoxEndTime_%s"  # 到期时间戳,0为永久,参数(气泡ID)
+Def_PDict_ChatBoxStar = "ChatBoxStar_%s"  # 星级,参数(气泡ID)
+Def_PDict_EmojiPackState = "EmojiPackState_%s"  # 表情包状态,参数(key编号)
+Def_PDict_EmojiPackEndTime = "EmojiPackEndTime_%s"  # 到期时间戳,0为永久,参数(表情包ID)
 
 #冰晶矿脉
 Def_PDict_IceLodeStarAwardRecord = "IceLodeStarAwardRecord" #星级奖励领奖记录
@@ -5026,7 +5031,8 @@
 Def_CalcAttrFunc_GubaoResonance, # 古宝共鸣 62
 Def_CalcAttrFunc_Face, # 头像 63
 Def_CalcAttrFunc_FacePic, # 头像框 64
-) = range(65)
+Def_CalcAttrFunc_ChatBox, # 气泡框 65
+) = range(66)
 
 # 技能功能点列表  - 默认不算战力,不享受百分比加成,技能功能点暂时配置,之后优化技能属性逻辑后可去掉
 CalcAttrFuncSkillList = [Def_CalcAttrFunc_HorseSkill, Def_CalcAttrFunc_PetSkill, Def_CalcAttrFunc_DogzBattleSkill]
@@ -5071,6 +5077,7 @@
                             ShareDefine.Def_MFPType_FamilyZhenfa:[Def_CalcAttrFunc_FamilyZhenfa],
                             ShareDefine.Def_MFPType_Face:[Def_CalcAttrFunc_Face],
                             ShareDefine.Def_MFPType_FacePic:[Def_CalcAttrFunc_FacePic],
+                            ShareDefine.Def_MFPType_ChatBox:[Def_CalcAttrFunc_ChatBox],
                             ShareDefine.Def_MFPType_Other:[Def_CalcAttrFunc_Success, Def_CalcAttrFunc_FamilyTech, Def_CalcAttrFunc_EquipDecompose],
                             }
 
@@ -5082,7 +5089,8 @@
                ShareDefine.Def_MFPType_PetSoul:"宠魂", ShareDefine.Def_MFPType_HorseSoul:"骑魂", ShareDefine.Def_MFPType_FaQi:"法器", ShareDefine.Def_MFPType_Dogz:"神兽",
                ShareDefine.Def_MFPType_Coat:"时装", ShareDefine.Def_MFPType_Love:"情缘", ShareDefine.Def_MFPType_Charm:"魅力", ShareDefine.Def_MFPType_LianTi:"炼体",
                ShareDefine.Def_MFPType_Enchant:"附魔", ShareDefine.Def_MFPType_Gubao:"古宝", ShareDefine.Def_MFPType_Shentong:"神通", ShareDefine.Def_MFPType_FamilyZhenfa:"阵法", 
-               ShareDefine.Def_MFPType_GatherTheSoul:"聚魂新", ShareDefine.Def_MFPType_Face:"头像", ShareDefine.Def_MFPType_FacePic:"头像框", ShareDefine.Def_MFPType_Other:"其他",
+               ShareDefine.Def_MFPType_GatherTheSoul:"聚魂新", ShareDefine.Def_MFPType_Face:"头像", ShareDefine.Def_MFPType_FacePic:"头像框", ShareDefine.Def_MFPType_ChatBox:"气泡框", 
+               ShareDefine.Def_MFPType_Other:"其他",
                }
 
 FuncIndexName = {
@@ -5101,7 +5109,7 @@
                  Def_CalcAttrFunc_LoveRing:"情戒基础", Def_CalcAttrFunc_LoveRingCouple:"情戒仙侣", Def_CalcAttrFunc_Charm:"魅力", Def_CalcAttrFunc_LianTi:"炼体",
                  Def_CalcAttrFunc_Enchant:"附魔", Def_CalcAttrFunc_LingQiEnchant:"灵器附魔", Def_CalcAttrFunc_Gubao:"古宝", Def_CalcAttrFunc_Shentong:"神通",
                  Def_CalcAttrFunc_HorseStar:"坐骑星级", Def_CalcAttrFunc_PetStar:"宠物星级", Def_CalcAttrFunc_TitleStar:"称号星级", Def_CalcAttrFunc_FamilyZhenfa:"阵法",
-                 Def_CalcAttrFunc_GatherTheSoul:"聚魂新", Def_CalcAttrFunc_GubaoResonance:"古宝共鸣", Def_CalcAttrFunc_Face:"头像", Def_CalcAttrFunc_FacePic:"头像框",
+                 Def_CalcAttrFunc_GatherTheSoul:"聚魂新", Def_CalcAttrFunc_GubaoResonance:"古宝共鸣", Def_CalcAttrFunc_Face:"头像", Def_CalcAttrFunc_FacePic:"头像框", Def_CalcAttrFunc_ChatBox:"气泡框",
                  }
 #-------------------------------------------------------------------------------
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
index e5d9468..aee33cf 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
@@ -6240,6 +6240,58 @@
 
 
 #------------------------------------------------------
+# A2 36 聊天气泡框升星 #tagCMChatBubbleBoxStarUP
+
+class  tagCMChatBubbleBoxStarUP(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("BoxID", c_ubyte),    #气泡ID
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xA2
+        self.SubCmd = 0x36
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xA2
+        self.SubCmd = 0x36
+        self.BoxID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCMChatBubbleBoxStarUP)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// A2 36 聊天气泡框升星 //tagCMChatBubbleBoxStarUP:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                BoxID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.BoxID
+                                )
+        return DumpString
+
+
+m_NAtagCMChatBubbleBoxStarUP=tagCMChatBubbleBoxStarUP()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMChatBubbleBoxStarUP.Cmd,m_NAtagCMChatBubbleBoxStarUP.SubCmd))] = m_NAtagCMChatBubbleBoxStarUP
+
+
+#------------------------------------------------------
 # A2 10 清除副本CD#tagCMClearFBCD
 
 class  tagCMClearFBCD(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
index 52fd278..e617a17 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -30611,18 +30611,17 @@
 #------------------------------------------------------
 # A7 17 聊天气泡框状态 #tagMCChatBubbleBoxState
 
-class  tagMCChatBubbleBoxState(Structure):
+class  tagMCChatBubbleBox(Structure):
     _pack_ = 1
     _fields_ = [
-                  ("Cmd", c_ubyte),
-                  ("SubCmd", c_ubyte),
-                  ("BoxState", c_int),    # 按二进制位存储代表是否已开启,暂支持31位,以后有需要再加
+                  ("BoxID", c_ubyte),    #气泡ID
+                  ("State", c_ubyte),    #是否已激活
+                  ("EndTime", c_int),    #到期时间戳,0为永久
+                  ("Star", c_ubyte),    #星级
                   ]
 
     def __init__(self):
         self.Clear()
-        self.Cmd = 0xA7
-        self.SubCmd = 0x17
         return
 
     def ReadData(self, stringData, _pos=0, _len=0):
@@ -30631,33 +30630,98 @@
         return _pos + self.GetLength()
 
     def Clear(self):
-        self.Cmd = 0xA7
-        self.SubCmd = 0x17
-        self.BoxState = 0
+        self.BoxID = 0
+        self.State = 0
+        self.EndTime = 0
+        self.Star = 0
         return
 
     def GetLength(self):
-        return sizeof(tagMCChatBubbleBoxState)
+        return sizeof(tagMCChatBubbleBox)
 
     def GetBuffer(self):
         return string_at(addressof(self), self.GetLength())
 
     def OutputString(self):
         DumpString = '''// A7 17 聊天气泡框状态 //tagMCChatBubbleBoxState:
-                                Cmd:%s,
-                                SubCmd:%s,
-                                BoxState:%d
+                                BoxID:%d,
+                                State:%d,
+                                EndTime:%d,
+                                Star:%d
                                 '''\
                                 %(
-                                self.Cmd,
-                                self.SubCmd,
-                                self.BoxState
+                                self.BoxID,
+                                self.State,
+                                self.EndTime,
+                                self.Star
+                                )
+        return DumpString
+
+
+class  tagMCChatBubbleBoxState(Structure):
+    Head = tagHead()
+    Count = 0    #(BYTE Count)
+    BoxList = list()    #(vector<tagMCChatBubbleBox> BoxList)
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA7
+        self.Head.SubCmd = 0x17
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.Count):
+            temBoxList = tagMCChatBubbleBox()
+            _pos = temBoxList.ReadData(_lpData, _pos)
+            self.BoxList.append(temBoxList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA7
+        self.Head.SubCmd = 0x17
+        self.Count = 0
+        self.BoxList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        for i in range(self.Count):
+            length += self.BoxList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.Count)
+        for i in range(self.Count):
+            data = CommFunc.WriteString(data, self.BoxList[i].GetLength(), self.BoxList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                Count:%d,
+                                BoxList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.Count,
+                                "..."
                                 )
         return DumpString
 
 
 m_NAtagMCChatBubbleBoxState=tagMCChatBubbleBoxState()
-ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCChatBubbleBoxState.Cmd,m_NAtagMCChatBubbleBoxState.SubCmd))] = m_NAtagMCChatBubbleBoxState
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCChatBubbleBoxState.Head.Cmd,m_NAtagMCChatBubbleBoxState.Head.SubCmd))] = m_NAtagMCChatBubbleBoxState
 
 
 #------------------------------------------------------
@@ -30959,6 +31023,118 @@
 
 
 #------------------------------------------------------
+# A7 21 表情包信息 #tagMCEmojiPackInfo
+
+class  tagMCEmojiPack(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("PackID", c_ubyte),    #表情包ID
+                  ("State", c_ubyte),    #是否已激活
+                  ("EndTime", c_int),    #到期时间戳,0为永久
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.PackID = 0
+        self.State = 0
+        self.EndTime = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCEmojiPack)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// A7 21 表情包信息 //tagMCEmojiPackInfo:
+                                PackID:%d,
+                                State:%d,
+                                EndTime:%d
+                                '''\
+                                %(
+                                self.PackID,
+                                self.State,
+                                self.EndTime
+                                )
+        return DumpString
+
+
+class  tagMCEmojiPackInfo(Structure):
+    Head = tagHead()
+    Count = 0    #(BYTE Count)
+    EmojiPackList = list()    #(vector<tagMCEmojiPack> EmojiPackList)
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA7
+        self.Head.SubCmd = 0x21
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.Count):
+            temEmojiPackList = tagMCEmojiPack()
+            _pos = temEmojiPackList.ReadData(_lpData, _pos)
+            self.EmojiPackList.append(temEmojiPackList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA7
+        self.Head.SubCmd = 0x21
+        self.Count = 0
+        self.EmojiPackList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        for i in range(self.Count):
+            length += self.EmojiPackList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.Count)
+        for i in range(self.Count):
+            data = CommFunc.WriteString(data, self.EmojiPackList[i].GetLength(), self.EmojiPackList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                Count:%d,
+                                EmojiPackList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.Count,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagMCEmojiPackInfo=tagMCEmojiPackInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCEmojiPackInfo.Head.Cmd,m_NAtagMCEmojiPackInfo.Head.SubCmd))] = m_NAtagMCEmojiPackInfo
+
+
+#------------------------------------------------------
 # A7 15 通知仙盟抢Boss伤血信息 #tagMCFamilyBossHurtList
 
 class  tagMCFamilyBossHurt(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ChatBox.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ChatBox.py
new file mode 100644
index 0000000..fb48fe7
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ChatBox.py
@@ -0,0 +1,144 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GM.Commands.ChatBox
+#
+# @todo:聊天框
+# @author hxp
+# @date 2024-09-13
+# @version 1.0
+#
+# 详细描述: 聊天框
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2024-09-13 16:30"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import IpyGameDataPY
+import PlayerControl
+import PlayerChatBox
+import ChConfig
+
+#逻辑实现
+## GM命令执行入口
+#  @param curPlayer 当前玩家
+#  @param msgList 参数列表
+#  @return None
+#  @remarks 函数详细说明.
+def OnExec(curPlayer, msgList):
+    
+    if not msgList:
+        GameWorld.DebugAnswer(curPlayer, "---------- %s" % GameWorld.GetCurrentDataTimeStr())
+        GameWorld.DebugAnswer(curPlayer, "添加气泡框: ChatBox b1 气泡框ID [有效秒]")
+        GameWorld.DebugAnswer(curPlayer, "删除气泡框: ChatBox b0 [气泡框ID]")
+        GameWorld.DebugAnswer(curPlayer, "气泡框星级: ChatBox bs 气泡框ID 星级")
+        GameWorld.DebugAnswer(curPlayer, "添加表情包: ChatBox e1 表情包ID [有效秒]")
+        GameWorld.DebugAnswer(curPlayer, "删除表情包: ChatBox e0 [表情包ID]")
+        GameWorld.DebugAnswer(curPlayer, "有效秒:不填读表,0-永久,>0指定有效秒")
+        return
+    
+    syncBoxIDList = []
+    packIDList = []
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    value1 = msgList[0]
+    
+    # 删除气泡框
+    if value1 == "b0":
+        boxID = msgList[1] if len(msgList) > 1 else 0
+        if not boxID:
+            for index in range(ipyDataMgr.GetChatBubbleBoxCount()):
+                ipyData = ipyDataMgr.GetChatBubbleBoxByIndex(index)
+                boxID = ipyData.GetBoxID()
+                if ipyData.GetUnlockDefault():
+                    continue
+                if not GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID):
+                    continue
+                GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID, 0)
+                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ChatBoxEndTime % boxID, 0)
+                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ChatBoxStar % boxID, 0)
+                syncBoxIDList.append(boxID)
+        else:
+            ipyData = IpyGameDataPY.GetIpyGameData("ChatBubbleBox", boxID)
+            if not ipyData:
+                GameWorld.DebugAnswer(curPlayer, "该气泡框不存在:%s" % boxID)
+                return
+            if ipyData.GetUnlockDefault():
+                GameWorld.DebugAnswer(curPlayer, "该气泡框默认激活:%s" % boxID)
+                return
+            GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID, 0)
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ChatBoxEndTime % boxID, 0)
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ChatBoxStar % boxID, 0)
+            syncBoxIDList.append(boxID)
+        GameWorld.DebugAnswer(curPlayer, "删除气泡框:%s" % syncBoxIDList)
+        
+    # 添加气泡框
+    elif value1 == "b1":
+        boxID = msgList[1] if len(msgList) > 1 else 0
+        expireTimes = msgList[2] if len(msgList) > 2 else None
+        if PlayerChatBox.AddChatBox(curPlayer, boxID, expireTimes):
+            endTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ChatBoxEndTime % boxID)
+            endTimeStr = "永久" if not endTime else GameWorld.ChangeTimeNumToStr(endTime)
+            GameWorld.DebugAnswer(curPlayer, "添加气泡框成功:%s,到期:%s" % (boxID, endTimeStr))
+        else:
+            GameWorld.DebugAnswer(curPlayer, "添加气泡框失败:%s" % (boxID))
+            
+    # 气泡框星级
+    elif value1 == "bs":
+        boxID = msgList[1] if len(msgList) > 1 else 0
+        boxStar = msgList[2] if len(msgList) > 2 else 0
+        if boxStar:
+            starIpyData = IpyGameDataPY.GetIpyGameData("ChatBubbleBoxStar", boxID, boxStar)
+            if not starIpyData:
+                GameWorld.DebugAnswer(curPlayer, "气泡框星级不存在:%s,star:%s" % (boxID, boxStar))
+                return
+        GameWorld.DebugAnswer(curPlayer, "设置气泡框星级:%s,星:%s" % (boxID, boxStar))
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ChatBoxStar % boxID, boxStar)
+        syncBoxIDList.append(boxID)
+        
+    # 删除表情包
+    elif value1 == "e0":
+        packID = msgList[1] if len(msgList) > 1 else 0
+        if not packID:
+            for index in range(ipyDataMgr.GetEmojiPackCount()):
+                ipyData = ipyDataMgr.GetEmojiPackByIndex(index)
+                packID = ipyData.GetEmojiPackID()
+                if ipyData.GetUnlockDefault():
+                    continue
+                if not GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_EmojiPackState, packID):
+                    continue
+                GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_EmojiPackState, packID, 0)
+                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_EmojiPackEndTime % packID, 0)
+                packIDList.append(packID)
+        else:
+            ipyData = IpyGameDataPY.GetIpyGameData("EmojiPack", packID)
+            if not ipyData:
+                GameWorld.DebugAnswer(curPlayer, "该表情包不存在:%s" % packID)
+                return
+            if ipyData.GetUnlockDefault():
+                GameWorld.DebugAnswer(curPlayer, "该表情包默认激活:%s" % packID)
+                return
+            GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_EmojiPackState, packID, 0)
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_EmojiPackEndTime % packID, 0)
+            packIDList.append(packID)
+        GameWorld.DebugAnswer(curPlayer, "删除表情包:%s" % packIDList)
+        
+    # 添加表情包
+    elif value1 == "e1":
+        packID = msgList[1] if len(msgList) > 1 else 0
+        expireTimes = msgList[2] if len(msgList) > 2 else None
+        if PlayerChatBox.AddEmojiPack(curPlayer, packID, expireTimes):
+            endTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_EmojiPackEndTime % packID)
+            endTimeStr = "永久" if not endTime else GameWorld.ChangeTimeNumToStr(endTime)
+            GameWorld.DebugAnswer(curPlayer, "添加表情包成功:%s,到期:%s" % (packID, endTimeStr))
+        else:
+            GameWorld.DebugAnswer(curPlayer, "添加表情包失败:%s" % (packID))
+            
+    if syncBoxIDList:
+        PlayerChatBox.RefreshBoxAttr(curPlayer)
+        PlayerChatBox.SyncChatBoxInfo(curPlayer, syncBoxIDList)
+        
+    if packIDList:
+        PlayerChatBox.SyncEmojiPackInfo(curPlayer, packIDList)
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
index 1cb6c1f..af4cf0d 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -1757,6 +1757,25 @@
                         ("BYTE", "BoxID", 1),
                         ("WORD", "NeedLV", 0),
                         ("BYTE", "NeedVIPLVGift", 0),
+                        ("BYTE", "UnlockDefault", 0),
+                        ("DWORD", "ExpireMinutes", 0),
+                        ("list", "LightAttrType", 0),
+                        ("list", "LightAttrValue", 0),
+                        ("DWORD", "LightFightPower", 0),
+                        ),
+
+                "ChatBubbleBoxStar":(
+                        ("BYTE", "BoxID", 1),
+                        ("BYTE", "BoxStar", 1),
+                        ("list", "StarUpNeedItemList", 0),
+                        ("list", "StarAttrType", 0),
+                        ("list", "StarAttrValue", 0),
+                        ),
+
+                "EmojiPack":(
+                        ("DWORD", "EmojiPackID", 1),
+                        ("BYTE", "UnlockDefault", 0),
+                        ("DWORD", "ExpireMinutes", 0),
                         ),
 
                 "ActRechargePrize":(
@@ -2533,7 +2552,7 @@
         
     def GetFaceID(self): return self.attrTuple[0] # 头像ID DWORD
     def GetFaceStar(self): return self.attrTuple[1] # 头像星级 BYTE
-    def GetStarUpNeedItemList(self): return self.attrTuple[2] # 升下一星所需道具 [[物品ID,个数], ...] list
+    def GetStarUpNeedItemList(self): return self.attrTuple[2] # 升该星所需物品 [[物品ID,个数], ...] list
     def GetStarAttrType(self): return self.attrTuple[3] # 累计总属性类型 list
     def GetStarAttrValue(self): return self.attrTuple[4] # 累计总属性值 list
 
@@ -2560,7 +2579,7 @@
         
     def GetFacePicID(self): return self.attrTuple[0] # 头像框ID DWORD
     def GetFacePicStar(self): return self.attrTuple[1] # 头像框星级 BYTE
-    def GetStarUpNeedItemList(self): return self.attrTuple[2] # 升下一星所需道具 [[物品ID,个数], ...] list
+    def GetStarUpNeedItemList(self): return self.attrTuple[2] # 升该星所需物品 [[物品ID,个数], ...] list
     def GetStarAttrType(self): return self.attrTuple[3] # 累计总属性类型 list
     def GetStarAttrValue(self): return self.attrTuple[4] # 累计总属性值 list
 
@@ -5037,7 +5056,36 @@
         
     def GetBoxID(self): return self.attrTuple[0] # 气泡框ID BYTE
     def GetNeedLV(self): return self.attrTuple[1] # 所需等级 WORD
-    def GetNeedVIPLVGift(self): return self.attrTuple[2] # 所需购买VIP等级礼包 BYTE
+    def GetNeedVIPLVGift(self): return self.attrTuple[2] # 所需购买VIP等级礼包 BYTE
+    def GetUnlockDefault(self): return self.attrTuple[3] # 是否默认激活 BYTE
+    def GetExpireMinutes(self): return self.attrTuple[4] # 时效分钟,0永久 DWORD
+    def GetLightAttrType(self): return self.attrTuple[5] # 点亮属性类型 list
+    def GetLightAttrValue(self): return self.attrTuple[6] # 点亮属性值 list
+    def GetLightFightPower(self): return self.attrTuple[7] # 点亮附加战力 DWORD
+
+# 聊天气泡升星表
+class IPY_ChatBubbleBoxStar():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetBoxID(self): return self.attrTuple[0] # 气泡框ID BYTE
+    def GetBoxStar(self): return self.attrTuple[1] # 气泡框星级 BYTE
+    def GetStarUpNeedItemList(self): return self.attrTuple[2] # 升该星所需物品 [[物品ID,个数], ...] list
+    def GetStarAttrType(self): return self.attrTuple[3] # 累计总属性类型 list
+    def GetStarAttrValue(self): return self.attrTuple[4] # 累计总属性值 list
+
+# 表情包表
+class IPY_EmojiPack():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetEmojiPackID(self): return self.attrTuple[0] # 表情包ID DWORD
+    def GetUnlockDefault(self): return self.attrTuple[1] # 是否默认激活 BYTE
+    def GetExpireMinutes(self): return self.attrTuple[2] # 时效分钟,0永久 DWORD
 
 # 充值返利活动表
 class IPY_ActRechargePrize():
@@ -6422,6 +6470,8 @@
         self.__LoadFileData("WishingWell", onlyCheck)
         self.__LoadFileData("FunctionForecast", onlyCheck)
         self.__LoadFileData("ChatBubbleBox", onlyCheck)
+        self.__LoadFileData("ChatBubbleBoxStar", onlyCheck)
+        self.__LoadFileData("EmojiPack", onlyCheck)
         self.__LoadFileData("ActRechargePrize", onlyCheck)
         self.__LoadFileData("RechargePrizeTemplate", onlyCheck)
         self.__LoadFileData("ActTotalRecharge", onlyCheck)
@@ -7877,6 +7927,20 @@
         self.CheckLoadData("ChatBubbleBox")
         return self.ipyChatBubbleBoxCache[index]
 
+    def GetChatBubbleBoxStarCount(self):
+        self.CheckLoadData("ChatBubbleBoxStar")
+        return self.ipyChatBubbleBoxStarLen
+    def GetChatBubbleBoxStarByIndex(self, index):
+        self.CheckLoadData("ChatBubbleBoxStar")
+        return self.ipyChatBubbleBoxStarCache[index]
+
+    def GetEmojiPackCount(self):
+        self.CheckLoadData("EmojiPack")
+        return self.ipyEmojiPackLen
+    def GetEmojiPackByIndex(self, index):
+        self.CheckLoadData("EmojiPack")
+        return self.ipyEmojiPackCache[index]
+
     def GetActRechargePrizeCount(self):
         self.CheckLoadData("ActRechargePrize")
         return self.ipyActRechargePrizeLen
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChItem.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChItem.py
index 010b3e5..8db8eef 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChItem.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChItem.py
@@ -737,6 +737,7 @@
                             ChConfig.Def_Effect_AddKillBossCnt:"Item_AddKillBossCnt", # 增加BOSS可击杀次数
                             ChConfig.Def_Effect_AddMagicWeaponUpExp:"Item_AddMagicWeaponUpExp", # 增加法宝升星经验
                             ChConfig.Def_Effect_ChatBubbleBox:"Item_ChatBubbleBox", # 激活聊天气泡框
+                            ChConfig.Def_Effect_EmojiPack:"Item_EmojiPack",
                             ChConfig.Def_Effect_ItemGiveWeekPartyPoint:"Item_WeekPartyPoint", # 增加活动巡礼积分
                             ChConfig.Def_Effect_ItemGiveWeekPartyPoint1:"Item_WeekPartyPoint", # 增加活动巡礼积分
                             ChConfig.Def_Effect_AddRealmExpRate:"Item_AddRealmExpRate", # 增加聚灵效率
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_ChatBubbleBox.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_ChatBubbleBox.py
index 192a518..ff2f1b6 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_ChatBubbleBox.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_ChatBubbleBox.py
@@ -16,9 +16,8 @@
 #-------------------------------------------------------------------------------
 
 import ItemCommon
+import PlayerChatBox
 import ChConfig
-import ChPlayer
-
 
 def BatchUseItem(curPlayer, curRoleItem, tick, useCnt, exData):
     ##批量使用物品
@@ -27,7 +26,7 @@
     if not bubbleBoxID:
         return
     
-    if not ChPlayer.DoActivateChatBubbleBox(curPlayer, bubbleBoxID):
+    if not PlayerChatBox.AddChatBox(curPlayer, bubbleBoxID):
         return
     
     ItemCommon.DelItem(curPlayer, curRoleItem, useCnt, True, ChConfig.ItemDel_ChatBubbleBox)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_EmojiPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_EmojiPack.py
new file mode 100644
index 0000000..c1448a2
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/Item_EmojiPack.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package UseItem.Item_EmojiPack
+#
+# @todo:表情包
+# @author hxp
+# @date 2024-09-13
+# @version 1.0
+#
+# 详细描述: 表情包
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2024-09-13 16:30"""
+#-------------------------------------------------------------------------------
+
+import ItemCommon
+import PlayerChatBox
+
+def UseItem(curPlayer, curRoleItem, tick):
+    useItemEff = curRoleItem.GetEffectByIndex(0)
+    effectA1 = useItemEff.GetEffectValue(0) 
+    if effectA1 == 0:
+        return
+    if not PlayerChatBox.AddEmojiPack(curPlayer, effectA1):
+        return
+    ItemCommon.DelItem(curPlayer, curRoleItem, 1)
+    return True
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
index 146a263..bd3d975 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -162,6 +162,7 @@
 import PlayerChangeJob
 import PlayerGuaji
 import PlayerFace
+import PlayerChatBox
 import PlayerXiangong
 import PlayerMineArea
 import PlayerActLoginNew
@@ -929,8 +930,6 @@
     PlayerLuckyTreasure.OnLogin(curPlayer)
     # 小助手
     SyncLittleHelper(curPlayer)
-    # 聊天气泡框
-    SyncChatBubbleBoxState(curPlayer)
     # 副本助战
     FBHelpBattle.DoPlayerLogin(curPlayer)
     # 聚魂
@@ -994,6 +993,7 @@
         pass
     
     else:
+        PlayerChatBox.OnPlayerLogin(curPlayer)
         PlayerFace.OnPlayerLogin(curPlayer)
         PlayerXiangong.OnPlayerLogin(curPlayer)
         PlayerGubao.OnPlayerLogin(curPlayer)
@@ -3182,59 +3182,6 @@
         return
     
     curPlayer.SetSetting(setting)
-    return
-
-#// A2 30 设置聊天气泡框 #tagCMSetChatBubbleBox
-#
-#struct    tagCMSetChatBubbleBox
-#{
-#    tagHead         Head;
-#    BYTE        BubbleBoxType;    //气泡框类型
-#};
-def OnSetChatBubbleBox(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    useBoxID = clientData.BubbleBoxType
-    GameWorld.DebugLog("设置使用的聊天气泡框ID: useBoxID=%s" % useBoxID, curPlayer.GetPlayerID())
-    
-    ipyData = IpyGameDataPY.GetIpyGameData("ChatBubbleBox", useBoxID)
-    if not ipyData:
-        return
-    
-    curBoxState = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, useBoxID)
-    if not curBoxState:
-        needLV = ipyData.GetNeedLV()
-        if needLV and curPlayer.GetLV() < needLV:
-            GameWorld.DebugLog("    等级不足,无法使用该气泡框!needLV=%s" % needLV, curPlayer.GetPlayerID())
-            return
-        
-        needVIPLVGift = ipyData.GetNeedVIPLVGift() # 兼容已购买VIP礼包的老号用
-        if needVIPLVGift:
-            record = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_VipAwardRecord)
-            hasBuy = record & pow(2, needVIPLVGift)
-            if not hasBuy:
-                GameWorld.DebugLog("    未购买VIP礼包,无法使用该气泡框!needVIPLVGift=%s" % needVIPLVGift, curPlayer.GetPlayerID())
-                return
-            
-    PlayerControl.SetChatBubbleBox(curPlayer, useBoxID)
-    return
-
-def DoActivateChatBubbleBox(curPlayer, boxID):
-    ## 激活聊天气泡框
-    ipyData = IpyGameDataPY.GetIpyGameData("ChatBubbleBox", boxID)
-    if not ipyData:
-        return
-    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID, 1)
-    SyncChatBubbleBoxState(curPlayer)
-    return True
-
-def SyncChatBubbleBoxState(curPlayer):
-    ## 同步聊天气泡框状态
-    boxState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ChatBubbleBoxState % 0)
-    if not boxState:
-        return
-    boxStatePack = ChPyNetSendPack.tagMCChatBubbleBoxState()
-    boxStatePack.BoxState = boxState
-    NetPackCommon.SendFakePack(curPlayer, boxStatePack)
     return
 
 #// A2 29 设置小助手 #tagCMSetLittleHelper
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerChatBox.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerChatBox.py
new file mode 100644
index 0000000..eec6f21
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerChatBox.py
@@ -0,0 +1,404 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.PlayerChatBox
+#
+# @todo:聊天气泡表情
+# @author hxp
+# @date 2024-09-13
+# @version 1.0
+#
+# 详细描述: 聊天气泡表情
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2024-09-13 16:30"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import ShareDefine
+import PlayerControl
+import IPY_GameWorld
+import ChPyNetSendPack
+import IpyGameDataPY
+import NetPackCommon
+import ItemCommon
+import ChConfig
+
+import time
+
+def OnPlayerLogin(curPlayer):
+    SyncChatBoxInfo(curPlayer)
+    SyncEmojiPackInfo(curPlayer)
+    return
+
+def OnMinute(curPlayer):
+    curTime = int(time.time())
+    delBoxList = []
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetChatBubbleBoxCount()):
+        ipyData = ipyDataMgr.GetChatBubbleBoxByIndex(index)
+        boxID = ipyData.GetBoxID()
+        if ipyData.GetUnlockDefault():
+            # 默认激活的不处理
+            continue
+        if not GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID):
+            # 未激活的不处理
+            continue
+        endTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ChatBoxEndTime % boxID)
+        if not endTime or endTime > curTime:
+            # 永久或未过期
+            continue
+        if DelChatBox(curPlayer, boxID, False, "ChatBoxTimeout"):
+            delBoxList.append(boxID)
+            
+    if delBoxList:
+        RefreshBoxAttr(curPlayer)
+        
+    # 表情包
+    for index in range(ipyDataMgr.GetEmojiPackCount()):
+        ipyData = ipyDataMgr.GetEmojiPackByIndex(index)
+        packID = ipyData.GetEmojiPackID()
+        if ipyData.GetUnlockDefault():
+            # 默认激活的不处理
+            continue
+        if not GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_EmojiPackState, packID):
+            # 未激活的不处理
+            continue
+        endTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_EmojiPackEndTime % packID)
+        if not endTime or endTime > curTime:
+            # 永久或未过期
+            continue
+        DelEmojiPack(curPlayer, packID, "EmojiPackTimeout")
+        
+    return
+
+def AddChatBox(curPlayer, boxID, setExpireTimes=None):
+    if boxID <= 0:
+        return
+    playerID = curPlayer.GetPlayerID()
+    ipyData = IpyGameDataPY.GetIpyGameData("ChatBubbleBox", boxID)
+    if not ipyData:
+        return
+    if ipyData.GetUnlockDefault():
+        GameWorld.DebugLog("气泡框默认解锁的不用添加: boxID=%s" % (boxID), playerID)
+        return
+    ipyExpireSeconds = ipyData.GetExpireMinutes() * 60
+    
+    curTime = int(time.time())
+    state = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID)
+    endTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ChatBoxEndTime % boxID)
+    GameWorld.Log("添加气泡框: boxID=%s,setExpireTimes=%s,state=%s,endTime=%s,ipyExpireSeconds=%s,curTime=%s" 
+                  % (boxID, setExpireTimes, state, endTime, ipyExpireSeconds, curTime), playerID)
+    updEndTime = endTime
+    
+    # 指定时长的,如GM指定
+    if setExpireTimes >= 0:
+        updEndTime = curTime + setExpireTimes
+        GameWorld.Log("    指定气泡框时长: boxID=%s,updEndTime=%s" % (boxID, updEndTime), playerID)
+        
+    # 永久
+    elif ipyExpireSeconds == 0:
+        updEndTime = 0
+        GameWorld.Log("    添加永久气泡框: boxID=%s,updEndTime=%s" % (boxID, updEndTime), playerID)
+        
+    else:
+        # 未过期
+        if endTime > curTime:
+            updEndTime = endTime + ipyExpireSeconds
+            GameWorld.Log("    累加气泡框时长: boxID=%s,updEndTime=%s" % (boxID, updEndTime), playerID)
+        else:
+            updEndTime = curTime + ipyExpireSeconds
+            GameWorld.Log("    重新激活气泡框: boxID=%s,updEndTime=%s" % (boxID, updEndTime), playerID)
+            
+    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID, 1)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ChatBoxEndTime % boxID, updEndTime)
+    RefreshBoxAttr(curPlayer)
+    SyncChatBoxInfo(curPlayer, [boxID])
+    return True
+
+def DelChatBox(curPlayer, boxID, isRefreshAttr=True, notifyMail=""):
+    playerID = curPlayer.GetPlayerID()
+    ipyData = IpyGameDataPY.GetIpyGameData("ChatBubbleBox", boxID)
+    if not ipyData:
+        return
+    if ipyData.GetUnlockDefault():
+        return
+    GameWorld.Log("删除玩家聊天气泡框: boxID=%s,notifyMail=%s" % (boxID, notifyMail), playerID)
+    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID, 0)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ChatBoxEndTime % boxID, 0)
+    # 星级不重置,重新激活后再次生效
+    
+    if PlayerControl.GetChatBubbleBox(curPlayer) == boxID:
+        canUseID = GetCanUseBoxID(curPlayer)
+        PlayerControl.SetChatBubbleBox(curPlayer, canUseID)
+        GameWorld.DebugLog("玩家聊天气泡框被删除,重新设置可用ID=%s" % canUseID, playerID)
+        
+    if isRefreshAttr:
+        RefreshBoxAttr(curPlayer)
+    SyncChatBoxInfo(curPlayer, [boxID])
+    if notifyMail:
+        PlayerControl.SendMailByKey(notifyMail, [playerID], [], [boxID])
+    return True
+
+def GetCanUseBoxID(curPlayer):
+    ## 获取一个可用的气泡框
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetChatBubbleBoxCount())[::-1]:
+        ipyData = ipyDataMgr.GetChatBubbleBoxByIndex(index)
+        boxID = ipyData.GetBoxID()
+        if IsBoxCanUse(curPlayer, boxID, ipyData):
+            return boxID
+    return 0
+
+def IsBoxCanUse(curPlayer, boxID, ipyData=None):
+    ## 气泡框是否可用
+    state = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID)
+    if state:
+        return True
+    
+    if not ipyData:
+        ipyData = IpyGameDataPY.GetIpyGameData("ChatBubbleBox", boxID)
+    if ipyData:
+        if ipyData.GetUnlockDefault():
+            return True
+        
+        needLV = ipyData.GetNeedLV()
+        if needLV and curPlayer.GetLV() < needLV:
+            #GameWorld.DebugLog("    等级不足,无法使用该气泡框!needLV=%s" % needLV, curPlayer.GetPlayerID())
+            return False
+        
+        needVIPLVGift = ipyData.GetNeedVIPLVGift() # 兼容已购买VIP礼包的老号用
+        if needVIPLVGift:
+            record = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_VipAwardRecord)
+            hasBuy = record & pow(2, needVIPLVGift)
+            if not hasBuy:
+                #GameWorld.DebugLog("    未购买VIP礼包,无法使用该气泡框!needVIPLVGift=%s" % needVIPLVGift, curPlayer.GetPlayerID())
+                return False
+            
+        if needLV or needVIPLVGift:
+            return True
+        
+    return False
+
+def RefreshBoxAttr(curPlayer):
+    CalcBoxAttr(curPlayer)
+    PlayerControl.PlayerControl(curPlayer).RefreshPlayerAttrState()
+    return
+
+def CalcBoxAttr(curPlayer):
+    
+    fightPowerEx = 0
+    allAttrList = [{} for i in range(4)]
+    
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetChatBubbleBoxCount()):
+        ipyData = ipyDataMgr.GetChatBubbleBoxByIndex(index)
+        boxID = ipyData.GetBoxID()
+        if not IsBoxCanUse(curPlayer, boxID, ipyData):
+            continue
+        
+        fightPowerEx += ipyData.GetLightFightPower()
+        # 激活属性
+        lightAttrType = ipyData.GetLightAttrType()
+        lightAttrValue = ipyData.GetLightAttrValue()
+        for i, attrID in enumerate(lightAttrType):
+            attrValue = lightAttrValue[i]
+            PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrList)
+        
+        # 星级属性
+        boxStar = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ChatBoxStar % boxID)
+        starIpyData = IpyGameDataPY.GetIpyGameDataNotLog("ChatBubbleBoxStar", boxID, boxStar)
+        if starIpyData:
+            starAttrType = starIpyData.GetStarAttrType()
+            starAttrValue = starIpyData.GetStarAttrValue()
+            for i, attrID in enumerate(starAttrType):
+                attrValue = starAttrValue[i]
+                PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrList)
+                
+    # 保存计算值
+    PlayerControl.SetCalcAttrListValue(curPlayer, ChConfig.Def_CalcAttrFunc_ChatBox, allAttrList)
+    curPlayer.SetDict(ChConfig.Def_PlayerKey_MFPEx % ShareDefine.Def_MFPType_ChatBox, fightPowerEx)
+    return
+
+#// A2 30 设置聊天气泡框 #tagCMSetChatBubbleBox
+#
+#struct    tagCMSetChatBubbleBox
+#{
+#    tagHead         Head;
+#    BYTE        BubbleBoxType;    //气泡框类型
+#};
+def OnSetChatBubbleBox(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    useBoxID = clientData.BubbleBoxType
+    playerID = curPlayer.GetPlayerID()
+    if not IsBoxCanUse(curPlayer, useBoxID):
+        GameWorld.DebugLog("聊天气泡框不可用,无法设置! useBoxID=%s" % useBoxID, playerID)    
+        return
+    GameWorld.DebugLog("设置使用的聊天气泡框ID: useBoxID=%s" % useBoxID, playerID)    
+    PlayerControl.SetChatBubbleBox(curPlayer, useBoxID)
+    return
+
+#// A2 36 聊天气泡框升星 #tagCMChatBubbleBoxStarUP
+#
+#struct    tagCMChatBubbleBoxStarUP
+#{
+#    tagHead         Head;
+#    WORD        BoxID;    //气泡ID
+#};
+def OnChatBubbleBoxStarUP(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    playerID = curPlayer.GetPlayerID()
+    boxID = clientData.BoxID
+    
+    if not IsBoxCanUse(curPlayer, boxID):
+        GameWorld.DebugLog("聊天气泡框不可用! boxID=%s" % (boxID), playerID)
+        return
+    
+    curStar = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ChatBoxStar % boxID)        
+    nextStar = curStar + 1
+    nextIpyData = IpyGameDataPY.GetIpyGameData("ChatBubbleBoxStar", boxID, nextStar)
+    if not nextIpyData:
+        GameWorld.DebugLog("不存在该气泡框星级! boxID=%s,nextStar=%s" % (boxID, nextStar), playerID)
+        return
+    needItemList = nextIpyData.GetStarUpNeedItemList()
+    if not needItemList:
+        return
+    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
+    lackItemDict, delInfoDict = ItemCommon.GetCostItemIndexList(needItemList, itemPack)
+    if lackItemDict:
+        GameWorld.DebugLog("气泡框升星所需物品不足! boxID=%s,nextStar=%s,needItemList=%s,lackItemDict=%s" 
+                           % (boxID, nextStar, needItemList, lackItemDict), playerID)
+        return
+    ItemCommon.DelCostItem(curPlayer, itemPack, delInfoDict, "ChatBoxStarUP")
+    
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ChatBoxStar % boxID, nextStar)
+    
+    GameWorld.DebugLog("气泡框升星! boxID=%s,curStar=%s,nextStar=%s" % (boxID, curStar, nextStar), playerID)
+    
+    RefreshBoxAttr(curPlayer)
+    SyncChatBoxInfo(curPlayer, [boxID])
+    return
+
+def SyncChatBoxInfo(curPlayer, boxIDList=None):
+    if boxIDList == None:
+        syncIDList = []
+        ipyDataMgr = IpyGameDataPY.IPY_Data()
+        for index in range(ipyDataMgr.GetChatBubbleBoxCount()):
+            ipyData = ipyDataMgr.GetChatBubbleBoxByIndex(index)
+            syncIDList.append(ipyData.GetBoxID())
+    else:
+        syncIDList = boxIDList
+        
+    boxList = []
+    for boxID in syncIDList:
+        state = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_ChatBubbleBoxState, boxID)
+        if not state and boxIDList == None:
+            continue
+        box = ChPyNetSendPack.tagMCChatBubbleBox()
+        box.BoxID = boxID
+        box.State = state
+        box.EndTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ChatBoxEndTime % boxID)
+        box.Star = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ChatBoxStar % boxID)
+        boxList.append(box)
+        
+    if not boxList:
+        return
+    
+    clientPack = ChPyNetSendPack.tagMCChatBubbleBoxState()
+    clientPack.BoxList = boxList
+    clientPack.Count = len(clientPack.BoxList)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+##-------------------------------------------------------------------------------------------------
+
+def AddEmojiPack(curPlayer, packID, setExpireTimes=None):
+    if packID <= 0:
+        return
+    playerID = curPlayer.GetPlayerID()
+    ipyData = IpyGameDataPY.GetIpyGameData("EmojiPack", packID)
+    if not ipyData:
+        return
+    if ipyData.GetUnlockDefault():
+        GameWorld.DebugLog("表情包默认解锁的不用添加: packID=%s" % (packID), playerID)
+        return
+    ipyExpireSeconds = ipyData.GetExpireMinutes() * 60
+    
+    curTime = int(time.time())
+    state = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_EmojiPackState, packID)
+    endTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_EmojiPackEndTime % packID)
+    GameWorld.Log("添加表情包: packID=%s,setExpireTimes=%s,state=%s,endTime=%s,ipyExpireSeconds=%s,curTime=%s" 
+                  % (packID, setExpireTimes, state, endTime, ipyExpireSeconds, curTime), playerID)
+    updEndTime = endTime
+    
+    # 指定时长的,如GM指定
+    if setExpireTimes >= 0:
+        updEndTime = curTime + setExpireTimes
+        GameWorld.Log("    指定表情包时长: packID=%s,updEndTime=%s" % (packID, updEndTime), playerID)
+        
+    # 永久
+    elif ipyExpireSeconds == 0:
+        updEndTime = 0
+        GameWorld.Log("    添加永久表情包: packID=%s,updEndTime=%s" % (packID, updEndTime), playerID)
+        
+    else:
+        # 未过期
+        if endTime > curTime:
+            updEndTime = endTime + ipyExpireSeconds
+            GameWorld.Log("    累加表情包时长: packID=%s,updEndTime=%s" % (packID, updEndTime), playerID)
+        else:
+            updEndTime = curTime + ipyExpireSeconds
+            GameWorld.Log("    重新激活表情包: packID=%s,updEndTime=%s" % (packID, updEndTime), playerID)
+            
+    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_EmojiPackState, packID, 1)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_EmojiPackEndTime % packID, updEndTime)
+    
+    SyncEmojiPackInfo(curPlayer, [packID])
+    return True
+
+def DelEmojiPack(curPlayer, packID, notifyMail=""):
+    playerID = curPlayer.GetPlayerID()
+    ipyData = IpyGameDataPY.GetIpyGameData("EmojiPack", packID)
+    if not ipyData:
+        return
+    if ipyData.GetUnlockDefault():
+        return
+    GameWorld.Log("删除玩家聊天表情包: packID=%s,notifyMail=%s" % (packID, notifyMail), playerID)
+    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_EmojiPackState, packID, 0)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_EmojiPackEndTime % packID, 0)
+    
+    SyncEmojiPackInfo(curPlayer, [packID])
+    if notifyMail:
+        PlayerControl.SendMailByKey(notifyMail, [playerID], [], [packID])
+    return True
+
+def SyncEmojiPackInfo(curPlayer, packIDList=None):
+    if packIDList == None:
+        syncIDList = []
+        ipyDataMgr = IpyGameDataPY.IPY_Data()
+        for index in range(ipyDataMgr.GetEmojiPackCount()):
+            ipyData = ipyDataMgr.GetEmojiPackByIndex(index)
+            syncIDList.append(ipyData.GetEmojiPackID())
+    else:
+        syncIDList = packIDList
+        
+    packList = []
+    for packID in syncIDList:
+        state = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_EmojiPackState, packID)
+        if not state and packIDList == None:
+            continue
+        pack = ChPyNetSendPack.tagMCEmojiPack()
+        pack.PackID = packID
+        pack.State = state
+        pack.EndTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_EmojiPackEndTime % packID)
+        packList.append(pack)
+        
+    if not packList:
+        return
+    
+    clientPack = ChPyNetSendPack.tagMCEmojiPackInfo()
+    clientPack.EmojiPackList = packList
+    clientPack.Count = len(clientPack.EmojiPackList)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
index f9b400e..9a993dc 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
@@ -55,6 +55,7 @@
 import FormulaControl
 import PlayerGoldGift
 import PlayerFlashSale
+import PlayerChatBox
 import PlayerFace
 import PlayerWing
 import ChEquip
@@ -1303,6 +1304,8 @@
     PlayerDienstgrad.CheckDienstgradTimeout(curPlayer)
     # 时效头像
     PlayerFace.OnMinute(curPlayer)
+    # 聊天气泡
+    PlayerChatBox.OnMinute(curPlayer)
     return
 
 # 一定时间自动释放的被动技能
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
index 5e43d27..3de6385 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -1785,7 +1785,7 @@
 )=range(5)
 
 # 战斗力模块类型
-Def_MFPType_Max = 36
+Def_MFPType_Max = 37
 ModuleFightPowerTypeList = (
 Def_MFPType_Role, # 角色 0
 Def_MFPType_Equip, # 装备(装备本身) 1
@@ -1822,6 +1822,7 @@
 Def_MFPType_GatherTheSoul, # 聚魂新 32
 Def_MFPType_Face, # 头像 33
 Def_MFPType_FacePic, # 头像框 34
+Def_MFPType_ChatBox, # 气泡框 35
 Def_MFPType_Other, # 其他
 ) = range(Def_MFPType_Max)
 

--
Gitblit v1.8.0