a166d721a3d7ff6cd2339983214e718c7a6a0ffe..96c0ba31f74064e59c746921fcd1386cddf78d42
2025-06-12 hxp
121 【武将】武将系统-服务端(武将锁定、解锁;)
96c0ba 对比 | 目录
2025-06-12 hxp
121 【武将】武将系统-服务端(图鉴;)
edc391 对比 | 目录
10个文件已修改
516 ■■■■ 已修改文件
PySysDB/PySysDBPY.h 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Hero.py 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PySysDB/PySysDBPY.h
@@ -30,12 +30,15 @@
struct    Hero
{
    DWORD        _HeroID;    //英雄ID
    WORD        HeroIndex;    // 记录索引
    BYTE        Country;    // 国家
    BYTE        Quality;    // 品质
    list        SkinNPCIDList;    // 皮肤NPCID列表
    DWORD        AtkSkillID;    //普攻技能ID
    DWORD        AngerSkillID;    //怒气技能ID
    WORD        AtkInheritPer;    //攻击继承
    WORD        DefInheritPer;    //防御继承
    WORD        HPInheritPer;    //生命继承
    dict        BatAttrDict;    //其他战斗属性字典 {"属性ID":值, ...}
};
//武将星级天赋表
@@ -71,6 +74,15 @@
    BYTE        AddStarUpper;    // 增加星级上限
};
//武将羁绊表
struct    HeroFetter
{
    WORD        _FetterID;    //羁绊ID
    list        HeroIDList;    // 武将ID组合列表
    list        AttrIDList;    // 属性ID列表
    list        AttrValueList;    // 属性值列表
};
//武将皮肤表
struct    HeroSkin
{
@@ -88,10 +100,14 @@
    list        UPCostItem;    // 升级消耗道具
    list        InitTalentWeight;    // 初始天赋数权重
    BYTE        InitStarUpper;    // 初始星级上限
    DWORD        InitAddPer;    // 初始加成万分率
    DWORD        LVAddPer;    // 每等级加成
    DWORD        BreakLVAddPer;    // 每突破等级加成
    DWORD        StarAddPer;    // 每星级加成
    DWORD        InitAddPer;    // 上阵初始加成万分率
    DWORD        LVAddPer;    // 上阵每等级加成
    DWORD        BreakLVAddPer;    // 上阵每突破等级加成
    DWORD        StarAddPer;    // 上阵每星级加成
    list        BookActAwardMoney;    // 图鉴激活奖励货币 类型|值
    DWORD        BookInitAddPer;    // 图鉴初始加成
    DWORD        BookStarAddPer;    // 图鉴每星级加成
    DWORD        BookBreakLVAddPer;    // 图鉴每突破等级加成
};
//武将品质突破表
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -1910,7 +1910,7 @@
Writer = hxp
Releaser = hxp
RegType = 0
RegisterPackCount = 8
RegisterPackCount = 10
PacketCMD_1=0xB2
PacketSubCMD_1=0x30
@@ -1940,6 +1940,14 @@
PacketSubCMD_7=0x36
PacketCallFunc_7=OnHeroWearSkin
PacketCMD_8=0xB4
PacketSubCMD_8=0x12
PacketCallFunc_8=OnHeroBattlePosSave
PacketCMD_8=0xB2
PacketSubCMD_8=0x37
PacketCallFunc_8=OnHeroBookUP
PacketCMD_9=0xB2
PacketSubCMD_9=0x38
PacketCallFunc_9=OnHeroLock
PacketCMD_10=0xB4
PacketSubCMD_10=0x12
PacketCallFunc_10=OnHeroBattlePosSave
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -4420,9 +4420,8 @@
Def_PDict_TiandaoAward = "TiandaoAward_%s" # 天道树领奖记录,按奖励索引记录是否已领取,参数(key编号)
#武将
Def_PDict_HeroActState = "HeroActState_%s" # 武将解锁状态,按记录索引二进制存储,参数(key编号)
Def_PDict_HeroSkin = "HeroSkin_%s" # 武将皮肤解锁状态,按皮肤索引二进制存储,参数(武将ID)
Def_PDict_HeroBook = "HeroBook_%s" # 武将图鉴激活等级,参数(武将ID) cccbbba a-初始激活状态1-英雄激活,2-初始图鉴激活; bbb-存星级图鉴激活等级;ccc-存突破图鉴激活等级
#-------------------------------------------------------------------------------
#可以从07 41封包购买的背包类型,和对应字典{背包类型:[字典key, 默认格子数]}
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
@@ -17372,6 +17372,66 @@
#------------------------------------------------------
# B2 37 武将图鉴激活升级 #tagCSHeroBookUP
class  tagCSHeroBookUP(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("HeroID", c_int),    #武将ID
                  ("ItemIndex", c_ushort),    #关联武将物品所在武将背包索引,激活时可不用发
                  ("BookType", c_ubyte),    #图鉴激活类型: 0-初始激活;1-星级升级;2-突破等级升级
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xB2
        self.SubCmd = 0x37
        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 = 0xB2
        self.SubCmd = 0x37
        self.HeroID = 0
        self.ItemIndex = 0
        self.BookType = 0
        return
    def GetLength(self):
        return sizeof(tagCSHeroBookUP)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// B2 37 武将图鉴激活升级 //tagCSHeroBookUP:
                                Cmd:%s,
                                SubCmd:%s,
                                HeroID:%d,
                                ItemIndex:%d,
                                BookType:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.HeroID,
                                self.ItemIndex,
                                self.BookType
                                )
        return DumpString
m_NAtagCSHeroBookUP=tagCSHeroBookUP()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCSHeroBookUP.Cmd,m_NAtagCSHeroBookUP.SubCmd))] = m_NAtagCSHeroBookUP
#------------------------------------------------------
# B2 32 武将突破 #tagCSHeroBreak
class  tagCSHeroBreak(Structure):
@@ -17424,6 +17484,62 @@
#------------------------------------------------------
# B2 38 武将锁定 #tagCSHeroLock
class  tagCSHeroLock(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("ItemIndex", c_ushort),    #武将物品所在武将背包位置索引
                  ("IsLock", c_ubyte),    #0-解锁;1-锁定
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xB2
        self.SubCmd = 0x38
        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 = 0xB2
        self.SubCmd = 0x38
        self.ItemIndex = 0
        self.IsLock = 0
        return
    def GetLength(self):
        return sizeof(tagCSHeroLock)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// B2 38 武将锁定 //tagCSHeroLock:
                                Cmd:%s,
                                SubCmd:%s,
                                ItemIndex:%d,
                                IsLock:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.ItemIndex,
                                self.IsLock
                                )
        return DumpString
m_NAtagCSHeroLock=tagCSHeroLock()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCSHeroLock.Cmd,m_NAtagCSHeroLock.SubCmd))] = m_NAtagCSHeroLock
#------------------------------------------------------
# B2 30 武将升级 #tagCSHeroLVUP
class  tagCSHeroLVUP(Structure):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -44100,8 +44100,10 @@
    _pack_ = 1
    _fields_ = [
                  ("HeroID", c_int),    # 武将ID
                  ("IsActive", c_ubyte),    # 是否已激活
                  ("SkinState", c_int),    # 武将皮肤已解锁状态信息,按皮肤所在索引二进制位运算判断是否解锁,0索引位默认皮肤,不用验证
                  ("BookInitState", c_ubyte),    # 图鉴激活状态:0-未激活;1-可激活;2-已激活
                  ("BookStarLV", c_ushort),    # 图鉴星级等级
                  ("BookBreakLV", c_ushort),    # 图鉴突破等级
                  ]
    def __init__(self):
@@ -44115,8 +44117,10 @@
    def Clear(self):
        self.HeroID = 0
        self.IsActive = 0
        self.SkinState = 0
        self.BookInitState = 0
        self.BookStarLV = 0
        self.BookBreakLV = 0
        return
    def GetLength(self):
@@ -44128,13 +44132,17 @@
    def OutputString(self):
        DumpString = '''// B1 22 武将信息 //tagSCHeroInfo:
                                HeroID:%d,
                                IsActive:%d,
                                SkinState:%d
                                SkinState:%d,
                                BookInitState:%d,
                                BookStarLV:%d,
                                BookBreakLV:%d
                                '''\
                                %(
                                self.HeroID,
                                self.IsActive,
                                self.SkinState
                                self.SkinState,
                                self.BookInitState,
                                self.BookStarLV,
                                self.BookBreakLV
                                )
        return DumpString
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Hero.py
@@ -18,6 +18,7 @@
import PlayerHero
import ShareDefine
import IpyGameDataPY
import PlayerControl
import GameWorld
import ChConfig
@@ -34,7 +35,7 @@
        GameWorld.DebugAnswer(curPlayer, "武将升星: Hero s 背包位置 升x星")
        GameWorld.DebugAnswer(curPlayer, "武将突破: Hero b 背包位置 设置等级")
        GameWorld.DebugAnswer(curPlayer, "武将觉醒: Hero a 背包位置 设置等级")
        GameWorld.DebugAnswer(curPlayer, "武将图鉴: Hero t 武将ID 是否激活")
        GameWorld.DebugAnswer(curPlayer, "武将图鉴: Hero t 武将ID 图鉴星级 图鉴突破等级")
        GameWorld.DebugAnswer(curPlayer, "重置图鉴: Hero t 0")
        GameWorld.DebugAnswer(curPlayer, "武将皮肤: Hero sk 武将ID 皮肤索引 是否解锁")
        GameWorld.DebugAnswer(curPlayer, "清空武将: ClearPack 35")
@@ -46,7 +47,6 @@
    
    # 图鉴
    if value == "t":
        isAct = 0
        if not value2:
            heroIDList = []
            ipyDataMgr = IpyGameDataPY.IPY_Data()
@@ -54,25 +54,28 @@
                ipyData = ipyDataMgr.GetHeroByIndex(index)
                heroIDList.append(ipyData.GetHeroID())
        else:
            isAct = msgList[2] if len(msgList) > 2 else 0
            heroIDList = [value2]
        syncHeroIDList = []
        for heroID in heroIDList:
            heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
            if not heroIpyData:
                continue
            heroIndex = heroIpyData.GetHeroIndex()
            if GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HeroActState, heroIndex) == isAct:
                continue
            GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_HeroActState, heroIndex, isAct)
            syncHeroIDList.append(heroID)
            
        PlayerHero.Sync_HeroInfo(curPlayer, syncHeroIDList)
        if not value2:
        if value2 == 0:
            syncHeroIDList = []
            for heroID in heroIDList:
                if not curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID):
                    continue
                syncHeroIDList.append(heroID)
                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, 0)
            GameWorld.DebugAnswer(curPlayer, "重置图鉴OK!")
        else:
            GameWorld.DebugAnswer(curPlayer, "设置武将(%s),激活(%s)" % (value2, isAct))
            heroID = value2
            syncHeroIDList = [heroID]
            bookStarLV = msgList[2] if len(msgList) > 2 else 0
            bookBreakLV = msgList[3] if len(msgList) > 3 else 0
            if (bookStarLV or bookStarLV) and not PlayerHero.GetHeroBookInitState(curPlayer, heroID):
                PlayerHero.SetHeroBookInitState(curPlayer, heroID, 1)
            PlayerHero.SetHeroBookStarLV(curPlayer, heroID, bookStarLV)
            PlayerHero.SetHeroBookBreakLV(curPlayer, heroID, bookBreakLV)
            GameWorld.DebugAnswer(curPlayer, "设置武将(%s)图鉴星级(%s),突破(%s)" % (heroID, bookStarLV, bookBreakLV))
        PlayerHero.Sync_HeroInfo(curPlayer, syncHeroIDList)
        return
    
    # 皮肤
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
@@ -2103,6 +2103,32 @@
    numValue += pow(10, dataIndex)*(dataValue - lastTagLV)
    return numValue
def GetValue(dataValue, fromRight, bits):
    '''获取某个数值中,从右往左数第x位开始,截图n位得到的数据
    @param dataValue: 原始数值
    @param fromRight: 从右往左数第x位开始,从1开始
    @param bits: 截取n位
    @return: 数值value
    '''
    lPow = pow(10, fromRight)
    rPow = pow(10, fromRight - bits)
    curValue = dataValue % lPow / rPow
    return curValue
def SetValue(dataValue, fromRight, bits, updValue):
    '''修改某个数值,从右往左数第x位开始,截图n位得到的数据,替换为具体数值
    @param dataValue: 原始数值
    @param fromRight: 从右往左数第x位开始,从1开始
    @param bits: 截取n位
    @param updValue: 替换值
    @return: 修改后的value值
    '''
    lPow = pow(10, fromRight)
    rPow = pow(10, fromRight - bits)
    leftValue = dataValue / lPow * lPow
    rightValue = dataValue % rPow
    return leftValue + updValue * rPow + rightValue
def GetBitValue(dataValue, index):
    """ 得到某个字节值中某一位(Bit)的值
    @param dataValue: 待取值的字节值
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -58,12 +58,15 @@
                "Hero":(
                        ("DWORD", "HeroID", 1),
                        ("WORD", "HeroIndex", 0),
                        ("BYTE", "Country", 0),
                        ("BYTE", "Quality", 0),
                        ("list", "SkinNPCIDList", 0),
                        ("DWORD", "AtkSkillID", 0),
                        ("DWORD", "AngerSkillID", 0),
                        ("WORD", "AtkInheritPer", 0),
                        ("WORD", "DefInheritPer", 0),
                        ("WORD", "HPInheritPer", 0),
                        ("dict", "BatAttrDict", 0),
                        ),
                "HeroTalent":(
@@ -93,6 +96,13 @@
                        ("BYTE", "AddStarUpper", 0),
                        ),
                "HeroFetter":(
                        ("WORD", "FetterID", 1),
                        ("list", "HeroIDList", 0),
                        ("list", "AttrIDList", 0),
                        ("list", "AttrValueList", 0),
                        ),
                "HeroSkin":(
                        ("DWORD", "SkinNPCID", 1),
                        ("list", "WearAttrIDList", 0),
@@ -110,6 +120,10 @@
                        ("DWORD", "LVAddPer", 0),
                        ("DWORD", "BreakLVAddPer", 0),
                        ("DWORD", "StarAddPer", 0),
                        ("list", "BookActAwardMoney", 0),
                        ("DWORD", "BookInitAddPer", 0),
                        ("DWORD", "BookStarAddPer", 0),
                        ("DWORD", "BookBreakLVAddPer", 0),
                        ),
                "HeroQualityBreak":(
@@ -2767,12 +2781,15 @@
        return
        
    def GetHeroID(self): return self.attrTuple[0] # 英雄ID DWORD
    def GetHeroIndex(self): return self.attrTuple[1] #  记录索引 WORD
    def GetCountry(self): return self.attrTuple[2] #  国家 BYTE
    def GetQuality(self): return self.attrTuple[3] #  品质 BYTE
    def GetSkinNPCIDList(self): return self.attrTuple[4] #  皮肤NPCID列表 list
    def GetAtkSkillID(self): return self.attrTuple[5] # 普攻技能ID DWORD
    def GetAngerSkillID(self): return self.attrTuple[6] # 怒气技能ID DWORD
    def GetCountry(self): return self.attrTuple[1] #  国家 BYTE
    def GetQuality(self): return self.attrTuple[2] #  品质 BYTE
    def GetSkinNPCIDList(self): return self.attrTuple[3] #  皮肤NPCID列表 list
    def GetAtkSkillID(self): return self.attrTuple[4] # 普攻技能ID DWORD
    def GetAngerSkillID(self): return self.attrTuple[5] # 怒气技能ID DWORD
    def GetAtkInheritPer(self): return self.attrTuple[6] # 攻击继承 WORD
    def GetDefInheritPer(self): return self.attrTuple[7] # 防御继承 WORD
    def GetHPInheritPer(self): return self.attrTuple[8] # 生命继承 WORD
    def GetBatAttrDict(self): return self.attrTuple[9] # 其他战斗属性字典 {"属性ID":值, ...} dict
# 武将星级天赋表
class IPY_HeroTalent():
@@ -2816,6 +2833,18 @@
    def GetUnlockTalentSlot(self): return self.attrTuple[5] #  解锁第x槽位 BYTE
    def GetAddStarUpper(self): return self.attrTuple[6] #  增加星级上限 BYTE
# 武将羁绊表
class IPY_HeroFetter():
    def __init__(self):
        self.attrTuple = None
        return
    def GetFetterID(self): return self.attrTuple[0] # 羁绊ID WORD
    def GetHeroIDList(self): return self.attrTuple[1] #  武将ID组合列表 list
    def GetAttrIDList(self): return self.attrTuple[2] #  属性ID列表 list
    def GetAttrValueList(self): return self.attrTuple[3] #  属性值列表 list
# 武将皮肤表
class IPY_HeroSkin():
    
@@ -2840,10 +2869,14 @@
    def GetUPCostItem(self): return self.attrTuple[1] #  升级消耗道具 list
    def GetInitTalentWeight(self): return self.attrTuple[2] #  初始天赋数权重 list
    def GetInitStarUpper(self): return self.attrTuple[3] #  初始星级上限 BYTE
    def GetInitAddPer(self): return self.attrTuple[4] #  初始加成万分率 DWORD
    def GetLVAddPer(self): return self.attrTuple[5] #  每等级加成 DWORD
    def GetBreakLVAddPer(self): return self.attrTuple[6] #  每突破等级加成 DWORD
    def GetStarAddPer(self): return self.attrTuple[7] #  每星级加成 DWORD
    def GetInitAddPer(self): return self.attrTuple[4] #  上阵初始加成万分率 DWORD
    def GetLVAddPer(self): return self.attrTuple[5] #  上阵每等级加成 DWORD
    def GetBreakLVAddPer(self): return self.attrTuple[6] #  上阵每突破等级加成 DWORD
    def GetStarAddPer(self): return self.attrTuple[7] #  上阵每星级加成 DWORD
    def GetBookActAwardMoney(self): return self.attrTuple[8] #  图鉴激活奖励货币 类型|值 list
    def GetBookInitAddPer(self): return self.attrTuple[9] #  图鉴初始加成 DWORD
    def GetBookStarAddPer(self): return self.attrTuple[10] #  图鉴每星级加成 DWORD
    def GetBookBreakLVAddPer(self): return self.attrTuple[11] #  图鉴每突破等级加成 DWORD
# 武将品质突破表
class IPY_HeroQualityBreak():
@@ -6881,6 +6914,7 @@
        self.__LoadFileData("HeroTalent", onlyCheck)
        self.__LoadFileData("HeroBreak", onlyCheck)
        self.__LoadFileData("HeroAwake", onlyCheck)
        self.__LoadFileData("HeroFetter", onlyCheck)
        self.__LoadFileData("HeroSkin", onlyCheck)
        self.__LoadFileData("HeroQuality", onlyCheck)
        self.__LoadFileData("HeroQualityBreak", onlyCheck)
@@ -7416,6 +7450,13 @@
        self.CheckLoadData("HeroAwake")
        return self.ipyHeroAwakeCache[index]
    def GetHeroFetterCount(self):
        self.CheckLoadData("HeroFetter")
        return self.ipyHeroFetterLen
    def GetHeroFetterByIndex(self, index):
        self.CheckLoadData("HeroFetter")
        return self.ipyHeroFetterCache[index]
    def GetHeroSkinCount(self):
        self.CheckLoadData("HeroSkin")
        return self.ipyHeroSkinLen
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py
@@ -1661,11 +1661,11 @@
    if endIndex == 0:
        endIndex = curPack.GetCount() - 1
    #检查是否能整理
    for i in range(0, curPack.GetCount()):
        curItem = curPack.GetAt(i)
        if curItem.GetIsLocked():
            #GameWorld.Log("物品有锁, 不允许重整")
            return
    #for i in range(0, curPack.GetCount()):
    #    curItem = curPack.GetAt(i)
    #    if curItem.GetIsLocked():
    #        #GameWorld.Log("物品有锁, 不允许重整")
    #        return
    
    # 整理方式不同区分
    if packIndex == ShareDefine.rptHero:
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py
@@ -110,12 +110,75 @@
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    heroIndex = heroIpyData.GetHeroIndex()
    heroActState = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HeroActState, heroIndex)
    if not heroActState:
        GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_HeroActState, heroIndex, 1)
        GameWorld.DebugLog("首次激活武将: heroID=%s,heroIndex=%s" % (heroID, heroIndex), curPlayer.GetPlayerID())
    if not GetHeroActivite(curPlayer, heroID):
        SetHeroActivite(curPlayer, heroID, 1)
        GameWorld.DebugLog("首次激活武将: heroID=%s" % (heroID), curPlayer.GetPlayerID())
        #首次获得图鉴额外逻辑 ...
        Sync_HeroInfo(curPlayer, [heroID])
    return
def GetHeroActivite(curPlayer, heroID):
    ## 武将状态
    # @return: 0-未激活;1-武将已获得,可激活;2-图鉴已激活
    actState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID) % 10
    return actState
def SetHeroActivite(curPlayer, heroID, isAct=1):
    ## 设置武将已获得,可激活状态
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    actState = bookState % 10
    if isAct:
        if actState:
            return
        actState = 1
    else:
        actState = 0
    updBookState = GameWorld.SetValue(bookState, 1, 1, actState)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, updBookState)
    GameWorld.DebugLog("设置武将激活状态:%s,bookState=%s,updBookState=%s" % (isAct, bookState, updBookState), curPlayer.GetPlayerID())
    return
def GetHeroBookInitState(curPlayer, heroID):
    ## 武将图鉴激活状态
    initState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID) % 10
    return initState > 1
def SetHeroBookInitState(curPlayer, heroID, isAct=1):
    ## 设置武将图鉴激活状态
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    actState = bookState % 10
    if isAct:
        actState = 2
    else:
        actState = 1 if actState else 0
    updBookState = GameWorld.SetValue(bookState, 1, 1, actState)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, updBookState)
    GameWorld.DebugLog("设置武将图鉴激活状态:%s,bookState=%s,updBookState=%s" % (isAct, bookState, updBookState), curPlayer.GetPlayerID())
    return
def GetHeroBookStarLV(curPlayer, heroID):
    ## 武将图鉴星级等级
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    return GameWorld.GetValue(bookState, 4, 3)
def SetHeroBookStarLV(curPlayer, heroID, starLV):
    ## 设置武将图鉴星级等级,支持三位数 0~999 级
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    updBookState = GameWorld.SetValue(bookState, 4, 3, starLV)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, updBookState)
    GameWorld.DebugLog("设置武将图鉴星级等级:%s,bookState=%s,updBookState=%s" % (starLV, bookState, updBookState), curPlayer.GetPlayerID())
    return
def GetHeroBookBreakLV(curPlayer, heroID):
    ## 武将图鉴突破等级
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    return GameWorld.GetValue(bookState, 7, 3)
def SetHeroBookBreakLV(curPlayer, heroID, breakLV):
    ## 设置武将图鉴突破等级,支持三位数 0~999 级
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    updBookState = GameWorld.SetValue(bookState, 7, 3, breakLV)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, updBookState)
    GameWorld.DebugLog("设置武将图鉴突破等级:%s,bookState=%s,updBookState=%s" % (breakLV, bookState, updBookState), curPlayer.GetPlayerID())
    return
def GetHeroItem(curPlayer, itemIndex):
@@ -217,6 +280,9 @@
                       % (itemIndex, heroID, useItemIndex, useHeroID), playerID)
    if heroID != useHeroID:
        GameWorld.DebugLog("武将材料非本体,无法升星!", playerID)
        return
    if useItem.GetIsLocked():
        GameWorld.DebugLog("材料卡锁定中,无法升星! useItemIndex=%s,heroID=%s" % (useItemIndex, heroID), playerID)
        return
    washIDCnt = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentWashID)
    if washIDCnt:
@@ -811,6 +877,119 @@
    Sync_HeroInfo(curPlayer, [heroID])
    return
#// B2 37 武将图鉴激活升级 #tagCSHeroBookUP
#
#struct    tagCSHeroBookUP
#{
#    tagHead        Head;
#    DWORD        HeroID;        //武将ID
#    WORD        ItemIndex;    //关联武将物品所在武将背包索引,激活时可不用发
#    BYTE        BookType;    //图鉴激活类型: 0-初始激活;1-星级升级;2-突破等级升级
#};
def OnHeroBookUP(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    heroID = clientData.HeroID
    itemIndex = clientData.ItemIndex
    bookType = clientData.BookType
    if bookType == 1:
        __doHeroBookStarLVUP(curPlayer, heroID, itemIndex)
    elif bookType == 2:
        __doHeroBookBreakLVUP(curPlayer, heroID, itemIndex)
    else:
        __doHeroBookAct(curPlayer, heroID)
    return
def __doHeroBookAct(curPlayer, heroID):
    ## 图鉴激活
    playerID = curPlayer.GetPlayerID()
    if GetHeroBookInitState(curPlayer, heroID):
        GameWorld.DebugLog("该武将图鉴已激活! heroID=%s" % heroID, playerID)
        return
    GameWorld.DebugLog("武将图鉴激活! heroID=%s" % heroID, playerID)
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    quality = heroIpyData.GetQuality()
    qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
    if not qualityIpyData:
        return
    if GetHeroActivite(curPlayer, heroID) != 1:
        GameWorld.DebugLog("武将未获得过,不可激活图鉴! heroID=%s" % (heroID), playerID)
        return
    SetHeroBookInitState(curPlayer, heroID, 1)
    awardMoneyInfo = qualityIpyData.GetBookActAwardMoney()
    if awardMoneyInfo and len(awardMoneyInfo) == 2:
        moneyType, moneyValue = awardMoneyInfo
        if moneyType and moneyValue:
            PlayerControl.GiveMoney(curPlayer, moneyType, moneyValue, "HeroBookAct")
    Sync_HeroInfo(curPlayer, [heroID])
    return
def __doHeroBookStarLVUP(curPlayer, heroID, itemIndex):
    ## 图鉴星级升级
    playerID = curPlayer.GetPlayerID()
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
    if heroItem.GetItemTypeID() != heroID:
        GameWorld.DebugLog("非该武将图鉴关联物品! heroID=%s,itemID=%s" % (heroID, heroItem.GetItemTypeID()), playerID)
        return
    heroStar = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
    bookStar = GetHeroBookStarLV(curPlayer, heroID)
    if bookStar >= heroStar:
        GameWorld.DebugLog("该武将图鉴星级已达当前英雄星级! heroID=%s,bookStar=%s,heroStar=%s" % (heroID, bookStar, heroStar), playerID)
        return
    GameWorld.DebugLog("武将图鉴星级升级! heroID=%s,bookStar=%s,heroStar=%s" % (heroID, bookStar, heroStar), playerID)
    SetHeroBookStarLV(curPlayer, heroID, bookStar + 1)
    Sync_HeroInfo(curPlayer, [heroID])
    # 刷属性
    return
def __doHeroBookBreakLVUP(curPlayer, heroID, itemIndex):
    ## 图鉴突破升级
    playerID = curPlayer.GetPlayerID()
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
    if heroItem.GetItemTypeID() != heroID:
        GameWorld.DebugLog("非该武将图鉴关联物品! heroID=%s,itemID=%s" % (heroID, heroItem.GetItemTypeID()), playerID)
        return
    heroBreakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    bookBreakLV = GetHeroBookBreakLV(curPlayer, heroID)
    if bookBreakLV >= heroBreakLV:
        GameWorld.DebugLog("该武将图鉴突破等级已达当前英雄突破等级! heroID=%s,bookBreakLV=%s,heroBreakLV=%s" % (heroID, bookBreakLV, heroBreakLV), playerID)
        return
    GameWorld.DebugLog("武将图鉴突破升级! heroID=%s,bookBreakLV=%s,heroBreakLV=%s" % (heroID, bookBreakLV, heroBreakLV), playerID)
    SetHeroBookBreakLV(curPlayer, heroID, bookBreakLV + 1)
    Sync_HeroInfo(curPlayer, [heroID])
    # 刷属性
    return
#// B2 38 武将锁定 #tagCSHeroLock
#
#struct    tagCSHeroLock
#{
#    tagHead        Head;
#    WORD        ItemIndex;    //武将物品所在武将背包位置索引
#    BYTE        IsLock;        //0-解锁;1-锁定
#};
def OnHeroLock(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    itemIndex = clientData.ItemIndex
    isLock = clientData.IsLock
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
    heroItem.SetIsLocked(1 if isLock else 0)
    return
#// B4 12 战斗阵型保存 #tagCSHeroBattlePosSave
#
#struct    tagCSHeroBattlePos
@@ -837,7 +1016,7 @@
        posNum = posInfo.PosNum
        itemIndex = posInfo.ItemIndex
        if itemIndex in indexList:
            # 单英雄只能一个位置,一个位置只能对应唯一英雄单位
            # 单武将只能一个位置,一个位置只能对应唯一武将单位
            continue
        indexList.append(itemIndex)
        heroPosDict[posNum] = itemIndex
@@ -902,15 +1081,15 @@
        heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
        if not heroIpyData:
            continue
        heroIndex = heroIpyData.GetHeroIndex()
        heroActState = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HeroActState, heroIndex)
        if not heroActState and heroIDList == None:
        if heroIDList == None and not curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID):
            continue
        
        hero = ChPyNetSendPack.tagSCHero()
        hero.HeroID = heroID
        hero.IsActive = heroActState
        hero.SkinState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkin % heroID)
        hero.BookInitState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID) % 10
        hero.BookStarLV = GetHeroBookStarLV(curPlayer, heroID)
        hero.BookBreakLV = GetHeroBookBreakLV(curPlayer, heroID)
        syncInfoList.append(hero)
        
    if not syncInfoList: