hxp
7 天以前 b4488bc3020cf5da3bf9fe4a0b27ba59b2d9f8cc
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -16,7 +16,6 @@
import GameWorld
import SkillShell
import ChConfig
import PlayerHorse
import SkillCommon
import GameMap
import FBLogic
@@ -37,30 +36,27 @@
import NetPackCommon
import DataRecordPack
import PlayerPrestigeSys
import PlayerActivity
import FBCommon
import PassiveBuffEffMng
import EventReport
import PlayerSuccess
import ItemControler
import GameFuncComm
import IpyGameDataPY
import PyGameData
import PlayerFeastTravel
import PlayerActTurntable
import PlayerCostRebate
import PlayerActLunhuidian
import GY_Query_CrossRealmReg
import PlayerTongTianLing
import FunctionNPCCommon
import PlayerGoldInvest
import OpenServerActivity
import CrossRealmPlayer
import CrossPlayerData
import PlayerActivity
import ChNetSendPack
import PlayerState
import PlayerOnline
import PlayerTask
import PlayerMail
import TurnAttack
import ChPlayer
import GameObj
@@ -416,24 +412,6 @@
        return False
    
    return True
##地图特殊限制检查, 装备检查
# @param curPlayer
# @return bool
def CheckEquipCanTrans(curPlayer, destMapID):
    if destMapID != 10060:
        return True
    #有翅膀可以上天空
    playerEquip = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptEquip)
    curEquip = playerEquip.GetAt(IPY_GameWorld.retWing)
    if curEquip and not curEquip.IsEmpty():
        return True
    #GeRen_lhs_861048
    NotifyCode(curPlayer, "GeRen_lhs_861048", [destMapID])
    return False
#---------------------------------------------------------------------
##通知客户端, 玩家Loading开始
@@ -933,19 +911,6 @@
    return (curPlayerAction in ChConfig.Def_Player_Can_Transfer_State)
#------------------------------玩家离开服务器的逻辑------------------------------------
#---------------------------------------------------------------------
##骑马下线逻辑
# @param curPlayer 玩家实例
# @return 返回值无意义
# @remarks 骑马下线逻辑
def __RidingHorsePlayerDisconnect(curPlayer):
    #在骑马
    if curPlayer.GetPlayerVehicle() == IPY_GameWorld.pvHorse:
        #执行下马逻辑
        PlayerHorse.PlayerRideHorseDown(curPlayer, False)
    return
#---------------------------------------------------------------------
##玩家下线/玩家切换地图公用逻辑
# @param curPlayer 玩家实例
# @param tick 时间戳
@@ -1002,6 +967,64 @@
        if playerID in playerIDList:
            playerIDList.remove(playerID)
    return
def OnPlayerLogin(curPlayer):
    DoGMForbidenTalkOnLogin(curPlayer)
    curPlayer.SetDict(ChConfig.Def_PDict_DayOnlineCalcTime, int(time.time()))
    return
def PlayerOnDay(curPlayer):
    PayCoinOnDay(curPlayer)
    # 重置今日累计在线时长统计
    NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_DayOnlineTime, 0)
    curPlayer.SetDict(ChConfig.Def_PDict_DayOnlineCalcTime, int(time.time()))
    return
def OnMinute(serverTime):
    # 定时记录当前在线玩家今日总在线时长
    if [serverTime.hour, serverTime.minute] in [[23, 55], [23, 59]]:
        playerManager = GameWorld.GetPlayerManager()
        for i in xrange(playerManager.GetPlayerCount()):
            curPlayer = playerManager.GetPlayerByIndex(i)
            if not GameWorld.IsNormalPlayer(curPlayer):
                continue
            RecordTodayOnlineTime(curPlayer)
    return
def RecordTodayOnlineTime(curPlayer):
    '''更新记录今日累计在线时长
    【注】不能在onday调用,不然可能导致流向记录是错误的
    比如玩家离线了多天后上线,会触发onday,此时记录的在线时长实际是上一次离线天的在线时长
    【正确调用时机】
    1. 每次离线
    2. 每日的 23:59 分触发一次,理论上可能最多少统计1分钟,暂无视
    '''
    onlineTime = GetOnlineTimeToday(curPlayer)
    DataRecordPack.DR_OnlineTimeToday(curPlayer, onlineTime)
    return
def GetOnlineTimeToday(curPlayer):
    ## 获取今日累计在线时长,即使不离线过天也需要重置重新计算
    curTime = int(time.time())
    onlineTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_DayOnlineTime)
    calcTime = curPlayer.GetDictByKey(ChConfig.Def_PDict_DayOnlineCalcTime) # 计算用,不用存db
    if not calcTime:
        calcTime = curTime
    curPlayer.SetDict(ChConfig.Def_PDict_DayOnlineCalcTime, curTime)
    passTime = curTime - calcTime
    if passTime > 0:
        onlineTime += passTime
    onlineTime = min(onlineTime, 86400) # 3600*24=86400 # 最大累计1天时长
    GameWorld.DebugLogEx("今日累计在线时长: %s, passTime=%s", onlineTime, passTime, curPlayer.GetPlayerID())
    NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_DayOnlineTime, onlineTime)
    return onlineTime
##更新保存玩家在线时间
# @param curPlayer 玩家实例
@@ -1093,6 +1116,7 @@
    #宠物下线逻辑, 这里要进行排行榜, 优先做, 避免玩家光环等属性影响宠物属性失效
    PetControl.DoLogic_PetInfo_OnLeaveServer(curPlayer, tick)
    
    RecordTodayOnlineTime(curPlayer)
    #清除下线消失的buff, 在更新排行榜之前
    __DisconnectClearBuff(curPlayer, tick)
    
@@ -1112,8 +1136,6 @@
    
    #玩家下线/玩家切换地图公用逻辑
    __PlayerLeaveServerLogic(curPlayer, tick, True)
    #骑马玩家下线逻辑
    __RidingHorsePlayerDisconnect(curPlayer)
    #召唤兽死亡
    KillPlayerSummonNPC(curPlayer)
    #更新从本地图离线信息
@@ -2485,17 +2507,12 @@
    return
def GetUnXiantaoCntEquip(curPlayer):
    '''因为战锤对应装备是1个战锤可能对应多个装备掉落,所以分解装备的时候1个战锤需要支持可拆分
    所以需要支持小数存储,暂定以支持3位小数存储
    '''
    return curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip) / 1000.0
    return curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip)
def AddUnXiantaoCntEquip(curPlayer, addCnt):
    unXiantaoCntEquip = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip) + addCnt * 1000
    unXiantaoCntEquip = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip) + addCnt
    return NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntEquip, unXiantaoCntEquip)
def SetUnXiantaoCntEquip(curPlayer, unXiantaoCntEquip):
    ## 保存装备未结算战锤数,保留3位小数
    # @param unXiantaoCntEquip: 实际的未结算数量,支持小数
    unXiantaoCntEquip = int(round(unXiantaoCntEquip, 3) * 1000) # 保留3为小数
    ## 保存装备未结算战锤数
    return NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntEquip, unXiantaoCntEquip)
##玩家是否有钱款
@@ -2776,13 +2793,11 @@
    #银票支付
    elif type_Price == IPY_GameWorld.TYPE_Price_Silver_Paper:
        __PayMoneyAfterBySilverPaper(curPlayer, price)
    #活跃度处理
    PlayerActivity.OnPayMoneyActivity(curPlayer, type_Price, price)
    #转盘活动
    PlayerActTurntable.OnPlayerUseGold(curPlayer, type_Price, price)
    #PlayerActTurntable.OnPlayerUseGold(curPlayer, type_Price, price)
    #轮回殿
    PlayerActLunhuidian.AddLunhuidianValue(curPlayer, PlayerActLunhuidian.AwardType_PayMoney, type_Price, price)
    #PlayerActLunhuidian.AddLunhuidianValue(curPlayer, PlayerActLunhuidian.AwardType_PayMoney, type_Price, price)
    if type_Price == ShareDefine.TYPE_Price_Xiantao:
        # 累加未结算战锤 - 经验
        unXiantaoCntExp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntExp)
@@ -2802,9 +2817,7 @@
                unXiantaoCntBooty = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntBooty % itemID)
                NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntBooty % itemID, unXiantaoCntBooty + price)
                
        PlayerPrestigeSys.AddRealmTaskValue(curPlayer, PlayerPrestigeSys.RealmTaskType_UseXiantao, price)
        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_CutTree, price)
        TurnAttack.GetMainFightMgr(curPlayer).useZhanchui += price
        
    unitPrice = price if quantity == 1 else int(math.ceil(price * 1.0 / quantity)) # 单价
    #reason_name = "Unknown" if not costType else costType
@@ -2840,35 +2853,8 @@
        orderInfo = infoDict.get("orderInfo", "")
        GameWorld.Log("记录消耗代币: eventName=%s,price=%s,nowMoney=%s,payCoinDay=%s,orderInfo=%s" % (eventName, price, nowMoney, payCoinDay, orderInfo), playerID)
        GameWorld.AddPlayerRec(playerID, ShareDefine.Def_PlayerRecType_PayCoin, [2, price, nowMoney, payCoinDay], orderInfo, 1)
    # 流向用 eventName
    #if eventName:
    if costType == ChConfig.Def_Cost_Treasure:
        GameWorld.DebugLog("BT版本寻宝消耗货币暂时不记录流向: %s,type_Price=%s,price=%s" % (eventName, type_Price, price))
    else:
        DataRecordPack.DR_UseMoney(curPlayer, eventName, type_Price, price, infoDict) # 流向
        EventReport.WriteEvent_virtual_resource(curPlayer, type_Price, reason_name, quantity,
                                                unitPrice, ShareDefine.Def_UserAction_Money_Use, infoDict)
    #===========================================================================
    # if type_Price == IPY_GameWorld.TYPE_Price_Gold_Money:
    #    EventReport.WriteEvent_virtual_cost(curPlayer, quantity, unitPrice, reason_name)
    # elif type_Price in [IPY_GameWorld.TYPE_Price_Gold_Paper, IPY_GameWorld.TYPE_Price_Silver_Money]:
    #
    #    # 金币未指定消费类型的不记录
    #    if type_Price == IPY_GameWorld.TYPE_Price_Silver_Money and costType == ChConfig.Def_Cost_Unknown:
    #        return
    #    EventReport.WriteEvent_virtual_resource(curPlayer, type_Price, reason_name, quantity,
    #                                            unitPrice, ShareDefine.Def_UserAction_Money_Use)
    #
    # # 自定义记录
    # priceNameDict = {IPY_GameWorld.TYPE_Price_Gold_Money:["钻石", "GetGold", 0],
    #                 IPY_GameWorld.TYPE_Price_Gold_Paper:["绑钻", "GetGoldPaper", 0],
    #                 IPY_GameWorld.TYPE_Price_Silver_Money:["金币", "GetSilver", ChConfig.Def_DRRecord_Min_Silver],
    #                 }
    # if type_Price in priceNameDict:
    #    typeName, moneyFuncName, minPrice = priceNameDict[type_Price]
    #    if minPrice <= 0 or price >= minPrice:
    #        EventReport.WriteEvent_pay_money(curPlayer, reason_name, typeName, price, getattr(curPlayer, moneyFuncName)())
    #===========================================================================
    DataRecordPack.DR_UseMoney(curPlayer, eventName, type_Price, price, infoDict) # 流向
    return
## 付款以后后续操作(金子)
@@ -2884,62 +2870,16 @@
    # 消费返利
    if costType not in ChConfig.CostRebate_DisableType:
        PlayerCostRebate.AddCostRebateGold(curPlayer, costType, price, infoDict)
        PlayerFeastTravel.AddFeastTravelTaskValue(curPlayer, ChConfig.Def_FeastTravel_UseGold, price)
    else:
        GameWorld.DebugLog("不计入消费活动的消费类型!costType=%s" % costType, curPlayer.GetPlayerID())
        
    # 事件汇报
    #===========================================================================
    # if costType == ChConfig.Def_Cost_BourseBuy:
    #    # 交易所购买物品,汇报交易钻石
    #    playerName = curPlayer.GetPlayerName()
    #    leftGold = curPlayer.GetGold()
    #    eventParam = "RoleID=%s,Price=%s,TradeType=Lost,LeftGold=%s" \
    #                    % (playerName, price, leftGold)
    #    EventReport.EventReport(ShareDefine.Def_UserAction_TradeGold, eventParam, curPlayer)
    # else:
    #    playerName = curPlayer.GetPlayerName()
    #    leftGold = curPlayer.GetGold()
    #    eventParam = "RoleID=%s,CostType=%s,ItemID=%s,Price=%s,LeftGold=%s" \
    #                    % (playerName, costType, infoDict, price, leftGold)
    #    EventReport.EventReport(ShareDefine.Def_UserAction_UseGold, eventParam, curPlayer)
    #===========================================================================
    # 通知元宝消费记录
    #__Sync_GoldCostReport(curPlayer, costType, price, expandValue)
    return
## 通知元宝消费记录
#  @param curPlayer: 玩家实例
#  @param costType: 消费点类型
#  @param price: 消费额度
#  @param itemID: 物品id,消费类型为物品时可填
#  @return None
def __Sync_GoldCostReport(curPlayer, costType, price, itemID=0):
    sendPack = ChPyNetSendPack.tagMCGoldCostReport()
    sendPack.Clear()
    sendPack.CostType = costType
    sendPack.Price = price
    sendPack.ItemID = itemID
    NetPackCommon.SendFakePack(curPlayer, sendPack)
    return
## 付款以后后续操作(金票)
#  @param curPlayer 玩家实例
#  @param price ,货币价格
#  @return None
def __PayMoneyAfterByGoldPaper(curPlayer, price, costType, infoDict):
    # 事件汇报
    #===========================================================================
    # playerName = curPlayer.GetPlayerName()
    # leftGoldPaper = curPlayer.GetGoldPaper()
    # eventParam = "RoleID=%s,CostType=%s,ItemID=%s,Price=%s,LeftGoldPaper=%s" \
    #                % (playerName, costType, infoDict, price, leftGoldPaper)
    # EventReport.EventReport(ShareDefine.Def_UserAction_UseGoldPaper, eventParam, curPlayer)
    #===========================================================================
def __PayMoneyAfterByGoldPaper(curPlayer, price, costType, infoDict):
    return
@@ -2992,8 +2932,9 @@
            return
        SetMoney(curPlayer, priceType, updPlayerGold)
        
        if isGiveBourseMoney and updPlayerGold > 0:
            GiveMoney(curPlayer, ShareDefine.TYPE_Price_BourseMoney, min(value, updPlayerGold))
        #废弃交易所额度
        #if isGiveBourseMoney and updPlayerGold > 0:
        #    GiveMoney(curPlayer, ShareDefine.TYPE_Price_BourseMoney, min(value, updPlayerGold))
        addDataDict["BourseMoney"] = GetMoney(curPlayer, ShareDefine.TYPE_Price_BourseMoney)
        
    elif priceType == IPY_GameWorld.TYPE_Price_Gold_Paper:
@@ -3031,8 +2972,6 @@
            return
        updValue = min(ChConfig.Def_UpperLimit_DWord, curCurrency + value)
        SetPlayerCurrency(curPlayer, priceType, updValue)
        if priceType == ShareDefine.TYPE_Price_RealmPoint:
            PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_GetRealmPoint, value)
    else:
        GameWorld.ErrLog("金钱类型错误, priceType=%s,value=%s,giveType=%s" % (priceType, value, giveType), curPlayer.GetPlayerID())
        DataRecordPack.DR_GiveMoneyError(curPlayer, priceType, value, giveType, addDataDict)
@@ -3064,9 +3003,13 @@
    # 除钻石及绑钻外,未指定操作类型的不记录
    
    PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_GetMoney, value, [priceType])
    PlayerActivity.AddDailyTaskValue(curPlayer, ChConfig.DailyTask_GetMoney, value, [priceType])
    PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_OSAGetMoney, value, [priceType])
    
    if priceType == ShareDefine.TYPE_Price_FamilyCoin:
        PlayerFamily.AddFamilyContrib(curPlayer, value) # 公会币同步增加公会贡献
    elif priceType == ShareDefine.TYPE_Price_OSAPoint:
        OpenServerActivity.AddOSACelebrationPoint(curPlayer, value)
        
    if priceType not in [IPY_GameWorld.TYPE_Price_Gold_Money, IPY_GameWorld.TYPE_Price_Gold_Paper, ShareDefine.TYPE_Price_PayCoin] \
        and giveType == ChConfig.Def_GiveMoney_Unknown:
@@ -3086,60 +3029,7 @@
        GameWorld.Log("记录获得代币: eventName=%s,value=%s,nowMoney=%s,payCoinDay=%s,orderInfo=%s" % (eventName, value, nowMoney, payCoinDay, orderInfo), playerID)
        GameWorld.AddPlayerRec(playerID, ShareDefine.Def_PlayerRecType_PayCoin, [1, value, nowMoney, payCoinDay], orderInfo, 1)
        
    if addDataDict.get("GiveItemEvent") == "Treasure":
        GameWorld.DebugLog("BT版本寻宝给货币暂时不记录流向: %s,priceType=%s,price=%s" % (eventName, priceType, value))
    else:
        DataRecordPack.DR_GiveMoney(curPlayer, eventName, priceType, value, addDataDict)
        EventReport.WriteEvent_virtual_resource(curPlayer, priceType, giveType, 1, value,
                                                ShareDefine.Def_UserAction_Money_Get, addDataDict)
#===============================================================================
#    reason_name = "Unknown" if not giveType else giveType
#    eventName = reason_name
#    if giveType in ChConfig.Def_GetType_Dict:
#        eventName, reason_name = ChConfig.Def_GetType_Dict[giveType][:2]
#    if isinstance(addDataDict, dict) and ChConfig.Def_Give_Reason_SonKey in addDataDict:
#        reasonSon = addDataDict[ChConfig.Def_Give_Reason_SonKey]
#    else:
#        reasonSon = reason_name
#    # 统一格式: 「获得组:获得原因」
#    reason_name = "%s:%s" % (reason_name, reasonSon)
#
#    # 流向用 eventName
#    if eventName:
#        DataRecordPack.DR_GiveMoney(curPlayer, eventName, priceType, value, addDataDict)
#
#    if priceType == IPY_GameWorld.TYPE_Price_Gold_Money:
#        playerName = curPlayer.GetPlayerName()
#        eventParam = "RoleID=%s,AddGold=%s,LeftGold=%s,AddDataDict=%s" \
#                    % (playerName, value, curPlayer.GetGold(), addDataDict)
#        EventReport.EventReport(ShareDefine.Def_UserAction_GameMakeGold, eventParam, curPlayer)
#
#    elif priceType == IPY_GameWorld.TYPE_Price_Gold_Paper:
#        playerName = curPlayer.GetPlayerName()
#        eventParam = "RoleID=%s,AddGoldPaper=%s,LeftGoldPaper=%s,AddDataDict=%s" \
#                        % (playerName, value, curPlayer.GetGoldPaper(), addDataDict)
#        EventReport.EventReport(ShareDefine.Def_UserAction_GameMakeGoldPaper, eventParam, curPlayer)
#
#
#    if priceType == IPY_GameWorld.TYPE_Price_Gold_Money: #and eventName != "CoinToGold":
#        EventReport.WriteEvent_virtual_reward(curPlayer, value, reason_name)
#    # 二级货币产出记录, 暂只记录绑钻、金币
#    elif priceType in [IPY_GameWorld.TYPE_Price_Gold_Paper, IPY_GameWorld.TYPE_Price_Silver_Money]:
#        EventReport.WriteEvent_virtual_resource(curPlayer, priceType, reason_name, 1, value,
#                                                ShareDefine.Def_UserAction_Money_Get)
#
#    # 自定义记录
#    priceNameDict = {IPY_GameWorld.TYPE_Price_Gold_Money:["钻石", "GetGold", 0],
#                     IPY_GameWorld.TYPE_Price_Gold_Paper:["绑钻", "GetGoldPaper", 0],
#                     IPY_GameWorld.TYPE_Price_Silver_Money:["金币", "GetSilver", ChConfig.Def_DRRecord_Min_Silver],
#                     }
#    if priceType in priceNameDict:
#        typeName, moneyFuncName, minPrice = priceNameDict[priceType]
#        if minPrice <= 0 or value >= minPrice:
#            EventReport.WriteEvent_give_money(curPlayer, reason_name, typeName, value, getattr(curPlayer, moneyFuncName)())
#===============================================================================
    DataRecordPack.DR_GiveMoney(curPlayer, eventName, priceType, value, addDataDict)
    return
@@ -3484,8 +3374,7 @@
                isNotifyServer = False
            
            curPlayer.SetLV(curLV, isNotifyServer)
            #EventReport.WriteEvent_level_up(curPlayer)
            # 记录玩家升级
            DataRecordPack.DR_PlayerUpgrade(curPlayer, curPlayer.GetLV(), GetPlayerTotalExp(curPlayer), lvUpNeedExp)
            DataRecordPack.Cache_FightPowerChangeInfo(curPlayer, ChConfig.PowerDownType_LVUP, {'lv':curLV})
@@ -3512,10 +3401,8 @@
            #aftFreePoint = curPlayer.GetFreePoint()
            if aftLV > befLV:
                curPlayer.SetLV(aftLV, False) # 这里不再通知GameServer
                PlayerTongTianLing.AddTongTianTaskValue(curPlayer, ChConfig.TTLTaskType_LVUp, aftLV - befLV)
                #PlayerTongTianLing.AddTongTianTaskValue(curPlayer, ChConfig.TTLTaskType_LVUp, aftLV - befLV)
                PlayerTask.UpdTaskValue(curPlayer, ChConfig.TaskType_LV)
                PlayerSuccess.UptateSuccessProgress(curPlayer, ShareDefine.SuccType_HeroLV, aftLV)
                PlayerActivity.DoAddActivityByLV(curPlayer, befLV, aftLV)
                
            #if aftFreePoint > befFreePoint:
            #    curPlayer.SetFreePoint(aftFreePoint)
@@ -3531,11 +3418,6 @@
            #if curPlayer.GetMaxMP() > 0:
            #    curPlayer.SetMP(curPlayer.GetMaxMP())
            
            # 记录开服活动冲级数据
            #OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_LV, curPlayer.GetLV())
            #神秘限购
            FunctionNPCCommon.MysticalLimitShopOpen(curPlayer, befLV, aftLV)
        #不需要做升级任务, 设置玩家经验
        SetPlayerTotalExp(curPlayer, curTotalExp) 
        return
@@ -4078,14 +3960,6 @@
    
    if curPlayerAction == IPY_GameWorld.paPreparing:
        DoExitPreparing(curPlayer)
    #---玩家交通工具处理---
    curPlayerVehicle = curPlayer.GetPlayerVehicle()
    #玩家骑马中, 下马
    if curPlayerVehicle == IPY_GameWorld.pvHorse:
        #执行下马逻辑
        PlayerHorse.PlayerRideHorseDown(curPlayer, False)
        
    #---其他系统处理---
    
@@ -4196,7 +4070,6 @@
def Sync_ExpRateChange(curPlayer):
    totalExpRate = GetPlayerExpRate(curPlayer)
    fightExpRate = curPlayer.GetFightExpRate() # 系统及功能累加
    fightExpRate += PlayerGoldInvest.GetAddFightExpRate(curPlayer)
    
    actExpRateInfo = PyGameData.g_operationActionDict.get(ShareDefine.OperationActionName_ExpRate, {})# 多倍经验活动加成
    if actExpRateInfo.get(ShareDefine.ActKey_State):
@@ -4251,6 +4124,15 @@
                totalExpRate -= effExpRate
                
    return totalExpRate
##外观额外数据:  其他 * 1000 + 坐骑外观
def GetHorseSkinID(curPlayer): return GameWorld.GetValue(curPlayer.GetEquipShowSwitch(), 3, 3)
def SetHorseSkinID(curPlayer, horseSkinID):
    showValue = curPlayer.GetEquipShowSwitch()
    updShowValue = GameWorld.SetValue(showValue, 3, 3, min(horseSkinID, 999))
    curPlayer.SetEquipShowSwitch(updShowValue)
    GameWorld.DebugLog("使用坐骑外观: horseSkinID=%s,showValue=%s,updShowValue=%s" % (horseSkinID, showValue, updShowValue))
    return
#===============================================================================
#---玩家扩展字段---
@@ -4347,14 +4229,31 @@
def IsMainLevelPass(curPlayer, lvID):
    ## 判断玩家是否过关某个主线关卡ID
    # @param lvID: 关卡唯一ID,与策划约定好 = 章节*100+关卡编号
    passChapterID, passLevelNum, _ = GetMainLevelPassInfo(curPlayer)
    passValue = passChapterID * 100 + passLevelNum # 因为pass的记录是带波数的,即当前关卡boss还没过关,所以只有大于该记录值的才算过关
    return passValue > lvID
    return GetPassMainLevelID(curPlayer) >= lvID
def GetPassMainLevelID(curPlayer):
    ## 获取已过关主线关卡ID
    passChapterID, passLevelNum, _ = GetMainLevelPassInfo(curPlayer) # 当前过关进度包含波,所以需要取上一关的
    passLVID = 0 # 关卡唯一ID,与策划约定好 = 章节*100+关卡编号
    if passLevelNum > 1:
        preLevelNum = passLevelNum - 1
        passLVID = passChapterID * 100 + preLevelNum
        GameWorld.DebugLog("非某章节的第1关直接减1即可: passLVID=%s,passChapterID=%s,passLevelNum=%s" % (passLVID, passChapterID, passLevelNum))
    elif passChapterID > 1: # 取上一章节
        preChapterID = passChapterID - 1
        levelDataList = IpyGameDataPY.GetIpyGameDataByCondition("MainLevel", {"ChapterID":preChapterID}, True)
        levelCount = len(levelDataList) if levelDataList else 0
        if levelCount:
            passLVID = preChapterID * 100 + levelCount
        GameWorld.DebugLog("取上一章节作为已过关关卡: passLVID=%s,preChapterID=%s,levelCount=%s,passChapterID=%s,passLevelNum=%s"
                           % (passLVID, preChapterID, levelCount, passChapterID, passLevelNum))
    return passLVID
## 主线关卡过关进度值 = 章节*10000+关卡编号*100+第x波
def GetMainLevelPassValue(curPlayer): return curPlayer.GetExAttr1()
def SetMainLevelPassValue(curPlayer, value): curPlayer.SetExAttr1(value, False, False) # 不通知GameServer
def SetMainLevelPassValue(curPlayer, value):
    curPlayer.SetExAttr1(value, False, False) # 不通知GameServer
    GameFuncComm.DoFuncOpenLogic(curPlayer)
def SetMainLevelPassInfo(curPlayer, chapterID, levelNum, wave=0):
    ## 设置主线关卡过关进度
    # @param chapterID: 章节ID
@@ -4363,9 +4262,14 @@
    value = ComMainLevelValue(chapterID, levelNum, wave)
    SetMainLevelPassValue(curPlayer, value)
    if wave == 0:
        lvID = chapterID * 100 + levelNum
        lvID = GetPassMainLevelID(curPlayer)
        PlayerBillboard.UpdatePlayerBillboard(curPlayer, ShareDefine.Def_BT_MainLevel, lvID)
        PlayerTask.UpdTaskValue(curPlayer, ChConfig.TaskType_MainLevel)
        PlayerSuccess.UptateSuccessProgress(curPlayer, ShareDefine.SuccType_MainLevel, lvID)
        PlayerSuccess.UptateSuccessProgress(curPlayer, ShareDefine.SuccType_OSAMainLevel, lvID)
        if OpenServerActivity.GetOSAState(curPlayer, ShareDefine.Def_BT_OSA_MainLevel) == 1:
            PlayerBillboard.UpdatePlayerBillboard(curPlayer, ShareDefine.Def_BT_OSA_MainLevel, lvID)
        DataRecordPack.DR_MainLevelPass(curPlayer, lvID)
    return value
def GetMainLevelPassInfo(curPlayer):
    ## 获取主线关卡过关进度信息
@@ -4406,9 +4310,16 @@
    wave = value % 100
    return chapterID, levelNum, wave
## 额外记录最后一次接到的主线任务ID,仅接到新任务时更新即可,可方便用于后台统计或其他判断
def GetMainTaskID(curPlayer):return curPlayer.GetExAttr20()
def SetMainTaskID(curPlayer, value): curPlayer.SetExAttr20(value)
## 获取佩戴的称号ID
def GetTitleID(curPlayer): return curPlayer.GetExAttr3()
def SetTitleID(curPlayer, titleID): curPlayer.SetExAttr3(titleID, False, False)
def SetTitleID(curPlayer, titleID):
    curPlayer.SetExAttr3(titleID, False, False)
    PlayerFamily.RefreshFamilyMember(curPlayer)
    return
## 协助目标玩家ID
def GetAssistTagPlayerID(curPlayer): return 0
@@ -4514,32 +4425,37 @@
    ## 获取玩家在本地图中的境界难度层级,必须在境界地图且有选择境界难度才算,否则为默认0;该难度值同时也是视野层级
    return 0
##玩家退出仙盟信息: 主动离开次数*100 + 被踢次数*10  + 最后一次是否主动离开的
def GetLeaveFamilyInfo(curPlayer):
    # @return: 主动离开次数, 最后一次是否主动离开的
    value = curPlayer.GetExAttr12()
    leaveCnt = value / 100
    kickedCnt = value % 100 / 10
    lastVoluntarily = value % 10
    return leaveCnt, kickedCnt, lastVoluntarily
def SetLeaveFamilyInfo(curPlayer, leaveCnt, kickedCnt, lastVoluntarily):
    value = min(9, leaveCnt) * 100 + min(9, kickedCnt) * 10 + lastVoluntarily
    curPlayer.SetExAttr12(value) # IPY_PlayerDefine.CDBPlayerRefresh_ExAttr12
    GameWorld.DebugLog("    SetLeaveFamilyInfo: value=%s" % (value))
    return value
##玩家离开仙盟时间(主动或被踢都算)
def GetLeaveFamilyTimeEx(curPlayer):return curPlayer.GetExAttr19()
def SetLeaveFamilyTimeEx(curPlayer, value):
    curPlayer.SetExAttr19(value)
    #curPlayer.SendGameServerRefreshState(ShareDefine.CDBPlayerRefresh_ExAttr19, value, 0)
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_ExAttr19, value, 0, False)
    return
    return value
##总战斗力,支持超过20E = 各模块战力总和
def GetFightPower(curPlayer): return curPlayer.GetFightPowerEx() * ChConfig.Def_PerPointValue + curPlayer.GetFightPower()
def SetFightPower(curPlayer, value):
    beforeFightPower = GetFightPower(curPlayer)
    curPlayer.SetFightPower(value % ChConfig.Def_PerPointValue, value / ChConfig.Def_PerPointValue, False) # 不通知GameServer bNotifyGameServer False
    if value < beforeFightPower:
        DataRecordPack.DR_FightPowerChangeInfo(curPlayer, beforeFightPower)
    highestFightPower = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FightPower_Highest)
    highestFightPower += curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FightPower_HighestEx) * ChConfig.Def_PerPointValue
    if value > highestFightPower:
        highestFightPower = value
        NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FightPower_Highest, highestFightPower % ChConfig.Def_PerPointValue)
        NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FightPower_HighestEx, highestFightPower / ChConfig.Def_PerPointValue)
    GameWorld.DebugLog("总战力: %s, 历史最高战力: %s, beforeFightPower=%s" % (value, highestFightPower, beforeFightPower), curPlayer.GetPlayerID())
    #if value < beforeFightPower:
    #    DataRecordPack.DR_FightPowerChangeInfo(curPlayer, beforeFightPower)
    GameWorld.DebugLog("总战力: %s, beforeFightPower=%s" % (value, beforeFightPower), curPlayer.GetPlayerID())
    #PlayerBillboard.UpdatePlayerFPTotalBillboard(curPlayer)
    # 记录开服活动数据
    #OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_FightPower, totalFightPower)
    #if beforeFightPower != totalFightPower:
    #    CrossPlayerData.OnPlayerFightPowerChange(curPlayer)
    return
@@ -4574,14 +4490,14 @@
# @return 无意义
def SetSight(curPlayer, sight):
    #外挂号 视野验证
    if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_AutoCheckHack_State) \
                                      == ChConfig.Def_AutoCheck_State_Danger:
        if curPlayer.GetSight() > sight:
            curPlayer.SetSight(sight)
        return
#    if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_AutoCheckHack_State) \
#                                      == ChConfig.Def_AutoCheck_State_Danger:
#
#        if curPlayer.GetSight() > sight:
#            curPlayer.SetSight(sight)
#
#        return
#
    curPlayer.SetSight(sight)
##获取攻击间隔
@@ -4591,9 +4507,9 @@
    atkInterval = curPlayer.GetAtkInterval()
    
    #外挂号 攻击间隔验证
    if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_AutoCheckHack_State) \
                                      == ChConfig.Def_AutoCheck_State_Danger:
        atkInterval *= 100
    #if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_AutoCheckHack_State) \
    #                                  == ChConfig.Def_AutoCheck_State_Danger:
    #    atkInterval *= 100
    
    return atkInterval
@@ -4614,15 +4530,7 @@
    if value == curZhenQi:
        #真气值没有改变
        return False
    #===============================================================================================
    # if isSysMsg:
    #    #GeRen_chenxin_254483:获得真气值XX点
    #    NotifyCode(curPlayer, "GeRen_chenxin_254483", [addValue])
    #===============================================================================================
    SetZhenQi(curPlayer, value)
    #EventReport.WriteEvent_add_zhenqi(curPlayer, eventName, eventData, addValue, value)
    return True
@@ -4640,13 +4548,7 @@
    if value < 0:
        GameWorld.ErrLog("curZhenQi = %s, lostValue = %s" % (curZhenQi, lostValue))
        return False
    #GeRen_chenxin_365899:消耗真气值XX点.
    #NotifyCode(curPlayer, "GeRen_chenxin_365899", [lostValue])
    SetZhenQi(curPlayer, value)
    #EventReport.WriteEvent_lost_zhenqi(curPlayer, eventName, eventData, lostValue, value)
    return True
## SP真气值 - 暂废弃 ExAttr7、ExAttr8 改为专精选择通知,用于前端表现其他玩家的不同专精特效
@@ -4887,7 +4789,7 @@
## 设置玩家字典值, 存库
def NomalDictSetProperty(curPlayer, key, value, dType=0):
    if CrossPlayerData.IsNeedProcessCrossPlayer(curPlayer) and key not in \
        [ChConfig.Def_PDict_FightPower_Total, ChConfig.Def_PDict_FightPower_TotalEx, ChConfig.Def_PlayerKey_CrossRegisterMap]:
        [ChConfig.Def_PlayerKey_CrossRegisterMap]:
        playerID = curPlayer.GetPlayerID()
        changeDict = PyGameData.g_crossPlayerDictChangeInfo.get(playerID, {})
        changeDict[(key, dType)] = value