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
@@ -25,13 +24,13 @@
import ItemCommon
import ReadChConfig
import BuffSkill
import PetControl
import OperControlManager
import PlayerFamily
import ShareDefine
import PlayerViewCache
import PlayerBillboard
import GameServerRefresh
import IPY_PlayerDefine
import IPY_GameWorld
import ChPyNetSendPack
import NetPackCommon
@@ -39,26 +38,21 @@
import PlayerPrestigeSys
import FBCommon
import PassiveBuffEffMng
import EventReport
import PlayerSuccess
import ItemControler
import GameFuncComm
import IpyGameDataPY
import PyGameData
import PlayerFeastTravel
import PlayerActTurntable
import PlayerCostRebate
import OpenServerActivity
import PlayerActLunhuidian
import GY_Query_CrossRealmReg
import FunctionNPCCommon
import PlayerGoldInvest
import CrossRealmPlayer
import CrossPlayerData
import PlayerActivity
import ChNetSendPack
import PlayerState
import PlayerBeauty
import PlayerOnline
import PlayerTask
import PlayerMail
import TurnAttack
import ChPlayer
import GameObj
@@ -234,7 +228,7 @@
#  @remarks 
def FamilyNotify(familyID, msgMark, msgParamList=[]):
    #GameWorld.GetPlayerManager().BroadcastCountry_NotifyCode(0, familyID, msgMark, __GetNotifyCodeList(msgParamList))
    PlayerFamily.NotifyAllFamilyMemberMsg(familyID, msgMark, msgParamList)
    #PlayerFamily.NotifyAllFamilyMemberMsg(familyID, msgMark, msgParamList)
    return
#---------------------------------------------------------------------
@@ -414,24 +408,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开始
@@ -635,10 +611,6 @@
#    curPlayer.SetIsConfronting(False)
#    #通知客户端 //0进入;其他退出
#    curPlayer.View_PlayerBattle(0, 0, 1)
#
#    #清除战宠仇恨
#    PetControl.ClearFightPetAngry(curPlayer)
#
#    #添加测试信息
#    GameWorld.GodLog(curPlayer, '退出战斗对峙成功')
    return
@@ -899,8 +871,6 @@
    PassiveBuffEffMng.OnPlayerLeaveMap(curPlayer)
    #杀死所有召唤的灵
    KillPlayerSummonNPC(curPlayer)
    #召唤回出战的宠物
    PetControl.ReCallFightPet(curPlayer)
    curPlayer.DeleteMirror()
    return
@@ -931,19 +901,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 时间戳
@@ -991,8 +948,6 @@
    
    PlayerSuccess.FinishDelayAddSuccessProgress(curPlayer, tick)
    playerID = curPlayer.GetPlayerID()
    if not isDisconnect:
        CrossPlayerData.ClearCrossSyncDataCache(curPlayer)
    #清除地图玩家缓存
    PyGameData.g_playerReqEnterFBEx.pop(playerID, None)
    #移除地图缓存的境界难度玩家ID信息
@@ -1000,6 +955,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 玩家实例
@@ -1088,9 +1101,7 @@
# @return 返回值无意义
# @remarks 玩家离开服务器
def PlayerLeaveServer(curPlayer, tick):
    #宠物下线逻辑, 这里要进行排行榜, 优先做, 避免玩家光环等属性影响宠物属性失效
    PetControl.DoLogic_PetInfo_OnLeaveServer(curPlayer, tick)
    RecordTodayOnlineTime(curPlayer)
    #清除下线消失的buff, 在更新排行榜之前
    __DisconnectClearBuff(curPlayer, tick)
    
@@ -1110,45 +1121,11 @@
    
    #玩家下线/玩家切换地图公用逻辑
    __PlayerLeaveServerLogic(curPlayer, tick, True)
    #骑马玩家下线逻辑
    __RidingHorsePlayerDisconnect(curPlayer)
    #召唤兽死亡
    KillPlayerSummonNPC(curPlayer)
    #更新从本地图离线信息
    PyGameData.g_disconnectPlayer[curPlayer.GetPlayerID()] = [tick, curPlayer.GetPosX(), curPlayer.GetPosY()]
    GameWorld.DebugLog("玩家从本地图离线: %s" % PyGameData.g_disconnectPlayer, curPlayer.GetPlayerID())
    GameWorld.DebugLog("玩家从本地图离线", curPlayer.GetPlayerID())
    return
def GetPlayerLeaveServerTick(playerID):
    # 获取玩家从本地图中离线时的tick, 最大支持1小时, 如果有需要大于1小时的请调整超时限制
    # 注: 返回值为0时,只能代表玩家不是在本地图离线1小时内,并不能代表玩家当前是否在线状态,可能在其他地图
    if playerID not in PyGameData.g_disconnectPlayer:
        return 0
    return PyGameData.g_disconnectPlayer[playerID][0]
def GetPlayerLeaveServerPos(playerID):
    # 获取玩家从本地图中离线时的坐标
    # 注:使用本函数时,一定要先使用函数 GetPlayerLeaveServerTick 确保是从本地图中离线的才可使用
    # @return: posX, posY
    if playerID not in PyGameData.g_disconnectPlayer:
        return 0, 0
    return PyGameData.g_disconnectPlayer[playerID][1:3]
def RemoveTimeoutLeaveServerPlayerInfo(tick):
    # 暂定每天凌晨5点执行一次
    for playerID, leaveInfo in PyGameData.g_disconnectPlayer.items():
        leaveTick = leaveInfo[0]
        if tick - leaveTick > 3600000: # 清除超时1小时的记录
            PyGameData.g_disconnectPlayer.pop(playerID)
    return
def RemoveLeaveServerPlayerInfo(playerID):
    # 上线移除在本地图下线的记录
    if playerID in PyGameData.g_disconnectPlayer:
        PyGameData.g_disconnectPlayer.pop(playerID)
        GameWorld.DebugLog("进入本地图,移除上次在本地图离线记录!", playerID)
    return
##清除回收战
# @param None
@@ -1181,8 +1158,6 @@
    
    FBLogic.DoPlayerChangeMapLogic(curPlayer, tick)
    #summonList = list()
    #召回宠物
    PetControl.ReCallFightPet(curPlayer)
    #1. 删除自己不需要的召唤兽(火焰之灵等)
    #必须用while, 因为在循环中要删除
    # 召唤兽切地图不带过去
@@ -1238,7 +1213,6 @@
    
    GameWorld.Log("PlayerLeaveFB...", curPlayer.GetPlayerID())
    if GameWorld.IsCrossServer():
        CrossRealmPlayer.PlayerExitCrossServer(curPlayer)
        return
    
    funcLineID = 0
@@ -1316,74 +1290,6 @@
    return
#---------------------------------------------------------------------
def PlayerEnterCrossServer(curPlayer, mapID, lineID):
    playerID = curPlayer.GetPlayerID()
    GameWorld.Log("玩家请求进入跨服地图: mapID=%s,lineID=%s" % (mapID, lineID), playerID)
    if mapID not in ChConfig.Def_CrossMapIDList:
        return
    tick = GameWorld.GetGameWorld().GetTick()
    lastRequestTick = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_RequestEnterCrossServerTick)
    if lastRequestTick and tick - lastRequestTick < 5000:
        GameWorld.DebugLog("    请求进入跨服CD中!", playerID)
        NotifyCode(curPlayer, "RequestEnterCrossServerCD")
        return
    crossRegisterMap = curPlayer.NomalDictGetProperty(ChConfig.Def_PlayerKey_CrossRegisterMap)
    if crossRegisterMap:
        GameWorld.ErrLog("跨服已经在上传数据,不重复提交!crossRegisterMap=%s,mapID=%s" % (crossRegisterMap, mapID), playerID)
        return
    if GameWorld.IsCrossServer():
        GameWorld.DebugLog("跨服服务器不允许该操作!")
        return
    if GetCrossMapID(curPlayer):
        GameWorld.ErrLog("玩家当前为跨服状态,不允许再次请求进入跨服!", curPlayer.GetPlayerID())
        return
    if not CrossRealmPlayer.IsCrossServerOpen():
        NotifyCode(curPlayer, "CrossMatching18")
        return
    if GameObj.GetHP(curPlayer) <= 0:
        NotifyCode(curPlayer, "CrossMap4")
        return
    if PlayerState.IsInPKState(curPlayer):
        NotifyCode(curPlayer, "SingleEnterPK", [mapID])
        return
    fbIpyData = FBCommon.GetFBIpyData(mapID)
    if fbIpyData:
        fbLineIpyData = FBCommon.GetFBLineIpyData(mapID, lineID, False)
        if not fbLineIpyData:
            GameWorld.DebugLog("副本表找不到副本对应功能线路!mapID=%s,lineID=%s" % (mapID, lineID))
            return
        ret = FBCommon.CheckCanEnterFBComm(curPlayer, mapID, lineID, fbIpyData, fbLineIpyData)
        if ret != ShareDefine.EntFBAskRet_OK:
            return
        if not FBLogic.OnEnterFBEvent(curPlayer, mapID, lineID, tick):
            GameWorld.DebugLog("    OnEnterFBEvent False!", curPlayer.GetPlayerID())
            NotifyCode(curPlayer, "SingleEnterDefaul")
            return
    # 需要动态分布线路的地图,发送到跨服服务器进行分配
    if mapID in ChConfig.Def_CrossDynamicLineMap:
        extendInfo = {}
        msgDict = {"PlayerID":curPlayer.GetPlayerID(), "MapID":mapID, "FuncLineID":lineID, "LV":curPlayer.GetLV()}
        if extendInfo:
            msgDict.update(extendInfo)
        GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_EnterFB, msgDict)
    else:
        isSend = GY_Query_CrossRealmReg.RegisterEnterCrossServer(curPlayer, mapID, lineID=lineID)
        if not isSend:
            return
    curPlayer.SetDict(ChConfig.Def_PlayerKey_RequestEnterCrossServerTick, tick)
    return
##玩家进入副本
# @param curPlayer 玩家实例
# @param mapID 地图ID
@@ -2483,17 +2389,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)
##玩家是否有钱款
@@ -2775,14 +2676,13 @@
    elif type_Price == IPY_GameWorld.TYPE_Price_Silver_Paper:
        __PayMoneyAfterBySilverPaper(curPlayer, price)
        
    #转盘活动
    PlayerActTurntable.OnPlayerUseGold(curPlayer, 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)
        NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntExp, unXiantaoCntExp + price)
        # 累加最后一档品质装备保底
        lastColorEquipLucky = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_LastColorEquipLucky)
        NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_LastColorEquipLucky, lastColorEquipLucky + price)
        # 累加未结算战锤 - 装备
        AddUnXiantaoCntEquip(curPlayer, price)
        # 累加未结算战锤 - 战利品
@@ -2793,15 +2693,17 @@
            for itemID, upperCnt in DailyBootyUpperList:
                if upperCnt <= 0:
                    continue
                upperCnt = GetBootyUpper(curPlayer, itemID, upperCnt)
                if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BootyDropToday % itemID) >= upperCnt:
                    continue
                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
        
    #轮回殿
    PlayerActLunhuidian.AddLunhuidianValue(curPlayer, PlayerActLunhuidian.AwardType_PayMoney, type_Price, price)
    unitPrice = price if quantity == 1 else int(math.ceil(price * 1.0 / quantity)) # 单价
    #reason_name = "Unknown" if not costType else costType
    reason_name = costType
@@ -2836,106 +2738,36 @@
        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
def GetBootyUpper(curPlayer, itemID, baseUpper):
    ## 战利品掉落上限
    dropUpper = baseUpper
    addPer = 0
    addPer += PlayerBeauty.GetBeautyEffInfo(curPlayer, PlayerBeauty.EffType_BootyPer)[0] # 战利品上限提高百分比
    # 其他功能增加上限,可扩展
    if addPer:
        dropUpper = int(baseUpper * (100 + addPer) / 100.0)
        GameWorld.DebugLogEx("提高战利品掉落上限: itemID=%s,baseUpper=%s,addPer=%s,dropUpper=%s", itemID, baseUpper, addPer, dropUpper)
    return dropUpper
## 付款以后后续操作(金子)
#  @param curPlayer 玩家实例
#  @param price ,货币价格
#  @return None
def __PayMoneyAfterByGoldMoney(curPlayer, type_Price, price, costType, infoDict, costVIPGold):
    # 充值活动玩家消耗元宝处理
    #PlayerGoldAction.PlayerUseGold(curPlayer, price)
    # 消费返利
    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
@@ -2988,8 +2820,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:
@@ -3058,9 +2891,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:
@@ -3080,60 +2917,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
@@ -3478,8 +3262,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})
@@ -3506,8 +3289,8 @@
            #aftFreePoint = curPlayer.GetFreePoint()
            if aftLV > befLV:
                curPlayer.SetLV(aftLV, False) # 这里不再通知GameServer
                #PlayerTongTianLing.AddTongTianTaskValue(curPlayer, ChConfig.TTLTaskType_LVUp, aftLV - befLV)
                PlayerTask.UpdTaskValue(curPlayer, ChConfig.TaskType_LV)
                ChPlayer.OnPlayerBaseInfoChange(curPlayer, IPY_PlayerDefine.CDBPlayerRefresh_LV) # 等级
                
            #if aftFreePoint > befFreePoint:
            #    curPlayer.SetFreePoint(aftFreePoint)
@@ -3523,11 +3306,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
@@ -3723,9 +3501,6 @@
        #杀死所有召唤的灵
        KillPlayerSummonNPC(curPlayer)
        #召唤回出战的宠物
        PetControl.ReCallFightPet(curPlayer)
        
        #清空使用技能记录
        curPlayer.ClearUseSkillRec()
@@ -4070,14 +3845,6 @@
    
    if curPlayerAction == IPY_GameWorld.paPreparing:
        DoExitPreparing(curPlayer)
    #---玩家交通工具处理---
    curPlayerVehicle = curPlayer.GetPlayerVehicle()
    #玩家骑马中, 下马
    if curPlayerVehicle == IPY_GameWorld.pvHorse:
        #执行下马逻辑
        PlayerHorse.PlayerRideHorseDown(curPlayer, False)
        
    #---其他系统处理---
    
@@ -4149,7 +3916,7 @@
    
    if crossActName and crossActIDKey:
        playerActID = curPlayer.NomalDictGetProperty(crossActIDKey)
        actInfo = CrossRealmPlayer.GetPlayerCrossActInfo(curPlayer, crossActName)
        actInfo = {}#GetPlayerCrossActInfo(curPlayer, crossActName)
        actID = actInfo.get(ShareDefine.ActKey_ID, 0)
        cfgID = actInfo.get(ShareDefine.ActKey_CfgID, 0)
        state = actInfo.get(ShareDefine.ActKey_State, 0)
@@ -4188,7 +3955,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):
@@ -4243,6 +4009,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
#===============================================================================
#---玩家扩展字段---
@@ -4339,14 +4114,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
@@ -4355,10 +4147,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):
    ## 获取主线关卡过关进度信息
@@ -4399,11 +4195,15 @@
    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)
    PlayerFamily.RefreshFamilyMember(curPlayer)
    ChPlayer.OnPlayerBaseInfoChange(curPlayer, IPY_PlayerDefine.CDBPlayerRefresh_ExAttr3) # 称号
    return
## 协助目标玩家ID
@@ -4419,11 +4219,8 @@
def GetFBFuncLineID(curPlayer): return 0
## 跨服状态所在地图ID: 0-非跨服状态,非0-跨服状态对应的地图ID
def GetCrossMapID(curPlayer): return curPlayer.GetExAttr5()
def GetCrossMapID(curPlayer): return 0
def SetCrossMapID(curPlayer, value):
    curPlayer.SetExAttr5(value, False, True)
    if not value:
        CrossPlayerData.ClearCrossSyncDataCache(curPlayer)
    return
## 铜钱点, 支持铜钱超20亿
@@ -4485,19 +4282,7 @@
    return
## 玩家所属服务器组ID
def GetPlayerServerGroupID(curPlayer): return curPlayer.GetExAttr13()
def UpdPlayerServerGroupID(curPlayer):
    # 更新自己的服务器组ID, 跨服服务器不处理
    if GameWorld.IsCrossServer():
        return
    serverGroupID = GameWorld.GetServerGroupID()
    if not serverGroupID:
        return
    playerServerGroupID = curPlayer.GetExAttr13()
    if playerServerGroupID != serverGroupID:
        curPlayer.SetExAttr13(serverGroupID, False, True)
        GameWorld.DebugLog("更新玩家所属服务器组ID: serverGroupID=%s" % serverGroupID)
    return
def GetPlayerServerGroupID(curPlayer): return 0
# 境界难度等级
def GetRealmDifficulty(curPlayer): return 0
@@ -4537,21 +4322,11 @@
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())
    #PlayerBillboard.UpdatePlayerFPTotalBillboard(curPlayer)
    # 记录开服活动数据
    #OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_FightPower, totalFightPower)
    #if beforeFightPower != totalFightPower:
    #    CrossPlayerData.OnPlayerFightPowerChange(curPlayer)
    #if value < beforeFightPower:
    #    DataRecordPack.DR_FightPowerChangeInfo(curPlayer, beforeFightPower)
    GameWorld.DebugLog("总战力: %s, beforeFightPower=%s" % (value, beforeFightPower), curPlayer.GetPlayerID())
    if beforeFightPower != value:
        ChPlayer.OnPlayerBaseInfoChange(curPlayer, IPY_PlayerDefine.CDBPlayerRefresh_FightPower) # 战力
    return
## 设置模块战斗力,支持超过20E = 模块公式战力 + 技能附加战力 + 其他附加战力
@@ -4584,14 +4359,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)
##获取攻击间隔
@@ -4601,67 +4376,11 @@
    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
##玩家增加真气
# @param curPlayer 玩家
# @param value 增加数值
# @param canOverbrim 可否溢出(默认不行)
# @param isSysMsg 是否系统提示(默认需要)
# @return None
def PlayerAddZhenQi(curPlayer, addValue, canOverbrim=False, isSysMsg=True, eventName="unknown", eventData=""):
    if addValue <= 0:
        return True
    curZhenQi = GetZhenQi(curPlayer)  # 当前真气
    value = curZhenQi + addValue
    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
##玩家减少真气
# @param curPlayer 玩家
# @param lostValue 减少数值
# @return None
def PlayerLostZhenQi(curPlayer, lostValue, eventName="unknown", eventData=""):
    if lostValue <= 0:
        return True
    curZhenQi = GetZhenQi(curPlayer)  # 当前真气
    value = max(0, curZhenQi - lostValue)
    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 改为专精选择通知,用于前端表现其他玩家的不同专精特效
def GetZhenQi(curPlayer): return 0
def SetZhenQi(curPlayer, totalZhenQi): return
#===============================================================================
# #@warning: ExAttr6~ExAttr10, 新增2个布尔默认参数, 是否通知客户端, 是否通知GameServer, 默认值为False
@@ -4896,13 +4615,6 @@
#-------------------------------------------------------------------------------
## 设置玩家字典值, 存库
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]:
        playerID = curPlayer.GetPlayerID()
        changeDict = PyGameData.g_crossPlayerDictChangeInfo.get(playerID, {})
        changeDict[(key, dType)] = value
        PyGameData.g_crossPlayerDictChangeInfo[playerID] = changeDict
    if value == 0:
        curPlayer.NomalDictDelProperty(key, dType)
        return 0