ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
@@ -6,1063 +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)
    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
## -------------------------------------------------------------------------------------------------