From 26958aff1b844a743a805b4f9075bee800b72a46 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期二, 04 十一月 2025 14:35:37 +0800
Subject: [PATCH] 332 【主界面】座骑系统-服务端

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py | 1425 +++++++++++++++++-----------------------------------------
 1 files changed, 425 insertions(+), 1,000 deletions(-)

diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
index 213b448..424652b 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
@@ -6,1065 +6,490 @@
 #
 # @todo:坐骑
 # @author hxp
-# @date 2019-12-17
+# @date 2025-11-04
 # @version 1.0
 #
 # 详细描述: 坐骑
 #
 #-------------------------------------------------------------------------------
-#"""Version = 2019-12-17 18:30"""
+#"""Version = 2025-11-04 14:30"""
 #-------------------------------------------------------------------------------
 
+import ChConfig
+import ItemCommon
+import ShareDefine
+import NetPackCommon
+import PlayerControl
 import ChPyNetSendPack
 import IPY_GameWorld
-import GameWorld
-import PlayerControl
-import ChConfig
-import OperControlManager
-import ItemCommon
-import ChEquip
-import ShareDefine
-import DataRecordPack
-import ItemControler
-import NetPackCommon
-import PlayerBillboard
-import GameFuncComm
 import IpyGameDataPY
-import CrossPlayerData
-import PlayerActLunhuidian
-import PlayerActTask
-import PlayerPet
+import GameFuncComm
+import PlayerOnline
+import GameWorld
+import ObjPool
 
 import time
 
-
-Def_HorseEquipIndex = 5
-
-
-#//03 09 玩家骑马#tagCRideHorse
-#
-#struct    tagCRideHorse
-#{
-#    tagHead        Head;
-#    BYTE        Ride;        //1: 骑马 0: 下马
-#};
-def OnRideHorse(index, tick):
-    
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
-        return False
-    
-    cdTime = ChConfig.TYPE_Player_Tick_Time[ChConfig.TYPE_Player_Tick_HorseChangState]
-    # 判断时间是否超过间隔
-    if tick - curPlayer.GetTickByType(ChConfig.TYPE_Player_Tick_HorseChangState) <= cdTime:
-        #没有到刷新间隔
-        return
-    curPlayer.SetTickByType(ChConfig.TYPE_Player_Tick_HorseChangState, tick)
-    
-    pack = IPY_GameWorld.IPY_CRideHorse()
-    #//1: 骑马 0: 下马
-    curPlayerRideType = pack.GetRide()
-    if PlayerControl.GetCrossMapID(curPlayer):
-        # 暂只处理上马
-        if curPlayerRideType:
-            dataList = [curPlayerRideType]
-            CrossPlayerData.SendDataToCrossServer(curPlayer, CrossPlayerData.CrossData_RideHorse, dataList)
-        return
-    #骑马行为状态, 客户端限制
-    if not OperControlManager.IsObjCanDoAction(curPlayer,
-                                               ChConfig.Def_Obj_ActState_ClientAct,
-                                               IPY_GameWorld.oalRide):
-        return
-    
-    #下马
-    if curPlayerRideType == 0 :
-        PlayerRideHorseDown(curPlayer, True)
-        
-    #骑马   
-    elif curPlayerRideType == 1:
-        PlayerRideHorseUp(curPlayer, True)
-        
-    return
-
-def CrossServer_RideHorse(curPlayer, dataList):
-    curPlayerRideType = dataList[0]
-    
-    #骑马
-    if curPlayerRideType == 1:
-        PlayerRideHorseUp(curPlayer, True)
-            
-    return
-
-def PlayerRideHorseDown(curPlayer, refreshState=True) :
-    ## 玩家下马
-    if curPlayer.GetPlayerVehicle() != IPY_GameWorld.pvHorse:
-        return
-    
-    curPlayer.SetPlayerVehicle(IPY_GameWorld.pvNull)
-    curPlayer.Sync_GetoffHorse()
-    
-    playerControl = PlayerControl.PlayerControl(curPlayer)
-    playerControl.RefreshPlayerAttrByBuff()
-    return
-
-def PlayerRideHorseUp(curPlayer, refreshState=True, isNotify=True) :
-    ## 玩家上马 
-    
-    # 未加载成功骑乘会导致模型加载失败报错
-    if not curPlayer.GetInitOK():
-        return
-    
-    #地图不允许骑马 RideLimit_lhs_0
-    if not GameWorld.GetMap().GetMapCanRide():
-        PlayerControl.NotifyCode(curPlayer, "RideLimit_lhs_0")
-        return
-        
-    #检查玩家状态,只有在空闲状态才能上马
-    if curPlayer.GetPlayerVehicle() != IPY_GameWorld.pvNull :
-        #GameWorld.Log("已经有交通工具,无法上马")
-        return
-    
-    #检查是否装备指定ID物品
-    playerEquip = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptEquip)
-    itemRideHorse = playerEquip.GetAt(Def_HorseEquipIndex)
-    #无指定道具
-    if itemRideHorse.IsEmpty():
-        return
-    
-    #设置玩家为骑马状态
-    curPlayer.SetPlayerVehicle(IPY_GameWorld.pvHorse)
-    #设置马匹为普通移动状态
-    curPlayer.SetPlayerRidehorseState(IPY_GameWorld.prsNormal)
-    
-    if isNotify:
-        curPlayer.Sync_RideHorse()
-        
-    if refreshState:
-        playerControl = PlayerControl.PlayerControl(curPlayer)
-        playerControl.RefreshPlayerAttrByBuff()
-    return True
-
-#=====================================================================
-
-#//A5 01 坐骑激活 #tagPlayerActivateHorse
-#
-#struct    tagPlayerActivateHorse
-#{
-#    tagHead        Head;
-#    DWORD        HorseID;        //坐骑幻化ID
-#};
-def ActivateHorseSkinItem(index, curPackData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    playerID = curPlayer.GetPlayerID()
-    activateID = curPackData.HorseID
-    
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
-        return
-    
-    ipyData = IpyGameDataPY.GetIpyGameData("HorseSkinPlus", activateID)
-    if not ipyData:
-        return
-    
-    updSkinEndTime = 0
-    validTime = ipyData.GetSkinValidTime()
-    if validTime:
-        curTime = int(time.time())
-        skinEndTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % activateID)
-        if curTime >= skinEndTime:
-            updSkinEndTime = curTime + validTime
-        else:
-            updSkinEndTime = skinEndTime + validTime
-    else:
-        if GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState, activateID):
-            GameWorld.DebugLog("该坐骑已幻化! activateID=%s" % activateID)
-            return
-        
-    needItemID = ipyData.GetUnlockItemID()
-    needItemCnt = ipyData.GetUnlockItemCnt()
-    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
-    hasEnough, itemList = ItemCommon.GetItem_FromPack_ByID(needItemID, itemPack, needItemCnt)
-    if not hasEnough:
-        GameWorld.DebugLog("坐骑幻化道具不足! needItemID=%s,needItemCnt=%s" % (needItemID, needItemCnt))
-        return
-    ItemCommon.ReduceItem(curPlayer, itemPack, itemList, needItemCnt, False, ChConfig.ItemDel_Horse)
-    
-    if validTime:
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinEndTime % activateID, updSkinEndTime)
-        GameWorld.DebugLog("坐骑幻化时效 activateID=%s,updSkinEndTime=%s(%s)" % (activateID, updSkinEndTime, GameWorld.ChangeTimeNumToStr(updSkinEndTime)), playerID)
-        
-    #时效及非时效该状态均设置激活
-    activateState, updActivateState = GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState, activateID, 1)
-    
-    playerEquip = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptEquip)
-    itemRideHorse = playerEquip.GetAt(Def_HorseEquipIndex)
-    #无指定道具
-    if itemRideHorse.IsEmpty():
-        DoChangeHorse(curPlayer, 2, activateID, tick)
-        
-    Sync_HorseClassData(curPlayer)
-    if validTime:
-        SyncHorseSkinTimeInfo(curPlayer, activateID)
-    GameWorld.Log("坐骑激活成功!activateID=%s,activateState=%s,updActivateState=%s,updSkinEndTime=%s" 
-                  % (activateID, activateState, updActivateState, updSkinEndTime), playerID)
-    
-    # 刷属性,更新排行榜
-    RefreshHorseAttr(curPlayer)
-    PlayerControl.WorldNotify(0, "GetMount", [curPlayer.GetName(), ipyData.GetHorseSkinPlusID()])
-    return
-
-
-#//A5 02 坐骑选择 #tagPlayerChooseHorse
-#
-#struct    tagPlayerChooseHorse
-#{
-#    tagHead        Head;
-#    BYTE        ChooseType;    // 1-按等阶,2-按幻化
-#    BYTE        LVID;        // 阶等级或幻化ID
-#};
-def OnPlayerChangeHorse(index, packData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
-        return
-    
-    #检查换装间隔
-    if tick - curPlayer.GetLastChangeEquipTick() <= ChConfig.Def_MinChangeEquipTime:
-        return
-    curPlayer.SetLastChangeEquipTick(tick)
-    
-    chooseType = packData.ChooseType
-    lvID = packData.LVID
-    
-    if not DoChangeHorse(curPlayer, chooseType, lvID, tick):
-        return
-    
-    if not GameWorld.IsCrossServer():
-        dataList = [chooseType, lvID]
-        CrossPlayerData.SendDataToCrossServer(curPlayer, CrossPlayerData.CrossData_HorseChange, dataList)
-        
-    return
-
-def CrossServer_ChangeHorse(curPlayer, dataList):
-    ## 跨服处理 坐骑变更
-    chooseType, lvID = dataList
-    tick = GameWorld.GetGameWorld().GetTick()
-    DoChangeHorse(curPlayer, chooseType, lvID, tick)
-    return
-
-def DoChangeHorse(curPlayer, chooseType, lvID, tick):
-    ''' 坐骑选择
-    @param chooseType: 1-按阶,2-按幻化
-    @param lvID:  阶等级或幻化ID
-    '''
-    
-    # 按阶
-    if chooseType == 1:
-        horseLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserLV)
-        if horseLV < lvID:
-            GameWorld.DebugLog("坐骑等级不足,无法使用该坐骑皮肤!  horseLV=%s,lvID=%s" % (horseLV, lvID))
-            return
-        
-        horseIpyData = IpyGameDataPY.GetIpyGameData("HorseLVUp", lvID)
-        if not horseIpyData:
-            return
-        horseItemID = horseIpyData.GetHorseSkinID()
-        
-    # 按幻化
-    elif chooseType == 2:
-        skinPlusIpyData = IpyGameDataPY.GetIpyGameData("HorseSkinPlus", lvID)
-        if not skinPlusIpyData:
-            return
-        if not CheckHorseSkinState(curPlayer, skinPlusIpyData):
-            return
-        horseItemID = skinPlusIpyData.GetHorseSkinPlusID()
-        
-    else:
-        return
-    
-    if not horseItemID:
-        GameWorld.DebugLog("找不到选择的坐骑ID! chooseType=%s,lvID=%s" % (chooseType, lvID))
-        return
-    
-    if ItemCommon.FindItemInPackByItemID(curPlayer, horseItemID, IPY_GameWorld.rptEquip):
-        GameWorld.DebugLog("已经选择了该坐骑,无需重新选择! horseItemID=%s" % horseItemID)
-        return
-    
-    isOK = ItemControler.PutItemInTempSwap(curPlayer, horseItemID)
-    if not isOK:
-        return
-    
-    curHorse = ItemCommon.FindItemInPackByItemID(curPlayer, horseItemID, ShareDefine.rptTempSwap)
-    if not curHorse:
-        return
-    isRideHorse = curPlayer.GetPlayerVehicle() == IPY_GameWorld.pvHorse
-    
-    # 如果骑乘状态,先下马,换装,再上马
-    if isRideHorse:
-        PlayerRideHorseDown(curPlayer, False)
-    
-    #---执行玩家换装逻辑---
-    #if ChEquip.DoPlayerEquipItem(curPlayer, curHorse, ItemCommon.GetEquipPackIndex(curHorse), tick):
-    #    if isRideHorse:
-    #        PlayerRideHorseUp(curPlayer, False)
-            
-    return True
-
-def CheckHorseSkinState(curPlayer, skinPlusIpyData):
-    skinID = skinPlusIpyData.GetID()
-    if skinPlusIpyData.GetSkinValidTime():
-        skinEndTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % skinID)
-        if not skinEndTime:
-            return False
-        curTime = int(time.time())
-        if curTime >= skinEndTime:
-            playerID = curPlayer.GetPlayerID()
-            GameWorld.Log("坐骑幻化已过期! skinID=%s,skinEndTime=%s(%s)" % (skinID, skinEndTime, GameWorld.ChangeTimeNumToStr(skinEndTime)), playerID)
-            # 发送过期通知邮件,重置为0
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinEndTime % skinID, 0)
-            
-            skinItemID = skinPlusIpyData.GetUnlockItemID()
-            addItemList = []
-            paramList = [skinItemID]
-            PlayerControl.SendMailByKey("HorseSkinInvalidNotify", [playerID], addItemList, paramList)
-            
-            activateState, updActivateState = GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState, skinID, 0)
-            GameWorld.Log("坐骑幻化过期更新状态! skinID=%s,activateState=%s,updActivateState=%s" % (skinID, activateState, updActivateState), playerID)
-            Sync_HorseClassData(curPlayer)
-            return False
-    else:
-        if not GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState, skinID):
-            #GameWorld.DebugLog("坐骑幻化未激活! skinID=%s,activateState=%s" % (skinID, activateState), curPlayer.GetPlayerID())
-            return False
-    return True
+HorseLVStart = 1
 
 def DoHorseOpen(curPlayer):
-    ## 马匹功能开启
-    horseLV = 1
-    ipyData = IpyGameDataPY.GetIpyGameData("HorseLVUp", horseLV)
-    if not ipyData:
-        return
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseClassLV, 0)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseLV, HorseLVStart)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseExp, 0)
     
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserLV, horseLV)
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserEatItemCount, 0)
-    for trainType in xrange(1, GetHorseTrainTypes() + 1):
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserTrainLV % trainType, 1)
-        
-    ipyDataMgr = IpyGameDataPY.IPY_Data()
-    for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
-        skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
-        skinID = skinPlusIpyData.GetID()
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinEndTime % skinID, 0)
-        GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState, skinID, 0)
-        
-    horseID = ipyData.GetHorseSkinID()
-    if not ItemCommon.FindItemInPackByItemID(curPlayer, horseID, IPY_GameWorld.rptEquip):
-        isOK = ItemControler.PutItemInTempSwap(curPlayer, horseID)
-        if not isOK:
-            return
-        
-        curHorse = ItemCommon.FindItemInPackByItemID(curPlayer, horseID, ShareDefine.rptTempSwap)
-        if not curHorse:
-            return
-        #---执行玩家换装逻辑---
-        #tick = GameWorld.GetGameWorld().GetTick()
-        #ChEquip.DoPlayerEquipItem(curPlayer, curHorse, Def_HorseEquipIndex, tick)
-        
-    GameWorld.DebugLog("坐骑功能开启! horseLV=%s,horseID=%s" % (horseLV, horseID))
-    Sync_HorseClassData(curPlayer)
+    skinID = GetDefaultHorseSkinID()
+    PlayerControl.SetHorseSkinID(curPlayer, skinID)
+    
+    GameWorld.DebugLog("坐骑功能开启! skinID=%s" % (skinID))
+    SyncHorseClassInfo(curPlayer)
     RefreshHorseAttr(curPlayer)
     return True
 
-def RefreshHorseAttr(curPlayer, isUpdateBill=True):
-    # 坐骑功能属性变更刷新
-    CalcHorseAttrEx(curPlayer)
-    # 需要更新坐骑榜、强制刷新属性
-    PlayerControl.PlayerControl(curPlayer).RefreshPlayerAttrState(billboardFunc=PlayerBillboard.UpdateHorseBillboard if isUpdateBill else None)
-    return
-
-def CalcHorseAttrEx(curPlayer):
-    ## 计算马匹属性
-    return
-#    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
-#        return
-#        
-#    allAttrList = [{} for _ in range(4)]
-#    allAttrListHorseSoul = [{} for _ in range(4)]
-#    allAttrListSkin = [{} for _ in range(4)]
-#    allAttrListTrain = [{} for _ in range(4)]
-#    allAttrListStar = [{} for _ in range(4)]
-#    
-#    customAttrDictHorse = {}
-#    customAttrDictSkin = {}
-#    
-#    horseSpeed = 0 # 坐骑功能增加的速度值,骑乘时才有效果
-#    horseLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserLV)
-#    # 等阶培养属性
-#    #totalItemCount = 0
-#    horseBaseAttrInfo = {}
-#    ipyDataMgr = IpyGameDataPY.IPY_Data()
-#    for index in xrange(ipyDataMgr.GetHorseLVUpCount()):
-#        horseIpyData = ipyDataMgr.GetHorseLVUpByIndex(index)
-#        dataHorseLV = horseIpyData.GetHorseLV()
-#        if dataHorseLV > horseLV:
-#            break
-#        elif dataHorseLV == horseLV:
-#            upItemCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserEatItemCount)
-#        else:
-#            upItemCount = horseIpyData.GetNeedEatCount()
-#                
-#        # 等阶额外属性
-#        lvAttrTypeList = horseIpyData.GetLVAttrType()
-#        lvAttrValueList = horseIpyData.GetLVAttrValue()
-#        for i, attrID in enumerate(lvAttrTypeList):
-#            attrValue = lvAttrValueList[i]
-#            if attrID == ShareDefine.Def_Effect_Speed:
-#                horseSpeed += attrValue
-#                continue
-#            PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrList)
-#            horseBaseAttrInfo[attrID] = horseBaseAttrInfo.get(attrID, 0) + attrValue
-#            
-#        # 培养丹增加属性
-#        upItemPerCount = horseIpyData.GetUpEatItemPerCount()
-#        if upItemCount and upItemPerCount:
-#            upItemAttrTypeList = horseIpyData.GetUpItemAttrType()
-#            upItemAttrValueList = horseIpyData.GetUpItemAttrValue()
-#            attrMultiple = upItemCount / upItemPerCount
-#            for i, attrID in enumerate(upItemAttrTypeList):
-#                attrValue = upItemAttrValueList[i]
-#                PlayerControl.CalcAttrDict_Type(attrID, attrValue * attrMultiple, allAttrList)
-#                horseBaseAttrInfo[attrID] = horseBaseAttrInfo.get(attrID, 0) + attrValue * attrMultiple
-#                
-#    customAttrDictHorse["horseBaseAttrInfo"] = horseBaseAttrInfo
-#    
-##    # 等阶培养丹累加个数属性
-##    eatItemAttrInfo = IpyGameDataPY.GetFuncCfg("HorseUpItem", 3)
-##    for attrID, attrValue in eatItemAttrInfo:
-##        PlayerControl.CalcAttrDict_Type(attrID, attrValue * totalItemCount, allAttrList)
-#        
-#    # 幻化属性
-#    initFPAdd = 0 #初始战力
-#    horseSkinQualityAttrInfo = {}
-#    for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
-#        skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
-#        if not CheckHorseSkinState(curPlayer, skinPlusIpyData):
-#            continue
-#        initFPAdd += skinPlusIpyData.GetInitFightPower()
-#        attrTypeList = skinPlusIpyData.GetAttrType()
-#        attrValueList = skinPlusIpyData.GetAttrValue()
-#        for i, attrID in enumerate(attrTypeList):
-#            attrValue = attrValueList[i]
-#            if attrID == ShareDefine.Def_Effect_Speed:
-#                horseSpeed += attrValue
-#                continue
-#            PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrListSkin)
-#            
-#        #觉醒战力
-#        skintype = 1
-#        horseID = skinPlusIpyData.GetHorseID()
-#        skinData = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorsePetSkinData % (skintype, horseID))
-#        skinIpyData = IpyGameDataPY.GetIpyGameDataNotLog('HorsePetSkin', skintype, horseID, skinData / 100)
-#        if skinIpyData:
-#            for attrID, attrValue in skinIpyData.GetAttrInfo().items():
-#                PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrListSkin)
-#                
-#        hIpyData = IpyGameDataPY.GetIpyGameData("Horse", horseID)
-#        if hIpyData:
-#            quality = hIpyData.GetQuality()
-#            if quality not in horseSkinQualityAttrInfo:
-#                horseSkinQualityAttrInfo[quality] = {}
-#            qualityAttrDict = horseSkinQualityAttrInfo[quality]
-#            for i, attrID in enumerate(attrTypeList):
-#                attrValue = attrValueList[i]
-#                qualityAttrDict[attrID] = qualityAttrDict.get(attrID, 0) + attrValue
-#                
-#        #星级
-#        horseStar = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserStar % horseID)
-#        starIpyData = IpyGameDataPY.GetIpyGameDataNotLog("HorseStarUp", horseID, horseStar)
-#        if starIpyData:
-#            starAttrType = starIpyData.GetStarAttrType()
-#            starAttrValue = starIpyData.GetStarAttrValue()
-#            for i, attrID in enumerate(starAttrType):
-#                attrValue = starAttrValue[i]
-#                if attrID == ShareDefine.Def_Effect_Speed:
-#                    horseSpeed += attrValue
-#                    continue
-#                PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrListStar)
-#        
-#    customAttrDictSkin["horseSkinQualityAttrInfo"] = horseSkinQualityAttrInfo
-#    
-#    # 新培养属性
-#    for index in xrange(ipyDataMgr.GetHorseTrainCount()):
-#        trainIpyData = ipyDataMgr.GetHorseTrainByIndex(index)
-#        trainType = trainIpyData.GetTrainType()
-#        dataTrainLV = trainIpyData.GetTrainLV()
-#        trainLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserTrainLV % trainType)
-#        
-#        if dataTrainLV > trainLV:
-#            continue
-#        elif dataTrainLV == trainLV:
-#            trainItemCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserTrainItemCount % trainType)
-#        else:
-#            trainItemCount = trainIpyData.GetEatCntTotal()
-#            
-#        # 等阶额外属性
-#        lvAttrTypeList = trainIpyData.GetLVAttrTypeList()
-#        lvAttrValueList = trainIpyData.GetLVAttrValueList()
-#        for i, attrID in enumerate(lvAttrTypeList):
-#            attrValue = lvAttrValueList[i]
-#            if attrID == ShareDefine.Def_Effect_Speed:
-#                horseSpeed += attrValue
-#                continue
-#            PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrListTrain)
-#            
-#        # 培养丹增加属性
-#        eatCntEverytime = trainIpyData.GetEatCntEverytime()
-#        if trainItemCount and eatCntEverytime:
-#            eatItemAttrTypeList = trainIpyData.GetEatItemAttrTypeList()
-#            eatItemAttrValueList = trainIpyData.GetEatItemAttrValueList()
-#            attrMultiple = trainItemCount / eatCntEverytime
-#            for i, attrID in enumerate(eatItemAttrTypeList):
-#                attrValue = eatItemAttrValueList[i]
-#                PlayerControl.CalcAttrDict_Type(attrID, attrValue * attrMultiple, allAttrListTrain)
-#                
-#    PlayerControl.SetMFPExFightPower(curPlayer, ShareDefine.Def_MFPType_Horse, initFPAdd)
-#    
-#    #GameWorld.DebugLog("坐骑功能属性: horseLV=%s,horseSpeed=%s,totalItemCount=%s,initFPAdd=%s" % (horseLV, horseSpeed, totalItemCount, initFPAdd))
-#    
-#    # 先缓存值,骑乘状态时进行附加
-#    curPlayer.SetDict(ChConfig.Def_PlayerKey_SpeedHorse, horseSpeed)
-#    
-#    # 果实对坐骑的加成, 魂石果实算附加属性层,单独计算
-#    fightPowerEx = PlayerAttrFruit.CalcAttrFruitAddAtrr(curPlayer, allAttrListHorseSoul, ShareDefine.Def_AttrFruitFunc_Horse)
-#    PlayerControl.SetMFPExFightPower(curPlayer, ShareDefine.Def_MFPType_HorseSoul, fightPowerEx)
-#    
-#    # 保存计算值
-#    PlayerControl.SetCalcAttrListValue(curPlayer, ChConfig.Def_CalcAttrFunc_Horse, allAttrList, customAttrDict=customAttrDictHorse)
-#    PlayerControl.SetCalcAttrListValue(curPlayer, ChConfig.Def_CalcAttrFunc_HorseSoul, allAttrListHorseSoul)
-#    PlayerControl.SetCalcAttrListValue(curPlayer, ChConfig.Def_CalcAttrFunc_HorseSkin, allAttrListSkin, customAttrDict=customAttrDictSkin)
-#    PlayerControl.SetCalcAttrListValue(curPlayer, ChConfig.Def_CalcAttrFunc_HorseTarin, allAttrListTrain)
-#    PlayerControl.SetCalcAttrListValue(curPlayer, ChConfig.Def_CalcAttrFunc_HorseStar, allAttrListStar)
-#    return
-
-def GetHorseSkinActCount(curPlayer):
-    horseSkinActCount = 0
-    ipyDataMgr = IpyGameDataPY.IPY_Data()
-    for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
-        skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
-        if not CheckHorseSkinState(curPlayer, skinPlusIpyData):
-            continue
-        horseSkinActCount += 1
-    return horseSkinActCount
-
-#// A5 27 坐骑提升 #tagCMHorseUp
-#
-#struct    tagCMHorseUp
-#{
-#    tagHead        Head;
-#    WORD        UseItemCnt;        //消耗材料个数
-#    BYTE        IsAutoBuy;        //是否自动购买
-#};
-def OnHorseClassLVUP(index, curPackData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    costItemCount = curPackData.UseItemCnt # 消耗材料个数
-    isAutoBuy = curPackData.IsAutoBuy # 是否自动购买
-    
-    # 判断玩家是否可以升级马匹
+def PlayerHorseLogin(curPlayer):
     if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
-        GameWorld.DebugLog("坐骑未开启!")
         return
-    
-    horseLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserLV)
-    curEatItemCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserEatItemCount)
-    if horseLV <= 0:
-        GameWorld.DebugLog("坐骑提升 该坐骑未激活 horseLV=%s" % horseLV)
-        return
-    
-    horseIpyData = IpyGameDataPY.GetIpyGameData("HorseLVUp", horseLV)
-    if not horseIpyData:
-        return
-    
-    needEatCount = horseIpyData.GetNeedEatCount()
-    if not needEatCount:
-        GameWorld.DebugLog("坐骑已满级!horseLV=%s" % horseLV)
-        return
-    
-    costItemID = IpyGameDataPY.GetFuncCfg("HorseUpItem", 1)
-    if not costItemID or not costItemCount:
-        return
-    
-    costItemIndexList, bindCnt, unBindCnt = ItemCommon.GetPackItemBindStateIndexInfo(curPlayer, costItemID, costItemCount)
-    lackCnt = costItemCount - bindCnt - unBindCnt
-    if lackCnt > 0 and not isAutoBuy:
-        GameWorld.DebugLog("消耗道具不足,无法升级坐骑!costItemID=%s,costItemCount=%s,bindCnt=%s,unBindCnt=%s,lackCnt=%s" 
-                           % (costItemID, costItemCount, bindCnt, unBindCnt, lackCnt))
-        return
-    
-    delCnt = costItemCount
-    if lackCnt > 0:
-        lackCost = ItemCommon.GetAutoBuyItemNeedGold({costItemID:lackCnt})
-        if lackCost <= 0:
-            return
-        infoDict = {ChConfig.Def_Cost_Reason_SonKey:costItemID}
-        if not PlayerControl.PayMoney(curPlayer, IPY_GameWorld.TYPE_Price_Gold_Money, lackCost, ChConfig.Def_Cost_BuyStoreItem, infoDict):
-            return
-        delCnt -= lackCnt
-        
-    # 扣除消耗
-    if delCnt:
-        ItemCommon.DelCostItemByBind(curPlayer, costItemIndexList, bindCnt, unBindCnt, delCnt, ChConfig.ItemDel_Horse)
-        
-    updClassLV = horseLV
-    updEatItemCount = curEatItemCount + costItemCount
-    GameWorld.DebugLog("坐骑培养: horseLV=%s,curEatItemCount=%s,costItemCount=%s,updEatItemCount=%s,needEatCount=%s" 
-                       % (horseLV, curEatItemCount, costItemCount, updEatItemCount, needEatCount))
-    
-    if updEatItemCount >= needEatCount:
-        updClassLV += 1
-        updEatItemCount -= needEatCount
-        GameWorld.DebugLog("    升阶: updClassLV=%s,updEatItemCount=%s" % (updClassLV, updEatItemCount))
-        
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserLV, updClassLV)
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserEatItemCount, updEatItemCount)
-    
-    # 升阶
-    upItemPerCount = horseIpyData.GetUpEatItemPerCount()
-    upCnt = costItemCount / upItemPerCount
-    if updClassLV > horseLV:
-        # 玩家马匹进阶
-        DataRecordPack.DR_NewHorseByClassUp(curPlayer, updClassLV, 0)
-        # 记录开服活动马匹阶级
-        #OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_Horse, updClassLV)
-        
-    Sync_HorseClassData(curPlayer)
-    # 刷属性,更新排行榜
-    RefreshHorseAttr(curPlayer)
-    
-    PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_HorseUpItem, costItemCount)
-    PlayerActLunhuidian.AddLunhuidianValue(curPlayer, PlayerActLunhuidian.AwardType_UseItem, costItemID, costItemCount)
+    SyncHorseClassInfo(curPlayer)
+    SyncHorseSkinInfo(curPlayer)
     return
 
-#// A5 31 坐骑培养 #tagCMHorseTrain
+#// B2 01 坐骑升级 #tagCSHorseLVUP
 #
-#struct    tagCMHorseTrain
+#struct    tagCSHorseLVUP
 #{
-#    tagHead        Head;
-#    BYTE        TrainType;        //培养类型: 1-基础培养,2-特殊培养,3-百分比培养
-#    WORD        UseItemCnt;        //消耗材料个数
+#    tagHead         Head;
+#    BYTE        IsQuick;    // 是否快速升级,0-只消耗1个道具;1-消耗升1级的道具
 #};
-def OnHorseTrain(index, curPackData, tick):
+def OnHorseLVUP(index, curPackData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    trainType = curPackData.TrainType # 培养类型
-    costItemCount = curPackData.UseItemCnt # 消耗材料个数
+    isQuick = curPackData.IsQuick
     
-    trainLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserTrainLV % trainType)
-    curEatItemCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserTrainItemCount % trainType)
-    GameWorld.DebugLog("坐骑培养: trainType=%s,trainLV=%s,costItemCount=%s,curEatItemCount=%s" 
-                       % (trainType, trainLV, costItemCount, curEatItemCount))
-    
-    if trainType <= 0 or trainType > GetHorseTrainTypes():
+    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
         return
     
-    if trainLV <= 0:
-        GameWorld.DebugLog("    坐骑培养未激活  trainType=%s" % trainType)
+    classLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseClassLV)
+    horseLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseLV)
+    curExp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseExp)
+    
+    classIpyData = IpyGameDataPY.GetIpyGameData("HorseClass", classLV)
+    if not classIpyData:
+        return
+    maxLV = classIpyData.GetMaxHorseLV()
+    if horseLV >= maxLV:
+        GameWorld.DebugLog("坐骑该阶已满级! classLV=%s,horseLV=%s >= %s" % (classLV, horseLV, maxLV))
         return
     
-    trainIpyData = IpyGameDataPY.GetIpyGameData("HorseTrain", trainType, trainLV)
-    if not trainIpyData:
+    needExp = classIpyData.GetLVUPItemCnt() # 所需道具数,1个=1点经验
+    if needExp <= 0:
+        GameWorld.DebugLog("坐骑该阶没有升级消耗! classLV=%s,horseLV=%s,needExp=%s" % (classLV, horseLV, needExp))
         return
     
-    needRealmLV = trainIpyData.GetNeedRealmLV()
-    curRealmLV = PlayerControl.GetTrainRealmLVReal(curPlayer, 1)
-    if curRealmLV < needRealmLV:
-        GameWorld.DebugLog("    境界不足,无法培养!  curRealmLV(%s) < needRealmLV(%s)" % (curRealmLV, needRealmLV))
-        return
-    
-    needEatCountTotal = trainIpyData.GetEatCntTotal()
-    if not needEatCountTotal:
-        GameWorld.DebugLog("    该培养已满级!")
-        return
-    
-    costItemIDList = IpyGameDataPY.GetFuncEvalCfg("HorseTrain", 1)
-    costItemID = costItemIDList[trainType - 1]
-    if not costItemID or not costItemCount:
-        return
-    
-    costItemIndexList, bindCnt, unBindCnt = ItemCommon.GetPackItemBindStateIndexInfo(curPlayer, costItemID, costItemCount)
-    lackCnt = costItemCount - bindCnt - unBindCnt
-    if lackCnt > 0:
-        GameWorld.DebugLog("    消耗道具不足,无法培养!costItemID=%s,costItemCount=%s,bindCnt=%s,unBindCnt=%s,lackCnt=%s" 
-                           % (costItemID, costItemCount, bindCnt, unBindCnt, lackCnt))
-        return
-    
-    delCnt = costItemCount
-    
-    # 扣除消耗
-    if delCnt:
-        ItemCommon.DelCostItemByBind(curPlayer, costItemIndexList, bindCnt, unBindCnt, delCnt, ChConfig.ItemDel_Horse)
+    costItemCount = 0
+    if isQuick:
+        costItemCount = max(0, needExp - curExp)
+    elif curExp < needExp:
+        costItemCount = 1
         
-    updClassLV = trainLV
-    updEatItemCount = curEatItemCount + costItemCount
-    GameWorld.DebugLog("    updEatItemCount=%s,needEatCountTotal=%s" % (updEatItemCount, needEatCountTotal))
-    
-    if updEatItemCount >= needEatCountTotal:
-        updClassLV += 1
-        updEatItemCount -= needEatCountTotal
-        GameWorld.DebugLog("    进阶: updClassLV=%s,updEatItemCount=%s" % (updClassLV, updEatItemCount))
+    costItemID = IpyGameDataPY.GetFuncCfg("HorseUpItem", 1)
+    if costItemID and costItemCount > 0:
+        costItemIndexList, bindCnt, unBindCnt = ItemCommon.GetPackItemBindStateIndexInfo(curPlayer, costItemID, costItemCount)
+        lackCnt = costItemCount - bindCnt - unBindCnt
+        if lackCnt > 0:
+            GameWorld.DebugLog("消耗道具不足,无法升级坐骑! costItemID=%s,costItemCount=%s,bindCnt=%s,unBindCnt=%s,lackCnt=%s" 
+                               % (costItemID, costItemCount, bindCnt, unBindCnt, lackCnt))
+            return
         
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserTrainLV % trainType, updClassLV)
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserTrainItemCount % trainType, updEatItemCount)
+        # 扣除消耗
+        ItemCommon.DelCostItemByBind(curPlayer, costItemIndexList, bindCnt, unBindCnt, costItemCount, ChConfig.ItemDel_Horse)
+        
+    addExp = costItemCount # 暂固定1个道具=1点经验
+    updExp = curExp + addExp
+    GameWorld.DebugLog("坐骑加经验: classLV=%s,horseLV=%s,curExp=%s,costItemCount=%s,updExp=%s/%s" 
+                       % (classLV, horseLV, curExp, costItemCount, updExp, needExp))
     
-    # 升阶
-    if updClassLV > trainLV:
-        pass
+    updHorseLV = horseLV
+    if updExp >= needExp:
+        updExp -= needExp
+        updHorseLV += 1
+        GameWorld.DebugLog("    升级: updHorseLV=%s,updExp=%s" % (updHorseLV, updExp))
+        
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseLV, updHorseLV)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseExp, updExp)
     
-    Sync_HorseClassData(curPlayer)
-    # 刷属性,更新排行榜
-    RefreshHorseAttr(curPlayer)
-    
-    if trainType == 2:
-        PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_HorseTrainItem2, costItemCount)
-    elif trainType == 3:
-        PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_HorseTrainItem3, costItemCount)
-    elif trainType == 1:
-        PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_HorseTrainItem1, costItemCount)
+    SyncHorseClassInfo(curPlayer)
+    # 有升级额外处理
+    if updHorseLV > horseLV:
+        RefreshHorseAttr(curPlayer)
     return
 
-#// A5 35 坐骑升星 #tagCMHorseStarUp
+#// B2 02 坐骑进阶 #tagCSHorseClassUP
 #
-#struct    tagCMHorseStarUp
+#struct    tagCSHorseClassUP
 #{
-#    tagHead        Head;
-#    DWORD        HorseID;        //坐骑ID,对应坐骑表ID
+#    tagHead         Head;
 #};
-def OnHorseStarUp(index, clientData, tick):
+def OnHorseClassUP(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
     playerID = curPlayer.GetPlayerID()
-    horseID = clientData.HorseID
     
-    horseStar = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserStar % horseID)        
-    nextStar = horseStar + 1
-    nextIpyData = IpyGameDataPY.GetIpyGameData("HorseStarUp", horseID, nextStar)
-    if not nextIpyData:
-        GameWorld.DebugLog("坐骑不存在该星级,无法升星. horseID=%s,curStar=%s" % (horseID, horseStar), playerID)
+    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
         return
-    needItemList = nextIpyData.GetStarUpNeedItemList()
-    if not needItemList:
+    
+    classLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseClassLV)
+    horseLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseLV)
+    
+    classIpyData = IpyGameDataPY.GetIpyGameData("HorseClass", classLV)
+    if not classIpyData:
         return
+    maxLV = classIpyData.GetMaxHorseLV()
+    if horseLV < maxLV:
+        GameWorld.DebugLog("坐骑该阶未满级无法升阶! classLV=%s,horseLV=%s < %s" % (classLV, horseLV, maxLV))
+        return
+    
+    updClassLV = classLV + 1
+    if not IpyGameDataPY.GetIpyGameDataNotLog("HorseClass", updClassLV):
+        GameWorld.DebugLog("坐骑已满阶! classLV=%s" % (classLV))
+        return
+    
+    costItemID = IpyGameDataPY.GetFuncCfg("HorseUpItem", 2)
+    costItemCnt = classIpyData.GetClassUPItemCnt()
+    if not costItemID or not costItemCnt:
+        return
+    
+    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
+    lackItemDict, delInfoDict = ItemCommon.GetCostItemIndexList({costItemID:costItemCnt}, itemPack)
+    if lackItemDict:
+        GameWorld.DebugLog("坐骑进阶物品不足! classLV=%s,costItemID=%s,costItemCnt=%s,lackItemDict=%s" 
+                           % (classLV, costItemID, costItemCnt, lackItemDict), playerID)
+        return
+    
+    ItemCommon.DelCostItem(curPlayer, itemPack, delInfoDict, "HorseClassUP")
+    
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseClassLV, updClassLV)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseLV, HorseLVStart)
+    
+    GameWorld.DebugLog("坐骑进阶: updClassLV=%s" % (updClassLV), playerID)
+    
+    SyncHorseClassInfo(curPlayer)
+    RefreshHorseAttr(curPlayer)
+    return
+
+def RefreshHorseAttr(curPlayer):
+    CalcHorseAttr(curPlayer)
+    PlayerOnline.GetOnlinePlayer(curPlayer).RefreshRoleAttr()
+    return
+
+def CalcHorseAttr(curPlayer):
+    
+    playerID = curPlayer.GetPlayerID()
+    attrDict = {}
+    
+    classLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseClassLV)
+    horseLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseLV)
+    
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetHorseClassCount()):
+        ipyData = ipyDataMgr.GetHorseClassByIndex(index)
+        ipyClassLV = ipyData.GetClassLV()
+        if classLV < ipyClassLV:
+            continue
+        
+        # 阶特殊属性
+        specAttrIDList = ipyData.GetClassSpecAttrIDList()
+        specAttrValueList = ipyData.GetClassSpecAttrValueList()
+        for index, attrID in enumerate(specAttrIDList):
+            attrValue = specAttrValueList[index] if len(specAttrValueList) > index else 0
+            attrDict[attrID] = attrDict.get(attrID, 0) + attrValue
+            
+        # 阶基础属性
+        attrIDList = ipyData.GetAttrIDList()
+        attrValueList = ipyData.GetClassAttrValueList()
+        for index, attrID in enumerate(attrIDList):
+            attrValue = attrValueList[index] if len(attrValueList) > index else 0
+            attrDict[attrID] = attrDict.get(attrID, 0) + attrValue
+            
+        # 阶等级属性
+        if ipyClassLV < classLV:
+            attrLV = ipyData.GetMaxHorseLV()
+        else:
+            attrLV = horseLV
+        perLVAttrValueList = ipyData.GetPerLVAttrValueList()
+        for index, attrID in enumerate(attrIDList):
+            attrValue = perLVAttrValueList[index] if len(perLVAttrValueList) > index else 0
+            attrDict[attrID] = attrDict.get(attrID, 0) + attrValue * attrLV
+    #GameWorld.DebugLog("坐骑阶属性: %s" % attrDict, playerID)
+    
+    # 外观属性
+    for index in range(ipyDataMgr.GetHorseSkinCount()):
+        ipyData = ipyDataMgr.GetHorseSkinByIndex(index)
+        horseSkinID = ipyData.GetSkinID()
+        if not IsHorseSkinCanUse(curPlayer, horseSkinID, ipyData):
+            continue
+        attrIDList = ipyData.GetAttrIDList()
+        if not attrIDList:
+            continue
+        initAttrValueList = ipyData.GetInitAttrValueList()
+        perStarAddList = ipyData.GetAttrPerStarAddList()
+        star = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseSkinStar % horseSkinID)
+        for aIndex, attrID in enumerate(attrIDList):
+            initValue = initAttrValueList[aIndex] if len(initAttrValueList) > aIndex else 0
+            perStarAdd = perStarAddList[aIndex] if len(perStarAddList) > aIndex else 0
+            attrValue = initValue + perStarAdd * star
+            attrDict[attrID] = attrDict.get(attrID, 0) + attrValue
+            
+    # 保存计算值
+    GameWorld.DebugLog("坐骑属性: %s" % attrDict, playerID)
+    PlayerOnline.GetOnlinePlayer(curPlayer).SetCalcAttr(ChConfig.Def_CalcAttr_Horse, attrDict)
+    return
+
+def SyncHorseClassInfo(curPlayer):
+    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagSCHorseClassInfo)
+    clientPack.ClassLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseClassLV)
+    clientPack.HorseLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseLV)
+    clientPack.Exp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseExp)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+## ----------------------------------------- 外观 --------------------------------------------------
+
+#// B2 03 坐骑外观操作 #tagCSHorseSkinOP
+#
+#struct    tagCSHorseSkinOP
+#{
+#    tagHead         Head;
+#    BYTE        OPType;    // 操作 1-激活;2-佩戴;3-升星
+#    BYTE        SkinID;    // 外观ID,佩戴时发0即为卸下
+#};
+def OnHorseSkinOP(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    opType = clientData.OPType
+    horseSkinID = clientData.SkinID
+    
+    # 激活
+    if opType == 1:
+        AddHorseSkin(curPlayer, horseSkinID)
+    # 佩戴
+    elif opType == 2:
+        OnUseHorseSkin(curPlayer, horseSkinID)
+    # 升级
+    elif opType == 3:
+        OnHorseSkinStarUP(curPlayer, horseSkinID)
+        
+    return
+
+def OnMinute(curPlayer):
+    curTime = int(time.time())
+    
+    delIDList = []
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetHorseSkinCount()):
+        ipyData = ipyDataMgr.GetHorseSkinByIndex(index)
+        horseSkinID = ipyData.GetSkinID()
+        if not GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorseSkinState, horseSkinID):
+            # 未激活的不处理
+            continue
+        endTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseSkinEndTime % horseSkinID)
+        if not endTime or endTime > curTime:
+            # 永久或未过期
+            continue
+        if DelHorseSkin(curPlayer, horseSkinID, False, "HorseSkinTimeout"):
+            delIDList.append(horseSkinID)
+            
+    if delIDList:
+        RefreshHorseAttr(curPlayer)
+    return
+
+def AddHorseSkin(curPlayer, horseSkinID, setExpireTimes=None, isFree=False):
+    if horseSkinID <= 0:
+        return
+    playerID = curPlayer.GetPlayerID()
+    ipyData = IpyGameDataPY.GetIpyGameData("HorseSkin", horseSkinID)
+    if not ipyData:
+        return
+    if ipyData.GetUnlockWay() != 2:
+        GameWorld.DebugLog("非道具激活的不用添加: horseSkinID=%s" % (horseSkinID), playerID)
+        return
+    if not isFree:
+        itemID = ipyData.GetUnlockValue()
+        itemCount = ipyData.GetUnlockNeedCnt()
+        if not itemID or not itemCount:
+            return
+        needItemList = [[itemID, itemCount]]
+        itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
+        lackItemDict, delInfoDict = ItemCommon.GetCostItemIndexList(needItemList, itemPack)
+        if lackItemDict:
+            GameWorld.DebugLog("激活所需物品不足! horseSkinID=%s,lackItemDict=%s" % (horseSkinID, lackItemDict), playerID)
+            return
+        ItemCommon.DelCostItem(curPlayer, itemPack, delInfoDict, "AddHorseSkin")
+        
+    ipyExpireSeconds = ipyData.GetExpireMinutes() * 60
+    
+    curTime = int(time.time())
+    state = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorseSkinState, horseSkinID)
+    endTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseSkinEndTime % horseSkinID)
+    GameWorld.Log("添加坐骑外观: horseSkinID=%s,setExpireTimes=%s,state=%s,endTime=%s,ipyExpireSeconds=%s,curTime=%s" 
+                  % (horseSkinID, setExpireTimes, state, endTime, ipyExpireSeconds, curTime), playerID)
+    updEndTime = endTime
+    
+    # 指定时长的,如GM指定
+    if setExpireTimes > 0:
+        updEndTime = curTime + setExpireTimes
+        GameWorld.Log("    指定时长: horseSkinID=%s,updEndTime=%s" % (horseSkinID, updEndTime), playerID)
+        
+    # 永久
+    elif ipyExpireSeconds == 0 or setExpireTimes == 0:
+        updEndTime = 0
+        GameWorld.Log("    永久时长: horseSkinID=%s,updEndTime=%s" % (horseSkinID, updEndTime), playerID)
+        
+    else:
+        # 未过期
+        if endTime > curTime:
+            updEndTime = endTime + ipyExpireSeconds
+            GameWorld.Log("    累加时长: horseSkinID=%s,updEndTime=%s" % (horseSkinID, updEndTime), playerID)
+        else:
+            updEndTime = curTime + ipyExpireSeconds
+            GameWorld.Log("    重新激活: horseSkinID=%s,updEndTime=%s" % (horseSkinID, updEndTime), playerID)
+            
+    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorseSkinState, horseSkinID, 1)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseSkinEndTime % horseSkinID, updEndTime)
+    RefreshHorseAttr(curPlayer)
+    SyncHorseSkinInfo(curPlayer, [horseSkinID])
+    return True
+
+def DelHorseSkin(curPlayer, horseSkinID, isRefreshAttr=True, notifyMail=""):
+    playerID = curPlayer.GetPlayerID()
+    ipyData = IpyGameDataPY.GetIpyGameData("HorseSkin", horseSkinID)
+    if not ipyData:
+        return
+    if not GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorseSkinState, horseSkinID):
+        return
+    GameWorld.Log("删除坐骑外观: horseSkinID=%s,notifyMail=%s" % (horseSkinID, notifyMail), playerID)
+    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorseSkinState, horseSkinID, 0)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseSkinEndTime % horseSkinID, 0)
+    # 星级不重置,重新激活后再次生效
+    
+    if PlayerControl.GetHorseSkinID(curPlayer) == horseSkinID:
+        defaultSkinID = GetDefaultHorseSkinID()
+        PlayerControl.SetHorseSkinID(curPlayer, defaultSkinID)
+        GameWorld.DebugLog("玩家佩戴的坐骑外观被删除,随机重置默认! defaultSkinID=%s" % defaultSkinID, playerID)
+        
+    if isRefreshAttr:
+        RefreshHorseAttr(curPlayer)
+    SyncHorseSkinInfo(curPlayer, [horseSkinID])
+    if notifyMail:
+        PlayerControl.SendMailByKey(notifyMail, [playerID], [], [horseSkinID])
+        
+    return True
+
+def GetDefaultHorseSkinID():
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetHorseSkinCount()):
+        ipyData = ipyDataMgr.GetHorseSkinByIndex(index)
+        horseSkinID = ipyData.GetSkinID()
+        if ipyData.GetUnlockWay() != 1:
+            continue
+        if ipyData.GetUnlockValue() > 0:
+            # 取0阶的
+            continue
+        return horseSkinID
+    return 0
+
+def IsHorseSkinCanUse(curPlayer, horseSkinID, ipyData=None):
+    ## 坐骑外观是否可用
+    state = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorseSkinState, horseSkinID)
+    if state:
+        return True
+    
+    if not ipyData:
+        ipyData = IpyGameDataPY.GetIpyGameData("HorseSkin", horseSkinID)
+    if ipyData:
+        unlockWay = ipyData.GetUnlockWay()
+        if unlockWay == 1:
+            classLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseClassLV)
+            if classLV >= ipyData.GetUnlockValue():
+                return True
+            
+    return False
+
+def OnUseHorseSkin(curPlayer, horseSkinID):
+    playerID = curPlayer.GetPlayerID()
+    if horseSkinID and not IsHorseSkinCanUse(curPlayer, horseSkinID):
+        GameWorld.DebugLog("该坐骑外观不可用! horseSkinID=%s" % (horseSkinID), playerID)
+        return    
+    PlayerControl.SetHorseSkinID(curPlayer, horseSkinID)
+    return
+
+def OnHorseSkinStarUP(curPlayer, horseSkinID):
+    playerID = curPlayer.GetPlayerID()
+    if not IsHorseSkinCanUse(curPlayer, horseSkinID):
+        GameWorld.DebugLog("该坐骑外观不可用! horseSkinID=%s" % (horseSkinID), playerID)
+        return
+    ipyData = IpyGameDataPY.GetIpyGameData("HorseSkin", horseSkinID)
+    if not ipyData:
+        return
+    starMax = ipyData.GetStarMax()
+    curStar = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseSkinStar % horseSkinID)
+    if curStar >= starMax:
+        GameWorld.DebugLog("星级已满! horseSkinID=%s,curStar=%s >= %s" % (horseSkinID, curStar, starMax), playerID)
+        return
+    if ipyData.GetUnlockWay() != 2:
+        return
+    itemID = ipyData.GetUnlockValue()
+    itemCount = ipyData.GetUnlockNeedCnt()
+    if not itemID or not itemCount:
+        return
+    needItemList = [[itemID, itemCount]]
     itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
     lackItemDict, delInfoDict = ItemCommon.GetCostItemIndexList(needItemList, itemPack)
     if lackItemDict:
-        GameWorld.DebugLog("坐骑升星所需物品不足! horseID=%s,nextStar=%s,needItemList=%s,lackItemDict=%s" 
-                           % (horseID, nextStar, needItemList, lackItemDict), playerID)
+        GameWorld.DebugLog("升星所需物品不足! horseSkinID=%s,lackItemDict=%s" % (horseSkinID, lackItemDict), playerID)
         return
+    ItemCommon.DelCostItem(curPlayer, itemPack, delInfoDict, "HorseSkinStarUP")
     
-    ItemCommon.DelCostItem(curPlayer, itemPack, delInfoDict, "HorseStarUp")
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserStar % horseID, nextStar)
-    
-    GameWorld.DebugLog("坐骑升星. horseID=%s,curStar=%s,nextStar=%s" % (horseID, horseStar, nextStar), playerID)
-    
+    nextStar = curStar + 1
+    GameWorld.DebugLog("升星! horseSkinID=%s,nextStar=%s" % (horseSkinID, nextStar), playerID)
+    SetHorseSkinStar(curPlayer, horseSkinID, nextStar)
+    return
+
+def SetHorseSkinStar(curPlayer, horseSkinID, setStar):
+    if not IsHorseSkinCanUse(curPlayer, horseSkinID):
+        return
+    ipyData = IpyGameDataPY.GetIpyGameData("HorseSkin", horseSkinID)
+    if not ipyData:
+        return
+    setStar = min(setStar, ipyData.GetStarMax())
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorseSkinStar % horseSkinID, setStar)
     RefreshHorseAttr(curPlayer)
-    SyncHorseStarInfo(curPlayer, horseID)
-    return
+    SyncHorseSkinInfo(curPlayer, [horseSkinID])
+    return True
 
-def GetHorseTrainTypes():
-    return len(IpyGameDataPY.GetFuncEvalCfg("HorseTrain", 1))
-
-def PlayerHorseLogin(curPlayer):
-    ##坐骑登录处理
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
-        return
-    
-    # 坐骑幻化状态多值支持修改,线上版本做旧值转移
-    oldVerSkinStateKey = "HorserSkinPlusState"
-    oldVerSkinStateValue = curPlayer.NomalDictGetProperty(oldVerSkinStateKey)
-    if oldVerSkinStateValue:
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState % 0, oldVerSkinStateValue)
-        PlayerControl.NomalDictSetProperty(curPlayer, oldVerSkinStateKey, 0)
-        GameWorld.Log("线上版本转移坐骑幻化状态记录字典值! oldVerSkinStateValue=%s" % (oldVerSkinStateValue), curPlayer.GetPlayerID())
-        
-    # 培养是后面加的功能,每次登录补检查一下功能开始时设置为培养1级
-    for trainType in xrange(1, GetHorseTrainTypes() + 1):
-        if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserTrainLV % trainType) == 0:
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserTrainLV % trainType, 1)
-            
-    Sync_HorseClassData(curPlayer)
-    SyncHorsePetSkinData(curPlayer)
-    SyncHorseSkinTimeInfo(curPlayer)
-    SyncHorseStarInfo(curPlayer)
-    return
-
-def Sync_HorseClassData(curPlayer):
-    horseData = ChPyNetSendPack.tagTrainHorseData()
-    horseData.Clear()
-    horseData.LV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserLV)
-    horseData.EatItemCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserEatItemCount)
-    horseData.SkinPlusState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinPlusState)
-    horseData.TrainLVList = []
-    horseData.TrainItemCountList = []
-    for trainType in xrange(1, GetHorseTrainTypes() + 1):
-        horseData.TrainLVList.append(curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserTrainLV % trainType))
-        horseData.TrainItemCountList.append(curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserTrainItemCount % trainType))
-    horseData.TrainTypes = len(horseData.TrainLVList)
-    horseData.SkinPlusStateList = GetHorseSkinPlusStateList(curPlayer)
-    horseData.SkinPlusStateCount = len(horseData.SkinPlusStateList)
-    NetPackCommon.SendFakePack(curPlayer, horseData)
-    return
-
-def GetHorseSkinPlusStateList(curPlayer):
-    maxSkinID = 0
-    skinPlusStateList = []
-    ipyDataMgr = IpyGameDataPY.IPY_Data()
-    skinCnt = ipyDataMgr.GetHorseSkinPlusCount()
-    for index in xrange(skinCnt):
-        maxSkinID = max(maxSkinID, ipyDataMgr.GetHorseSkinPlusByIndex(index).GetID())
-    for index in xrange(maxSkinID / 31+1):
-        skinPlusStateList.append(curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinPlusState % index))
-    return skinPlusStateList
-
-def GetHorseSumLV(curPlayer):
-    ## 坐骑总等级
-    return curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserLV)
-
-def IsHorseLVFull(curPlayer):
-    ## 坐骑是否满级
-    horseLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserLV)    
-    horseIpyData = IpyGameDataPY.GetIpyGameData("HorseLVUp", horseLV)
-    if not horseIpyData:
-        return False
-    needEatCount = horseIpyData.GetNeedEatCount()
-    if not needEatCount:
-        return True
-    return False
-
-#============================骑宠觉醒=============================
-#// A5 29 骑宠觉醒 #tagCMHorsePetAwake
-#
-#struct    tagCMHorsePetAwake
-#{
-#    tagHead        Head;
-#    WORD    Type;    // 1-坐骑 2-灵宠
-#    DWORD    ID;    // 对应坐骑表灵宠表ID
-#    DWORD    EatItemID;    // 吞噬的物品ID
-#};
-def OnHorsePetAwake(index, curPackData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    skintype = curPackData.Type
-    horsePetID = curPackData.ID
-    eatItemID = curPackData.EatItemID
-    GameWorld.DebugLog("骑宠觉醒: skintype=%s,horsePetID=%s,eatItemID=%s" % (skintype, horsePetID, eatItemID))
-    if not IpyGameDataPY.GetIpyGameDataByCondition('HorsePetSkin', {'Type':skintype, 'ID':horsePetID}):
-        return
-#    if skintype == 1:
-#        #坐骑玩法修改,暂关闭坐骑觉醒
-#        return
-#        if not IsHorseMaxLV(curPlayer, horsePetID):
-#            GameWorld.DebugLog('骑宠觉醒 对应骑宠未满级 horsePetID=%s' % horsePetID)
-#            return
-#    else:
-#        if not PlayerPet.IsPetMaxLV(curPlayer, horsePetID):
-#            GameWorld.DebugLog('骑宠觉醒 对应骑宠未满级 horsePetID=%s' % horsePetID)
-#            return
-    
-    skinData = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorsePetSkinData % (skintype, horsePetID), 0)
-    curSkinLV, curSkinIndex = skinData / 100, skinData % 100
-    ipyData = IpyGameDataPY.GetIpyGameData('HorsePetSkin', skintype, horsePetID, curSkinLV)
-    if not ipyData:
-        return
-    upNeedExp = ipyData.GetNeedExp()
-    if not upNeedExp:
-        GameWorld.DebugLog('骑宠觉醒 已满级')
-        return
-    curExp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorsePetSkinExp % (skintype, horsePetID), 0)
-    curItem = GameWorld.GetGameData().GetItemByTypeID(eatItemID)
-    if not curItem:
-        return
-    curEff = curItem.GetEffectByIndex(4)
-    curEffID = curEff.GetEffectID()
-    addExp = curEff.GetEffectValue(0)
-    if curEffID != ChConfig.Def_Effect_HorsePetSkinExp or not addExp:
-        GameWorld.ErrLog('物品效果5没有配置骑宠觉醒经验效果! eatItemID=%s,curEffID=%s,addExp=%s,needEffID=%s' 
-                         % (eatItemID, curEffID, addExp, ChConfig.Def_Effect_HorsePetSkinExp))
-        return
-    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
-    hasEnough, itemList = ItemCommon.GetItem_FromPack_ByID(eatItemID, itemPack, 1)
-    if not hasEnough:
-        GameWorld.DebugLog("OnHorsePetAwake() item(%s[%s]) isn't enough" % (eatItemID, 1))
-        return
-    ItemCommon.ReduceItem(curPlayer, itemPack, itemList, 1, False, ChConfig.ItemDel_HorsePetAwake)
-    
-    updExp = curExp + addExp
-    updSkinLV = curSkinLV
-    updSkinIndex = curSkinIndex
-    if updExp >= upNeedExp:
-        for _ in xrange(10):
-            ipyData = IpyGameDataPY.GetIpyGameDataNotLog('HorsePetSkin', skintype, horsePetID, updSkinLV + 1)
-            if not ipyData:
-                break
-            if updExp < upNeedExp:
-                break
-            updExp -= upNeedExp
-            updSkinLV += 1
-            updSkinIndex = ipyData.GetSkinIndex()
-            upNeedExp = ipyData.GetNeedExp()
-            
-    updSkinData = updSkinLV * 100 + updSkinIndex
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorsePetSkinData % (skintype, horsePetID), updSkinData)
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorsePetSkinExp % (skintype, horsePetID), updExp)
-    GameWorld.DebugLog("skinData=%s,curExp=%s,updSkinData=%s,updExp=%s" % (skinData, curExp, updSkinData, updExp))
-    if curSkinIndex != updSkinIndex:
-        __DoHorsePetSkinChange(curPlayer, skintype, horsePetID, updSkinIndex)
-    if updSkinLV != curSkinLV:
-        if skintype == 1:
-            RefreshHorseAttr(curPlayer)
-        else:
-            PlayerPet.RefreshPetItemAddAttr(curPlayer, True)
-        
-    SyncHorsePetSkinData(curPlayer, [[skintype, horsePetID]])
-    return
-
-#// A5 30 骑宠外观选择 #tagCMHorsePetSkinSelect
-#struct    tagCMHorsePetSkinSelect
-#{
-#    tagHead        Head;
-#    WORD    Type;    // 1-坐骑 2-灵宠
-#    DWORD    ID;    // 对应坐骑表灵宠表ID
-#    BYTE    SkinIndex;    // 外观索引
-#};
-def OnHorsePetSkinSelect(index, curPackData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    skintype = curPackData.Type
-    horsePetID = curPackData.ID
-    skinIndex = curPackData.SkinIndex
-    ipyData = IpyGameDataPY.GetIpyGameDataByCondition('HorsePetSkin', {'Type':skintype, 'ID':horsePetID, 'SkinIndex':skinIndex})
-    if not ipyData:
-        return
-    skinData = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorsePetSkinData % (skintype, horsePetID), 0)
-    curSkinLV, curSkinIndex = skinData / 100, skinData % 100
-    if skinIndex == curSkinIndex:
-        return
-    if curSkinLV < ipyData.GetSkinLV():
-        return
-    
-    updSkinData = curSkinLV * 100 + skinIndex
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorsePetSkinData % (skintype, horsePetID), updSkinData)
-    
-    __DoHorsePetSkinChange(curPlayer, skintype, horsePetID, skinIndex)
-    
-    SyncHorsePetSkinData(curPlayer, [[skintype, horsePetID]])
-    return
-
-def __DoHorsePetSkinChange(curPlayer, skinType, horsePetID, skinIndex):
-    ##骑宠觉醒外观更换
-    ChEquip.ChangeEquipfacadeByHorsePetSkin(curPlayer, skinType, skinIndex)
-    if skinType == 1:#坐骑
-        # 坐骑功能修改,暂关闭觉醒
-        return
-        playerEquip = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptEquip)
-        itemRideHorse = playerEquip.GetAt(Def_HorseEquipIndex)
-        #无指定道具
-        if itemRideHorse.IsEmpty():
-            return
-        ipyData = IpyGameDataPY.GetIpyGameData("Horse", horsePetID)
-        if not ipyData:
-            return
-        horseItemID = ipyData.GetItemID()
-        if itemRideHorse.GetItemTypeID() != horseItemID:
-            return
-        itemRideHorse.SetUserAttr(ShareDefine.Def_IudetHorsePetSkinIndex, skinIndex)
-    elif skinType == 2:#灵宠
-        pass
-    return
-
-def GetHorsePetSkinIndex(curPlayer, skintype, horsePetID):
-    # 获取骑宠觉醒外观索引
-    skinData = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorsePetSkinData % (skintype, horsePetID), 0)
-    return skinData % 100
-
-def SyncHorsePetSkinData(curPlayer, skinList=None):
-    if not skinList:
-        skinList = []
-        ipyMgr = IpyGameDataPY.IPY_Data()
-        for i in xrange(ipyMgr.GetHorsePetSkinCount()):
-            ipyData = ipyMgr.GetHorsePetSkinByIndex(i)
-            if [ipyData.GetType(), ipyData.GetID()] not in skinList:
-                skinList.append([ipyData.GetType(), ipyData.GetID()])
-    
-    packData = ChPyNetSendPack.tagMCHorsePetSkinData()
-    packData.Clear()
-    packData.InfoList = []
-    for skintype, horsePetID in skinList:
-        skinInfo = ChPyNetSendPack.tagMCHorsePetSkinInfo()
-        skinInfo.Type = skintype
-        skinInfo.ID = horsePetID
-        curExp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorsePetSkinExp % (skintype, horsePetID), 0)
-        skinData = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorsePetSkinData % (skintype, horsePetID), 0)
-        if not curExp and not skinData:
-            continue
-        curSkinLV, curSkinIndex = skinData / 100, skinData % 100
-        skinInfo.Exp = curExp
-        skinInfo.SkinLV = curSkinLV
-        skinInfo.SkinIndex = curSkinIndex
-        packData.InfoList.append(skinInfo)
-    packData.Num = len(packData.InfoList)
-    NetPackCommon.SendFakePack(curPlayer, packData)
-    return
-
-def SyncHorseSkinTimeInfo(curPlayer, skinID=None):
-    skinList = []
-    if skinID > 0:
-        timeInfo = ChPyNetSendPack.tagMCHorseSkinTimeInfo()
-        timeInfo.ID = skinID
-        timeInfo.InvalidTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % skinID)
-        skinList.append(timeInfo)
-    else:
-        ipyDataMgr = IpyGameDataPY.IPY_Data()
-        for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
-            skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
-            skinID = skinPlusIpyData.GetID()
-            if not skinPlusIpyData.GetSkinValidTime():
-                continue
-            timeInfo = ChPyNetSendPack.tagMCHorseSkinTimeInfo()
-            timeInfo.ID = skinID
-            timeInfo.InvalidTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % skinID)
-            skinList.append(timeInfo)
-            
-    packData = ChPyNetSendPack.tagMCHorseSkinTimeInfoList()
-    packData.Clear()
-    packData.TimeInfoList = skinList
-    packData.TimeCnt = len(packData.TimeInfoList)
-    NetPackCommon.SendFakePack(curPlayer, packData)
-    return
-
-def SyncHorseStarInfo(curPlayer, horseID=None):
-    if horseID > 0:
-        syncIDList = [horseID]
-    else:
+def SyncHorseSkinInfo(curPlayer, horseSkinIDList=None):
+    if horseSkinIDList == None:
         syncIDList = []
         ipyDataMgr = IpyGameDataPY.IPY_Data()
-        for index in range(ipyDataMgr.GetHorseCount()):
-            ipyData = ipyDataMgr.GetHorseByIndex(index)
-            syncIDList.append(ipyData.GetHorseID())
-            
-    if not syncIDList:
-        return
-    
-    horseStarList = []
-    for horseID in syncIDList:
-        horseStar = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserStar % horseID)
-        if not horseStar:
-            continue
-        starInfo = ChPyNetSendPack.tagMCHorseStar()
-        starInfo.Clear()
-        starInfo.HorseID = horseID
-        starInfo.Star = horseStar
-        horseStarList.append(starInfo)
+        for index in range(ipyDataMgr.GetHorseSkinCount()):
+            ipyData = ipyDataMgr.GetHorseSkinByIndex(index)
+            syncIDList.append(ipyData.GetSkinID())
+    else:
+        syncIDList = horseSkinIDList
         
-    if not horseStarList:
+    horseSkinList = []
+    for horseSkinID in syncIDList:
+        state = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HorseSkinState, horseSkinID)
+        if not state and horseSkinIDList == None:
+            continue
+        horseSkin = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagSCHorseSkin)
+        horseSkin.HorseSkinID = horseSkinID
+        horseSkin.State = state
+        horseSkin.EndTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseSkinEndTime % horseSkinID)
+        horseSkin.Star = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorseSkinStar % horseSkinID)
+        horseSkinList.append(horseSkin)
+        
+    if not horseSkinList:
         return
     
-    clientPack = ChPyNetSendPack.tagMCHorseStarInfo()
-    clientPack.Clear()
-    clientPack.HorseStarList = horseStarList
-    clientPack.Count = len(clientPack.HorseStarList)
+    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagSCHorseSkinInfo)
+    clientPack.HorseSkinList = horseSkinList
+    clientPack.Count = len(clientPack.HorseSkinList)
     NetPackCommon.SendFakePack(curPlayer, clientPack)
     return
+## -------------------------------------------------------------------------------------------------

--
Gitblit v1.8.0