hxp
2019-02-11 cdb5d93a9fd7b32a2ed178feed4eee20ee6502e5
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -64,29 +64,38 @@
import PlayerGameEvent
import EventReport
import PlayerTeHui
import GameLogic_XMZZ
import PlayerGatherSoul
import PlayerSuccess
import PlayerPet
import PlayerGreatMaster
import ItemControler
import GameFuncComm
import PlayerMergeEvent
import IpyGameDataPY
import PlayerRune
import GameLogic_DuJie
import PyGameData
import PlayerMagicWeapon
import GameLogic_SealDemon
import GameLogic_ZhuXianBoss
import PlayerTJG
import PlayerVip
import PlayerRefineStove
import PlayerFamilyTech
import PlayerCostRebate
import GY_Query_CrossRealmReg
import PlayerFairyCeremony
import PlayerNewFairyCeremony
import PlayerCrossRealmPK
import FunctionNPCCommon
import CrossRealmPlayer
import CrossPlayerData
import ChNetSendPack
import EquipZhuXian
import PlayerCoat
import PlayerState
import QuestCommon
import PlayerDogz
import ChPlayer
import GMShell
import random
@@ -113,6 +122,7 @@
    Def_Max_Move_Tick = 5000
 
    if abs(gameWorldTick - clientWorldTick) >= Def_Max_Move_Tick:
        curPlayer.Sync_ClientTick()
        #时间相差过大,可能因网络引起,拉回
        GameWorld.DebugLog("PlayerMoveCheckClientWorldTick -- 服务器tick %s-客户端%s时间相差过大,可能因网络引起,拉回" % (
                            gameWorldTick, clientWorldTick), curPlayer.GetID())
@@ -160,7 +170,7 @@
# notifyCnt 代表广播周围玩家的数量,0为全部广播 -1为指定列表随机, 其他数字为指定指
def PyNotifyAll(curPlayer, sendPack, notifySelf=True, notifyCnt=0):
    if notifyCnt == -1:
        notifyCnt = random.choice((10, 10, 15, 15, 20, 25, 30, 50))
        notifyCnt = 8
    #GameWorld.DebugLog("PyNotifyAll %s"%notifyCnt)
    curPlayer.NotifyAll(sendPack.GetBuffer(), sendPack.GetLength(), notifySelf, notifyCnt)
@@ -227,15 +237,22 @@
#  @param mergeMapInfo 该提示所属的跨服活动地图信息, 主要用于不同子服对应所跨的活动地图ID
#  @return 无返回值
def WorldNotify(country, msgMark, msgParamList=[], lineID=0, mergeMinOSD=-1, mergeMaxOSD=-1, mergeMapInfo=[]):
    # 如果是跨服服务器,则广播子服
    if GameWorld.IsMergeServer():
        sendMsg = str([country, msgMark, msgParamList, lineID, mergeMinOSD, mergeMaxOSD, mergeMapInfo])
        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, 'MergeWorldNotify',
                                                                  sendMsg, len(sendMsg))
        FBNotify(msgMark, msgParamList) # 跨服中的全服广播只在地图中做广播即可,防止不同跨服分区的地图会相互看到广播,体验不好
    else:
        GameWorld.GetPlayerManager().BroadcastCountry_NotifyCode(country, 0, msgMark,
                                                        __GetNotifyCodeList(msgParamList), lineID)
    GameWorld.GetPlayerManager().BroadcastCountry_NotifyCode(country, 0, msgMark, __GetNotifyCodeList(msgParamList), lineID)
    return
def GetCrossWorldNotifyInfo(country, msgMark, msgParamList=[]):
    return {"Type":ShareDefine.CrossNotify_World, "Params":[country, msgMark, msgParamList]}
def GetCrossFamilyNotifyInfo(familyID, msgMark, msgParamList=[]):
    return {"Type":ShareDefine.CrossNotify_Family, "Params":[familyID, msgMark, msgParamList]}
def CrossNotify(serverGroupIDList, crossNotifyList):
    ''' 跨服广播信息提示,支持同步多条,同时也建议多条一起同步
    @param serverGroupIDList: 需要同步到的目标服务器组ID列表
    @param crossNotifyList: 信息提示列表,通过 GetCrossWorldNotifyInfo GetCrossFamilyNotifyInfo 函数获得返回值添加到列表
    '''
    sendMsg = str([serverGroupIDList, crossNotifyList])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "CrossNotify", sendMsg, len(sendMsg))
    return
#---------------------------------------------------------------------
@@ -351,7 +368,7 @@
        return
    
    # 跨服服务器不允许发送邮件
    if GameWorld.IsMergeServer():
    if GameWorld.IsCrossServer():
        return
    
    itemCountDict = {}
@@ -511,6 +528,10 @@
        if GameWorld.GetMap().GetMapID() not in IpyGameDataPY.GetFuncEvalCfg('DungeonDeliver', 1):
            NotifyCode(curPlayer, "Carry_lhs_844170") 
            return False
    if not GameWorld.IsCrossServer() and GetCrossMapID(curPlayer):
        NotifyCode(curPlayer, "CrossMap10")
        return False
    
    return True
@@ -1191,6 +1212,14 @@
    PyGameData.g_needRefreshMapServerState = True # 有玩家离开地图设置需要刷新
    
    PlayerSuccess.FinishDelayAddSuccessProgress(curPlayer, tick)
    if not isDisconnect:
        CrossPlayerData.ClearCrossSyncDataCache(curPlayer)
    #清除地图玩家缓存
    playerID = curPlayer.GetPlayerID()
    PyGameData.g_zhuXianSkillAddPerDict.pop(playerID, None)
    PyGameData.g_zhuXianSkillReducePerDict.pop(playerID, None)
    return
##更新保存玩家在线时间
@@ -1315,15 +1344,17 @@
def GetPlayerLeaveServerTick(playerID):
    # 获取玩家从本地图中离线时的tick, 最大支持1小时, 如果有需要大于1小时的请调整超时限制
    # 注: 返回值为None时,只能代表玩家不是在本地图离线1小时内,并不能代表玩家当前是否在线状态,可能在其他地图
    # 注: 返回值为0时,只能代表玩家不是在本地图离线1小时内,并不能代表玩家当前是否在线状态,可能在其他地图
    if playerID not in PyGameData.g_disconnectPlayer:
        return
        return 0
    return PyGameData.g_disconnectPlayer[playerID][0]
def GetPlayerLeaveServerPos(playerID):
    # 获取玩家从本地图中离线时的坐标
    # 注:使用本函数时,一定要先使用函数 GetPlayerLeaveServerTick 确保是从本地图中离线的才可使用
    # @return: posX, posY
    if playerID not in PyGameData.g_disconnectPlayer:
        return
        return 0, 0
    return PyGameData.g_disconnectPlayer[playerID][1:3]
def RemoveTimeoutLeaveServerPlayerInfo(tick):
@@ -1422,7 +1453,8 @@
    
    FBLogic.DoPlayerChangeMapLogic(curPlayer, tick)
    #summonList = list()
    #召回宠物
    PetControl.ReCallFightPet(curPlayer)
    #1. 删除自己不需要的召唤兽(火焰之灵等)
    #必须用while, 因为在循环中要删除
    # 召唤兽切地图不带过去
@@ -1450,6 +1482,24 @@
#        summonIndex += 1
        
    
    # 更新最后一次离开的非中立常规地图, 从中立地图退出时需要回到该地方,必须在 DoResetWorldPosAndClear 之前更新
    if GameWorld.GetMap().GetMapFBType() == IPY_GameWorld.fbtNull and curPlayer.GetMapID() not in IpyGameDataPY.GetFuncEvalCfg("MapLine", 4):
        mapID = curPlayer.GetMapID()
        posX = curPlayer.GetPosX()
        posY = curPlayer.GetPosY()
        lineID = curPlayer.GetClientLineID()
        NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FromMapID, mapID)
        NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FromPosX, posX)
        NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FromPosY, posY)
        NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FromLineID, lineID)
        GameWorld.DebugLog("最后一次离开的非中立常规地图更新!mapID=%s,lineID=%s,Pos(%s,%s)" % (mapID, lineID, posX, posY))
    else:
        mapID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FromMapID)
        posX = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FromPosX)
        posY = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FromPosY)
        lineID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FromLineID)
        GameWorld.DebugLog("最后一次离开的非中立常规地图不变!mapID=%s,lineID=%s,Pos(%s,%s)" % (mapID, lineID, posX, posY))
    #2. 调用切换地图接口
    curPlayer.DoResetWorldPosAndClear()
    
@@ -1477,11 +1527,37 @@
        GameServerRefresh.Set_PlayerRouteServerInitOK_OnLeaveFB(curPlayer, 1)
        return
    
    GameWorld.Log("PlayerLeaveFB...", curPlayer.GetPlayerID())
    if GameWorld.IsCrossServer():
        CrossRealmPlayer.PlayerExitCrossServer(curPlayer)
        return
    #中立地图回到上一次非中立常规地图
    if curPlayer.GetMapID() in IpyGameDataPY.GetFuncEvalCfg("MapLine", 4):
        mapID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FromMapID)
        posX = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FromPosX)
        posY = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FromPosY)
        lineID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FromLineID)
        # 老号支持,本来就在中立地图的,返回新手村
        if not mapID:
            # {ְҵ:[dataMapID,posX,posY], ...}
            createRoleMapDict = IpyGameDataPY.GetFuncEvalCfg("CreateRoleMap", 1, {})
            if not createRoleMapDict:
                return
            job = curPlayer.GetJob()
            lineID = 0
            if job in createRoleMapDict:
                mapID, posX, posY = createRoleMapDict[job]
            else:
                mapInfoList = createRoleMapDict.values()
                mapID, posX, posY = mapInfoList[0]
    #离开副本
    mapID = curPlayer.GetFromMapID()
    posX = curPlayer.GetFromPosX()
    posY = curPlayer.GetFromPosY()
    else:
        mapID = curPlayer.GetFromMapID()
        posX = curPlayer.GetFromPosX()
        posY = curPlayer.GetFromPosY()
        lineID = curPlayer.GetFromLineID()
    if mapID == curPlayer.GetMapID():
        # 如果在同一张地图, 取DB重生点, 普通地图下线重上时FromMapID会被设置为本地图
        gameMap = GameWorld.GetMap()
@@ -1491,7 +1567,7 @@
    #copyMapID = curPlayer.GetCopyMapID()
    GameWorld.Log("PlayerLeaveFB MapID = %d, PosX = %d, PosY = %d" % (mapID, posX, posY), curPlayer.GetPlayerID())
    
    if GameWorld.GetMap().GetAutoSize() and GameWorld.GetGameWorld().GetMapCopyPlayerManager().GetPlayerCount() == 1:
    if GameWorldProcess.IsNoPlayerNeedCloseFB() and GameWorld.GetGameWorld().GetMapCopyPlayerManager().GetPlayerCount() == 1:
        #如果副本中只有这一个人, 那么把这个副本设置为玩家安全下线, 关闭副本
        gameFB = GameWorld.GetGameFB()
        gameFB.SetIsSafeClose(1)
@@ -1504,7 +1580,7 @@
    #    ChangePlayerAction(curPlayer, IPY_GameWorld.paNull)
    #===============================================================================================
    
    PlayerResetWorldPosFB(curPlayer, mapID, posX, posY, False, curPlayer.GetFromLineID())
    PlayerResetWorldPosFB(curPlayer, mapID, posX, posY, False, lineID)
    
    #在空闲或者移动状态下,才能锁死玩家
    if curPlayer.GetPlayerAction() in [IPY_GameWorld.paNull] or curPlayer.IsMoving():
@@ -1515,71 +1591,35 @@
#---------------------------------------------------------------------
def ResetMergeFBPlayerCntInfo(resetMapID):
    # 重置跨服活动副本地图人数分配情况信息
    mapID = GameWorld.GetMap().GetMapID()
    if mapID != ChConfig.Def_MergeTransMapID:
        return
    GameWorld.Log("重置跨服活动副本地图人数分配情况信息: resetMapID=%s" % resetMapID)
    gameWorld = GameWorld.GetGameWorld()
    mergeFBPlayerCntDict = ReadChConfig.GetEvalChConfig("MergeFBPlayerCount")
    for reqMapID, mapInfo in mergeFBPlayerCntDict.items():
        if resetMapID and resetMapID != reqMapID:
            continue
        for playerMapID in mapInfo[1]:
            playerCnt = gameWorld.GetGameWorldDictByKey(ChConfig.Map_WorldKey_MergeFBMapPlayerCnt % playerMapID)
            for num in xrange(1, 1 + playerCnt):
                playerID = gameWorld.GetGameWorldDictByKey(ChConfig.Map_WorldKey_MergeFBMapPlayerID % (playerMapID, num))
                gameWorld.SetGameWorldDict(ChConfig.Map_WorldKey_MergeFBMapPlayerID % (playerMapID, num), 0) # 重置对应的玩家ID
                gameWorld.SetGameWorldDict(ChConfig.Map_WorldKey_MergeFBPlayerMapID % (reqMapID, playerID), 0) # 重置玩家ID对应的地图
            gameWorld.SetGameWorldDict(ChConfig.Map_WorldKey_MergeFBMapPlayerCnt % playerMapID, 0) # 重置地图人数
    return
def __GetMergeFBPlayerMapID(curPlayer, reqMapID):
    # 获取玩家所分配的跨服活动地图ID
    # @param reqMapID: 可以是本服活动的地图ID标识; 也可以是指定的跨服活动地图ID, 如果是指定的地图ID也是直接返回
    mergeFBPlayerCntDict = ReadChConfig.GetEvalChConfig("MergeFBPlayerCount")
    if reqMapID not in mergeFBPlayerCntDict:
        return reqMapID
def PlayerEnterCrossServer(curPlayer, mapID):
    playerID = curPlayer.GetPlayerID()
    gameWorld = GameWorld.GetGameWorld()
    GameWorld.Log("玩家请求进入跨服地图: mapID=%s" % (mapID), playerID)
    if GameWorld.IsCrossServer():
        GameWorld.DebugLog("跨服服务器不允许该操作!")
        return
    
    playerMapID = gameWorld.GetGameWorldDictByKey(ChConfig.Map_WorldKey_MergeFBPlayerMapID % (reqMapID, playerID))
    if playerMapID:
        GameWorld.DebugLog("已经有分配跨服活动地图,直接返回!playerMapID=%s" % (playerMapID), playerID)
        return playerMapID
    if GetCrossMapID(curPlayer):
        GameWorld.ErrLog("玩家当前为跨服状态,不允许再次请求进入跨服!", curPlayer.GetPlayerID())
        return
    
    # 还没分配该玩家, 则开始选择分配的地图ID
    maxPlayerCnt, mapIDList = mergeFBPlayerCntDict[reqMapID]
    minPlayerCount = 0 # 最少的地图玩家人数
    minPlayerMapID = 0 # 最少人数的地图ID
    for mID in mapIDList:
        curMapPlayerCnt = gameWorld.GetGameWorldDictByKey(ChConfig.Map_WorldKey_MergeFBMapPlayerCnt % mID)
        if curMapPlayerCnt < maxPlayerCnt:
            playerMapID = mID
            break
        # 保存最少人数的地图ID信息
        if not minPlayerCount or (minPlayerCount and curMapPlayerCnt < minPlayerCount):
            minPlayerCount = curMapPlayerCnt
            minPlayerMapID = mID
    if not CrossRealmPlayer.IsCrossServerOpen():
        NotifyCode(curPlayer, "CrossMatching18")
        return
    
    # 如果没有人数未满的活动地图,则分配到人数较少的地图
    if not playerMapID:
        playerMapID = minPlayerMapID
    # 更新分配信息
    if playerMapID:
        mapPlayerCnt = gameWorld.GetGameWorldDictByKey(ChConfig.Map_WorldKey_MergeFBMapPlayerCnt % playerMapID) + 1
        gameWorld.SetGameWorldDict(ChConfig.Map_WorldKey_MergeFBMapPlayerCnt % playerMapID, mapPlayerCnt)
        gameWorld.SetGameWorldDict(ChConfig.Map_WorldKey_MergeFBMapPlayerID % (playerMapID, mapPlayerCnt), playerID)
        gameWorld.SetGameWorldDict(ChConfig.Map_WorldKey_MergeFBPlayerMapID % (reqMapID, playerID), playerMapID)
        GameWorld.Log("分配跨服活动玩家所属地图: plaeyrID=%s,reqMapID=%s,分配MapID=%s,mapPlayerCnt=%s"
                      % (playerID, reqMapID, playerMapID, mapPlayerCnt))
    return playerMapID
    if curPlayer.GetHP() <= 0:
        NotifyCode(curPlayer, "CrossMap4")
        return
    if PlayerCrossRealmPK.GetIsCrossPKMatching(curPlayer):
        NotifyCode(curPlayer, "CrossMap3")
        return
    if PlayerState.IsInPKState(curPlayer):
        NotifyCode(curPlayer, "SingleEnterPK", [mapID])
        return
    GY_Query_CrossRealmReg.RegisterEnterCrossServer(curPlayer, mapID)
    return
##玩家进入副本
# @param curPlayer 玩家实例
@@ -1599,13 +1639,13 @@
    #        NotifyCode(curPlayer, 'jiazu_xyj_671654')
    #        return
        
    #跨服活动人数分流处理
    if GameWorld.IsMergeServer():
        reqMapID = mapID
        mapID = __GetMergeFBPlayerMapID(curPlayer, reqMapID)
        if not mapID:
            GameWorld.ErrLog("找不到可分配进入的跨服活动地图ID! reqMapID=%s" % reqMapID)
            return
#    #跨服活动人数分流处理
#    if GameWorld.IsCrossServer():
#        reqMapID = mapID
#        mapID = __GetMergeFBPlayerMapID(curPlayer, reqMapID)
#        if not mapID:
#            GameWorld.ErrLog("找不到可分配进入的跨服活动地图ID! reqMapID=%s" % reqMapID)
#            return
      
    #过滤封包地图ID
    if not GameWorld.GetMap().IsMapIDExist(mapID):
@@ -1667,6 +1707,14 @@
            else:
                bossID = GameLogic_SealDemon.CurFBLineBOSSID(lineID)
                extendParamList = [bossID]
        elif mapID == ChConfig.Def_FBMapID_ZhuXianBoss:
            bossID = GameLogic_ZhuXianBoss.CurFBLineBOSSID(lineID)
            extendParamList = [bossID, -1]
            enterCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_EnterFbCntDay % ChConfig.Def_FBMapID_ZhuXianBoss)
            if enterCnt >= FBCommon.GetEnterFBMaxCnt(curPlayer, ChConfig.Def_FBMapID_ZhuXianBoss):
                if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ZhuXianBossHelpCnt):
                    extendParamList = [bossID, curPlayer.GetFamilyID()]
        SendToGameServerEnterFB(curPlayer, mapID, lineID, tick, extendParamList)
        return
    
@@ -1730,6 +1778,18 @@
        if isNotify:
            NotifyCode(curPlayer, "SingleEnterPK", [mapID])
        return ShareDefine.EntFBAskRet_PKState
    ## 跨服PK匹配中
    if PlayerCrossRealmPK.GetIsCrossPKMatching(curPlayer):
        if isNotify:
            NotifyCode(curPlayer, "CrossMatching8", [mapID])
        return ShareDefine.EntFBAskRet_CrossPKMatching
    ## 跨服地图中
    if GetCrossMapID(curPlayer) and mapID not in ChConfig.Def_CrossMapIDList:
        if isNotify:
            NotifyCode(curPlayer, "CrossMap5", [mapID])
        return ShareDefine.EntFBAskRet_InCrossMap
    
    #===============================================================================================
    # # 这里不做状态限制,由前端处理,因为策划要根据界面来处理,同一传送功能有可能在不同界面
@@ -1954,16 +2014,18 @@
    sendPack.FuncLineID = funcLineID
    
    NetPackCommon.SendFakePack(curPlayer, sendPack)
    GameWorld.Log("准备切换地图", curPlayer.GetID())
    return
# 通知开始切换地图
def NotifyStartChangeMap(curPlayer):
    GameWorld.DebugLog("通知开始切换地图, NotifyStartChangeMap")
    sendPack = ChPyNetSendPack.tagMCStartChangeMap()
    sendPack.Clear()
    sendPack.MapID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ChangeMapID)
    sendPack.FuncLineID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ReqFBFuncLine)
    NetPackCommon.SendFakePack(curPlayer, sendPack)
    GameWorld.Log("通知开始切换地图", curPlayer.GetID())
    return
#---------------------------------------------------------------------
@@ -2026,8 +2088,12 @@
    #---同地图ID, 同分线, 仅切换坐标---
    #lingID = -1, 代表默认当前线, 如主城2传送剑宗, 到达剑宗2
    if gameWorld.GetMapID() == mapID and (shuntLineID == -1 or gameWorld.GetLineID() == shuntLineID):
        #重置坐标
        GameWorld.ResetPlayerPos(curPlayer, posX, posY)
        if exData1:
            #boss的用move,通知前端,由前端发起move
            ChPlayer.NotifyPlayerMove(curPlayer, posX, posY, exData1)
        else:
            #重置坐标
            GameWorld.ResetPlayerPos(curPlayer, posX, posY)
        #@bug: 在摆摊区快速传送至非摆摊区, 可以摆摊, 这里刷新一下场景Buff
        SkillShell.ProcessMapBuff(curPlayer, GameWorld.GetGameWorld().GetTick())
        return
@@ -2059,11 +2125,12 @@
    if PlayerTJG.GetIsTJG(curPlayer):
        GameWorld.DebugLog("脱机的玩家不处理分流!", curPlayer.GetPlayerID())
        return tagLineID
    if GameWorld.IsCrossServer():
        return tagLineID
    # 非常规地图之间的切换不处理
    if curMapID not in PyGameData.g_commMapLinePlayerCountDict or tagMapID not in PyGameData.g_commMapLinePlayerCountDict:
        return tagLineID
    tagLinePlayerCountDict = PyGameData.g_commMapLinePlayerCountDict[tagMapID]
    tagLinePlayerCountDict = PyGameData.g_commMapLinePlayerCountDict[tagMapID] # 此分线包含所有分线,含未开放的及活动分线
    
    playerID = curPlayer.GetPlayerID()
    playChangeLineID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_PlayChangeLineID)
@@ -2080,7 +2147,7 @@
            GameWorld.DebugLog("功能指定切换目标线路,记录当前线路后续切线备用! curLineID=%s,tagLineID=%s" % (curLineID, tagLineID), playerID)
            
        if bossID and NPCCommon.IsMapNeedBossShunt(tagMapID):
            bossShuntLineID = __GetBossShuntLineID(curPlayer, tagMapID, bossID, tagLinePlayerCountDict.keys())
            bossShuntLineID = __GetBossShuntLineID(curPlayer, curMapID, curLineID, tagMapID, bossID, tagLinePlayerCountDict.keys())
            if bossShuntLineID != -1:
                tick = GameWorld.GetGameWorld().GetTick()
                GameWorld.DebugLog("分流boss, bossID=%s,bossShuntLineID=%s" % (bossID, bossShuntLineID), playerID)
@@ -2169,10 +2236,23 @@
    GameWorld.DebugLog("分流到人数较少的线路,tagMapID=%s,linePlayerList[count,lineID]=%s" % (tagMapID, linePlayerList), playerID)
    return shuntLineID
def __GetBossShuntLineID(curPlayer, mapID, npcID, lineIDList):
def __GetBossShuntLineID(curPlayer, curMapID, curLineID, tagMapID, npcID, lineIDList):
    '''获取目标地图boss分流线路
    根据人数分流玩家,boss死亡状态,只能分流到已死亡线路
    队伍无视任何规则,默认分配到队伍队员多的那条线
                根据人数分流玩家,优先分配到活着的线路
                队伍无视任何规则,默认分配到队伍队员多的那条线
                前端:
        1.在中立地图的时候,显示当前线路BOSS的状态
        2.在常规地图的时候,显示玩家击杀BOSS的CD时间
        3.本地图不换线,60秒规则保持不变
        4.进入BOSS区域里,刷新BOSS状态
                玩家在非中立地图:
                本地图不换线,60秒规则保持不变()
                玩家在中立地图
                没有60秒规则
                直接发传送包,由后端决定(有队员在不同线路打同一只boss,则传送,否则move)
    '''
    playerID = curPlayer.GetPlayerID()
    playerTeamID = curPlayer.GetTeamID()
@@ -2180,65 +2260,75 @@
    hurtTeamLineID = -1
    hurtTeamMemCount = -1
    
    emptyLineID = -1
    bossLinePlayerCntList = []
    bossLinePlayerDict = {1:[-1, []], 0:[-1, []]} # {是否被击杀:[该状态人数空的线路, [[玩家数, 线路], ...]], ...}
    
    deadLineList = PyGameData.g_bossShuntDeadLine.get(npcID, [])
    bossState = not deadLineList # boss公共状态, 只要有一条线路是死亡的就是死亡
    bossLineStateDict = PyGameData.g_bossShuntLineState.get(npcID, {})
    
    GameWorld.DebugLog("玩家boss分流: playerTeamID=%s,mapID=%s,npcID=%s,lineIDList=%s,bossState=%s,deadLineList=%s,g_bossShuntPlayerInfo=%s"
                       % (playerTeamID, mapID, npcID, lineIDList, bossState, deadLineList, PyGameData.g_bossShuntPlayerInfo), playerID)
    haveAliveLine = False # 是否有活着的线路
    activityMapLineDict = IpyGameDataPY.GetFuncEvalCfg("MapLine", 2, {})
    
    GameWorld.DebugLog("玩家boss分流: playerTeamID=%s,tagMapID=%s,npcID=%s,lineIDList=%s,bossLineStateDict=%s,g_bossShuntPlayerInfo=%s"
                       % (playerTeamID, tagMapID, npcID, lineIDList, bossLineStateDict, PyGameData.g_bossShuntPlayerInfo), playerID)
    if tagMapID in activityMapLineDict:
        activityLineID = max(0, activityMapLineDict[tagMapID] - 1)
        # 非1线的活动线路不参与分流
        if activityLineID != 0 and activityLineID in lineIDList:
            lineIDList.remove(activityLineID)
            GameWorld.DebugLog("    非1线的活动线路不参与分流: activityLineID=%s,lineIDList=%s" % (activityLineID, lineIDList), playerID)
    for lineID in lineIDList:
        key = (mapID, lineID)
        key = (tagMapID, lineID)
        # boss分流玩家信息{(mapID, lineID):{playerID:[bossID, teamID, relatedTick], ...}, ...}
        shuntPlayerDict = PyGameData.g_bossShuntPlayerInfo.get(key, {})
        playerCount = 0
        teamPlayerCount = 0
        for shuntInfo in shuntPlayerDict.values():
        for shuntPlayerID, shuntInfo in shuntPlayerDict.items():
            bossID = shuntInfo[0]
            if npcID != bossID:
                continue
            playerCount += 1
            shuntTeamID = shuntInfo[1]
            if playerTeamID and playerTeamID == shuntTeamID:
            if playerTeamID and playerTeamID == shuntTeamID and shuntPlayerID != playerID:
                teamPlayerCount += 1
        
        if teamPlayerCount and teamPlayerCount > hurtTeamMemCount:
            hurtTeamMemCount = teamPlayerCount
            hurtTeamLineID = lineID
            
        lineBossState = lineID not in deadLineList # 当前线路boss状态
        if bossState != lineBossState:
            GameWorld.DebugLog("    lineID=%s,lineBossState=%s != bossState=%s,teamPlayerCount=%s,hurtTeamMemCount=%s,hurtTeamLineID=%s"
                           % (lineID, lineBossState, bossState, teamPlayerCount, hurtTeamMemCount, hurtTeamLineID), playerID)
            continue
        lineIsAlive = bossLineStateDict.get(lineID, 0)
        lineBossIsDead = 1 if not lineIsAlive else 0 # 当前线路boss是否死亡
        emptyLineID, linePlayerCountList = bossLinePlayerDict[lineBossIsDead]
        if not playerCount and emptyLineID == -1:
            emptyLineID = lineID
        linePlayerCountList.append([playerCount, lineID])
        bossLinePlayerDict[lineBossIsDead] = [emptyLineID, linePlayerCountList]
        
        GameWorld.DebugLog("    lineID=%s,lineBossState=%s,playerCount=%s,teamPlayerCount=%s,hurtTeamMemCount=%s,hurtTeamLineID=%s"
                           % (lineID, lineBossState, playerCount, teamPlayerCount, hurtTeamMemCount, hurtTeamLineID), playerID)
        if not playerCount:
            if emptyLineID < 0:
                emptyLineID = lineID
        else:
            bossLinePlayerCntList.append([playerCount, lineID])
        if not lineBossIsDead:
            haveAliveLine = True
            
        GameWorld.DebugLog("    lineID=%s,lineBossIsDead=%s,playerCount=%s,teamPlayerCount=%s,hurtTeamMemCount=%s,hurtTeamLineID=%s"
                           % (lineID, lineBossIsDead, playerCount, teamPlayerCount, hurtTeamMemCount, hurtTeamLineID), playerID)
    if hurtTeamLineID >= 0:
        GameWorld.DebugLog("    分流到队友人数多的线路 hurtTeamLineID=%s" % hurtTeamLineID, playerID)
        return hurtTeamLineID
    
    if not bossLinePlayerCntList:
        GameWorld.DebugLog("    没有人在该boss状态下的线路,默认空新线路!bossState=%s,emptyLineID=%s" % (bossState, emptyLineID), playerID)
        return emptyLineID
    if curMapID == tagMapID and curMapID in IpyGameDataPY.GetFuncEvalCfg("MapLine", 4):
        GameWorld.DebugLog("    中立地图在本地图中默认当前线路 curLineID=%s" % curLineID, playerID)
        return curLineID
    
    bossLinePlayerCntList.sort() # 升序
    playerCount, minPlayerCntLineID = bossLinePlayerCntList[0]
    shuntBossIsDead = 0 if haveAliveLine else 1 # 优先分流到活着的线路
    GameWorld.DebugLog("    boss状态对应线路人数: haveAliveLine=%s, 状态key0为活着: %s" % (haveAliveLine, bossLinePlayerDict))
    emptyLineID, linePlayerCountList = bossLinePlayerDict[shuntBossIsDead]
    linePlayerCountList.sort() # 升序
    playerCount, minPlayerCntLineID = linePlayerCountList[0]
    bossShuntPlayerCountMax = IpyGameDataPY.GetFuncCfg("BossShunt", 2)
    if playerCount >= bossShuntPlayerCountMax and emptyLineID >= 0:
        GameWorld.DebugLog("    分流到空新线路 bossState=%s,emptyLineID=%s" % (bossState, emptyLineID), playerID)
        GameWorld.DebugLog("    分流到空新线路 shuntBossIsDead=%s,emptyLineID=%s" % (shuntBossIsDead, emptyLineID), playerID)
        return emptyLineID
    
    GameWorld.DebugLog("    分流到人数最少的线路 bossState=%s,minPlayerCntLineID=%s,bossLinePlayerCntList=%s"
                       % (bossState, minPlayerCntLineID, bossLinePlayerCntList), playerID)
    GameWorld.DebugLog("    分流到人数最少的线路 shuntBossIsDead=%s,minPlayerCntLineID=%s,linePlayerCountList=%s"
                       % (shuntBossIsDead, minPlayerCntLineID, linePlayerCountList), playerID)
    return minPlayerCntLineID
#---------------------------------------------------------------------
@@ -2775,12 +2865,7 @@
        SetPlayerCurrency(curPlayer, type_Price, curCurrency - price)
    else:
        GameWorld.Log("付费金钱异常 type_Price = %s" % (type_Price), curPlayer.GetPlayerID())
        return False
    #添加跨服操作事件
    if costType in ChConfig.MergeServerCanCostType:
        eventInfo = [type_Price, price, costType, infoDict, quantity, costVIPGold]
        PlayerMergeEvent.AddMSPlayerEvent(curPlayer.GetPlayerID(), PlayerMergeEvent.Def_MSPEvent_PayMoney, eventInfo)
        return False
    
    #付款以后后续操作
    __PayMoneyAfter(curPlayer, type_Price, price, costType, infoDict, quantity, costVIPGold)
@@ -2887,6 +2972,7 @@
    PlayerCostRebate.AddCostRebateGold(curPlayer, costType, price, infoDict)
    # 绝版降临
    PlayerFairyCeremony.AddFCCostGold(curPlayer, costType, price)
    PlayerNewFairyCeremony.AddFCCostGold(curPlayer, costType, price)
    # 消费VIP
#    if costVIPGold < 0:
#        costVIPGold = price
@@ -2977,6 +3063,12 @@
    if value < 0:
        GameWorld.Log('玩家获得金钱异常 , value = %s , priceType = %s ,' % (value, priceType))
        return
    if GameWorld.IsCrossServer():
        serverGroupID = GetPlayerServerGroupID(curPlayer)
        msgInfo = {"PlayerID":curPlayer.GetPlayerID(), "MoneyType":priceType, "Value":value, "GiveType":giveType, "AddDataDict":addDataDict}
        GameWorld.SendMsgToClientServer(ShareDefine.CrossServerMsg_GiveMoney, msgInfo, [serverGroupID])
        return True
    
    if priceType == IPY_GameWorld.TYPE_Price_Gold_Money:
        if curPlayer.GetGold() + value > ChConfig.Def_PlayerTotalMoney_Gold:
@@ -3290,18 +3382,68 @@
# @remarks 获得玩家升级, 获得的属性点
def GetLvUp_AddPoint(curPlayer):
    curPlayerID = curPlayer.GetID()
    curReinCnt = curPlayer.GetReincarnationLv() # 当前转生次数
    curLV = curPlayer.GetLV() # 当前等级
    
    addPointList = IpyGameDataPY.GetFuncEvalCfg("LVUPAddPoint", 1)
    addPoint = addPointList[-1] if curReinCnt >= len(addPointList) else addPointList[curReinCnt]
    addPointDict = IpyGameDataPY.GetFuncEvalCfg("LVUPAddPoint", 1, {})
    addPoint = GameWorld.GetDictValueByRangeKey(addPointDict, curLV, 0)
    
    if addPoint == None:
        raise Exception('玩家获得升级属性点异常, reincarnationLv = %s PlayerID = %s' % (curReinCnt, curPlayerID))
        raise Exception('玩家获得升级属性点异常, curLV = %s PlayerID = %s' % (curLV, curPlayerID))
        return
    
    return int(addPoint)
def GetAllPointByLV(curPlayer):
    ##获取当前等级可得到属性点数
    openLV = GameFuncComm.GetFuncLimitLV(ShareDefine.GameFuncID_AddPoint)
    curLV = curPlayer.GetLV()
    if curLV < openLV:
        return 0
    addPointDict = IpyGameDataPY.GetFuncEvalCfg("LVUPAddPoint", 1, {})
    initFreePoint = IpyGameDataPY.GetFuncCfg("LVUPAddPoint", 2)
    setFreePoint = initFreePoint
    for lv in xrange(openLV, curLV+1):
        setFreePoint += GameWorld.GetDictValueByRangeKey(addPointDict, lv, 0)
    return setFreePoint
def DoAddPointOpen(curPlayer):
    '''加点功能开启 处理给自由属性点及老号处理
                    清除老服玩家未加点的点数(清零),以前加的加点属性不清除,属性不变,战力不减, 根据最新的加点开启等级和老服玩家的当前等级,相差的差值给予玩家对应的加点点数'''
    beforeFreePoint = curPlayer.GetFreePoint()
    setFreePoint = GetAllPointByLV(curPlayer)
    curLV = curPlayer.GetLV()
    addDataDict = {'beforeFreePoint':beforeFreePoint}
    curPlayer.SetFreePoint(setFreePoint)
    DataRecordPack.DR_Freepoint(curPlayer, "AddPointOpen", setFreePoint, addDataDict)
    GameWorld.DebugLog('    加点功能开启处理  beforeFreePoint=%s,curLV=%s, setFreePoint=%s'%(beforeFreePoint, curLV, setFreePoint), curPlayer.GetID())
    return
def FixOldAddPoint(curPlayer):
    ##老号加点处理 重置已加点数,
    if GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_Player_Dict_VersionFix, ChConfig.Def_VerFix_AddPoint):
        return
    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_Player_Dict_VersionFix, ChConfig.Def_VerFix_AddPoint, 1)
    baseSTR, basePNE, basePHY, baseCON = GetPlayerBasePoint(curPlayer.GetJob())
    addPoint = curPlayer.GetFreePoint()
    attrInfoDict = {'BaseSTR':baseSTR,'BasePNE':basePNE,'BasePHY':basePHY,'BaseCON':baseCON}
    for attrKey, basePoint in attrInfoDict.items():
        addPoint += (getattr(curPlayer, 'Get%s'%attrKey)() - basePoint)
    fixFreePoint = GetAllPointByLV(curPlayer)
    if addPoint != fixFreePoint:
        for attrKey, basePoint in attrInfoDict.items():
            getattr(curPlayer, 'Set%s'%attrKey)(basePoint)
        curPlayer.SetFreePoint(fixFreePoint)
        #重置天赋
        PlayerGreatMaster.DoResetMasterSkillPoint(curPlayer)
        #邮件补偿
        SendMailByKey('PointCompensation', [curPlayer.GetID()], [], goldPaper=200)
        addDataDict = {'beforeTotalPoint':addPoint}
        DataRecordPack.DR_Freepoint(curPlayer, "FixOldAddPoint", fixFreePoint, addDataDict)
        GameWorld.Log('老号加点处理 重置已加点数 addPoint=%s, fixFreePoint=%s' % (addPoint, fixFreePoint))
    return
#---------------------------------------------------------------------
## 功能模块战斗力类
@@ -3310,6 +3452,8 @@
class ModuleFightPower():
    
    __AttrName = "%s" # 参数为ChConfig.Def_Calc_AllAttrType_MAX对应所有属性列表索引
    __AttrNameNoline = "Noline_%s" # 参数为ChConfig.Def_Calc_AllAttrType_MAX对应所有属性列表索引
    __NolineAttrList = [ChConfig.TYPE_Calc_AttrSpeed] # 需要记录的非线性战斗属性
    
    ## 初始化
    #  @param self 类实例
@@ -3329,6 +3473,8 @@
#            if attrIndex == ChConfig.TYPE_Calc_SuperHit:
#                value = ChConfig.Def_SuperHitPercent # 默认最低暴击倍值
            setattr(self, self.__AttrName % attrIndex, value)
        for attrIndex in self.__NolineAttrList:
            setattr(self, self.__AttrNameNoline % attrIndex, 0)
        return
            
    ## 根据战斗属性列表设置计算战斗力属性
@@ -3339,6 +3485,12 @@
        # 设置本模块增加的线性战斗属性,非线性战斗属性增加的在刷属性时累加上去
        for attrIndex, value in battleAttrDict.items():
            self.AddCalcMFPAttr(attrIndex, value)
        # 非线性战斗属性仅设置时记录即可
        battleNolineAttrDict = allAttrList[ChConfig.CalcAttr_BattleNoline]
        for attrIndex, value in battleNolineAttrDict.items():
            if attrIndex in self.__NolineAttrList:
                setattr(self, self.__AttrNameNoline % attrIndex, value)
        return
    
    ## 设置计算战斗力属性值
@@ -3380,6 +3532,7 @@
        HPRestore = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_HPRestorePer) # 自动回复血量,固定值
        DamBackPer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_DamBackPer) * fpParam.GetCftDamBackPer() # 反伤百分比
        SpeedValue = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_AttrSpeed) # 移动速度值
        SpeedPer = getattr(self, self.__AttrNameNoline % ChConfig.TYPE_Calc_AttrSpeed) * fpParam.GetCftSpeedPer() # 移动速度百分比系数
        PetMinAtk = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_PetMinAtk) # 宠物最小攻击
        PetMaxAtk = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_PetMaxAtk) # 宠物最大攻击
        PetDamPer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_PetDamPer) # 宠物伤害百分比提升
@@ -3396,10 +3549,18 @@
        FinalHurtReduce = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_FinalHurtReduce) # 最终固定伤害减少
        DamagePerPVP = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_DamagePerPVP) * fpParam.GetCftDamagePerPVP() # 伤害输出计算百分比PVP
        DamagePerPVPReduce = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_DamagePerPVPReduce) * fpParam.GetCftDamagePerPVPReduce() # 伤害输出计算百分比PVP减少
        JobAHurtAddPer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_JobAHurtAddPer) * fpParam.GetCftJobAHurtAddPer() # 对目标战士伤害加成
        JobBHurtAddPer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_JobBHurtAddPer) * fpParam.GetCftJobBHurtAddPer() # 对目标法师伤害加成
        JobCHurtAddPer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_JobCHurtAddPer) * fpParam.GetCftJobCHurtAddPer() # 对目标弓箭伤害加成
        JobAAtkReducePer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_JobAAtkReducePer) * fpParam.GetCftJobAAtkReducePer() # 战士攻击伤害减免
        JobBAtkReducePer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_JobBAtkReducePer) * fpParam.GetCftJobBAtkReducePer() # 法师攻击伤害减免
        JobCAtkReducePer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_JobCAtkReducePer) * fpParam.GetCftJobCAtkReducePer() # 弓箭攻击伤害减免
        ComboRate = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_ComboRate) # 连击几率
        ComboDamPer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_ComboDamPer) # 连击伤害
        MaxProDef = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_MaxProDef) # 最大防护值
        ProDefAbsorb = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_ProDefAbsorb) # 防护值吸收伤害比率
        #MaxProDef = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_MaxProDef) # 最大防护值
        #ProDefAbsorb = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_ProDefAbsorb) # 防护值吸收伤害比率
        ProDefPer = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_ProDefHPPer) # 防护转化百分比
        
        OnlyFinalHurt = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_OnlyFinalHurt) # 额外输出伤害
        PVPAtkBackHP = getattr(self, self.__AttrName % ChConfig.TYPE_Calc_PVPAtkBackHP) # PVP攻击回血
@@ -3408,7 +3569,7 @@
        #其他需作为公式参数的系数
        AtkSpeedParameter = fpParam.GetCftAtkSpeed()
        LuckyHitParameter = fpParam.GetCftLuckyHit()
        #获取策划配置的表格
        FightpowerFormula = IpyGameDataPY.GetFuncCfg("FightpowerFormula")
        totalFightPower = eval(FormulaControl.GetCompileFormula("FightpowerFormula", FightpowerFormula))
@@ -3423,6 +3584,14 @@
        attrStr = ""
        for attrIndex in xrange(1, ChConfig.Def_Calc_AllAttrType_MAX):
            attrName = self.__AttrName % attrIndex
            attrValue = getattr(self, attrName)
            if attrValue <= 0:
                continue
            attrStr += "%s=%s," % (attrName, attrValue)
        for attrIndex in self.__NolineAttrList:
            attrName = self.__AttrNameNoline % attrIndex
            attrValue = getattr(self, attrName)
            if attrValue <= 0:
                continue
@@ -3546,10 +3715,6 @@
        #副本获得经验, 无论获得多少经验均需通知, 有些副本逻辑需要通过获得经验时机处理
        if GameWorld.GetMap().GetMapFBType() != IPY_GameWorld.fbtNull:
            FBLogic.OnGetExp(curPlayer, finalAddExp, expViewType)
        # 跨服中获得经验
        if finalAddExp and GameWorld.IsMergeServer():
            PlayerMergeEvent.AddMSPlayerEvent(curPlayer.GetPlayerID(), PlayerMergeEvent.Def_MSPEvent_AddExp, finalAddExp)
            
        return finalAddExp
    
@@ -3576,7 +3741,7 @@
        # 检查最大等级
        if curLV >= maxLV and curTotalExp >= maxLVExpStore:
            self.__NotifyExpFull(curPlayer, "GeRen_admin_825676")
            GameWorld.DebugLog("经验已满!已满级!curLV=%s" % (curLV), curPlayer.GetPlayerID())
            #GameWorld.DebugLog("经验已满!已满级!curLV=%s" % (curLV), curPlayer.GetPlayerID())
            return 0, expViewType
        
        # 杀怪
@@ -3712,7 +3877,7 @@
        #未达到升级经验
        if curTotalExp < lvUpNeedExp:
            return
        needSyncTalentPoint = False
        playerNeedDoLVUp = False
        curLV = curPlayer.GetLV()
        maxLV = IpyGameDataPY.GetFuncCfg("PlayerMaxLV", 1)
@@ -3746,6 +3911,7 @@
                       
            # 记录玩家升级
            DataRecordPack.DR_PlayerUpgrade(curPlayer, curPlayer.GetLV(), GetPlayerTotalExp(curPlayer), lvUpNeedExp)
            DataRecordPack.Cache_FightPowerChangeInfo(curPlayer, ChConfig.PowerDownType_LVUP, {'lv':curLV})
            
            self.__DoLVUPAddPoint()  # 升级加点
            #self.__DoLvUpAddSkill()  # 升级加技能
@@ -3753,7 +3919,10 @@
            lvIpyData = GetPlayerLVIpyData(curPlayer.GetLV())
            # 大师天赋点
            if lvIpyData:
                PlayerGreatMaster.AddGreatMasterSkillPointByLV(curPlayer, lvIpyData.GetTalentPoint())
                addTalentPoint = lvIpyData.GetTalentPoint()
                if addTalentPoint:
                    needSyncTalentPoint = True
                    PlayerGreatMaster.AddGreatMasterSkillPointByLV(curPlayer, addTalentPoint)
            
            EventShell.EventResponse_LVUp(curPlayer)  # 升级触发事件
            
@@ -3802,10 +3971,16 @@
            #    NotifyCode(curPlayer, "GeRen_liubo_127574")
            
            #===================================================================
            # 天赋点通知
            if needSyncTalentPoint:
                PlayerGreatMaster.Sync_GreatMasterFreeSkillPoint(curPlayer)
            # 升级需要执行的游戏功能处理
            GameFuncComm.DoFuncOpenLogic(curPlayer)
            ChEquip.CalcEquips_OutOfPrint(curPlayer)    # 缓存绝版属性
            if aftLV%10 == 0:
                # 控制下刷新次数
                PlayerPet.CalcPetItemAddPlayerAttr(curPlayer)   # 宠物有随等级变化的技能
            self.RefreshPlayerAttrState(billboardFunc=PlayerBillboard.UpdatePlayerLVBillboard)
            #放在功能开启后面
            PlayerWorldAverageLv.UpdatePlayerWorldAverageLv(curPlayer)
@@ -3813,10 +3988,12 @@
            curPlayer.SetHP(curPlayer.GetMaxHP())
            if curPlayer.GetMaxMP() > 0:
                curPlayer.SetMP(curPlayer.GetMaxMP())
            FBLogic.OnPlayerLVUp(curPlayer)
            # 记录开服活动冲级数据
            OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_LV, curPlayer.GetLV())
            #神秘限购
            FunctionNPCCommon.MysticalShopOpen(curPlayer, befLV, aftLV)
        #不需要做升级任务, 设置玩家经验
        SetPlayerTotalExp(curPlayer, curTotalExp) 
        return
@@ -3971,6 +4148,9 @@
        PlayerFamilyTech.CalcFamilyTechAttr(curPlayer)
        PlayerEquipDecompose.RefreshEDAttr(curPlayer)
        PlayerDogz.RefreshDogzAttr(curPlayer)
        EquipZhuXian.CalcZhuXianAttr(curPlayer)
        PlayerGatherSoul.RefreshGatherSoulAttr(curPlayer)
        PlayerCoat.CalcClothesCoatSkinAttr(curPlayer)
        self.RefreshAllState(isForce=True)
        GameWorld.DebugLog("End ReCalcAllState!!!")
        return
@@ -4094,6 +4274,7 @@
        #beforeAtkInterval = curPlayer.GetAtkInterval()
        beforeMaxHP = curPlayer.GetMaxHP()
        beforeMoveSpeedValue = GetSpeedValue(curPlayer)
        beforeMaxProDef = GetMaxProDef(curPlayer)
        #构建玩家刷新通知客户端字典, 缓存[索引, 数值]
        playerStateDict = {}
        for index in xrange(1, ChConfig.Def_Calc_AllAttrType_MAX):
@@ -4288,6 +4469,9 @@
        CalcLineEffect.ChangePlayerAttrInLineEffectList(curPlayer, skillNoFightPowerAttrList[ChConfig.CalcAttr_Battle])
        #self.PrintAttr(curPlayer, "固定层级")
        
        #护盾值刷新
        self.__RefreshMaxProDef(beforeMaxProDef)
        # 【到此所有功能属性都已刷新处理完毕,复制一份 功能属性的刷新结果,用于BUFF属性单独刷新】
        EffGetSet.CopyPlayerFuncAttr(curPlayer)
        
@@ -4315,6 +4499,33 @@
        GameWorld.DebugLog("End RefreshPlayerAttrStateEx!!!")
        return True
    
    # 生命转化为防护值
    def __RefreshMaxProDef(self, beforeMaxProDef):
        curPlayer = self.__Player
        if GetProDefHPPer(curPlayer) == 0:
            return
        maxHP = curPlayer.GetMaxHP()
        proDefPer = GetProDefHPPer(curPlayer)
        #获取策划配置的表格
        GodWeapon4 = IpyGameDataPY.GetFuncCfg("GodWeapon4", 2)
        maxProDef = eval(FormulaControl.GetCompileFormula("GodWeapon4", GodWeapon4))
        SetMaxProDef(curPlayer, int(maxProDef))
        afterMaxProDef = GetMaxProDef(curPlayer)
        addValue = max(0, afterMaxProDef - beforeMaxProDef)
        curProDef = GetProDef(curPlayer)
        if beforeMaxProDef > 0 and addValue > 0 and curPlayer.GetPlayerAction() != IPY_GameWorld.paDie:
            # 同步增加 (死亡状态下不刷)
            SetProDef(curPlayer, min(curProDef + addValue, afterMaxProDef))
        elif curProDef > afterMaxProDef:
            # 做一次防范纠正
            SetProDef(curPlayer, min(curProDef, afterMaxProDef))
        return
    
    def __RefreshBuffAttr(self):
        ## 刷新buff层属性,该层属性只会改变玩家最终属性,不会影响战力等
@@ -4592,17 +4803,21 @@
        mfpInfo.MFPCnt = len(mfpDataList)
        mfpInfo.MFPList = mfpDataList
        NetPackCommon.SendFakePack(curPlayer, mfpInfo)
        beforeFightPower = curPlayer.GetFightPower()
        curPlayer.SetFightPower(totalFightPower, False)
        if totalFightPower < beforeFightPower:
            DataRecordPack.DR_FightPowerChangeInfo(curPlayer, beforeFightPower)
        highestFightPower = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FightPower_Highest, 0,
                                                           ChConfig.Def_PDictType_FightPower)
        if totalFightPower > highestFightPower:
            NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FightPower_Highest, totalFightPower,
                                 ChConfig.Def_PDictType_FightPower)
        GameWorld.DebugLog("总战力: %s, 历史最高战力: %s" % (totalFightPower, highestFightPower))
        GameWorld.DebugLog("总战力: %s, 历史最高战力: %s, beforeFightPower=%s" % (totalFightPower, highestFightPower, beforeFightPower))
        PlayerBillboard.UpdatePlayerFPTotalBillboard(curPlayer)
        # 记录开服活动数据
        OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_FightPower, totalFightPower)
        if beforeFightPower != totalFightPower:
            CrossPlayerData.OnPlayerFightPowerChange(curPlayer)
        return
    
    def __RefreshMoveSpeed(self, allAttrListBuffs):
@@ -4635,6 +4850,7 @@
                speed = int(speed * (ShareDefine.Def_MaxRateValue + buffSpeedPer) / float(ShareDefine.Def_MaxRateValue) + buffSpeed)
                GameWorld.DebugLog("    buff影响后速度值: speed=%s,buffSpeedPer=%s,buffSpeed=%s" % (speed, buffSpeedPer, buffSpeed))
                
            speed = max(speed, 0)   #防小于0错误
        if GetSpeedValue(curPlayer) != speed:
            SetSpeedValue(curPlayer, speed)
            moveSpeed = eval(FormulaControl.GetCompileFormula("MoveSpeed", moveSpeedFormat))
@@ -4761,7 +4977,7 @@
                        }
        
        for i in xrange(1, ChConfig.Def_Calc_AllAttrType_MAX):
            if i in [ChConfig.TYPE_Calc_AttrHP, ChConfig.TYPE_Calc_AttrMP]:
            if i in [ChConfig.TYPE_Calc_AttrHP, ChConfig.TYPE_Calc_AttrMP, ChConfig.TYPE_Calc_ProDef]:
                continue
            value = 0 if i not in initAttrDict else initAttrDict[i]
            EffGetSet.SetValueByEffIndex(curPlayer, i, value)
@@ -4988,6 +5204,7 @@
    index = 0
    buffSkillIDList = []
    
    passiveEff = PassiveBuffEffMng.GetPassiveEffManager().GetPassiveEff(curPlayer)
    while index < buffState.GetBuffCount():
        curBuff = buffState.GetBuff(index)
        #异常
@@ -5002,10 +5219,16 @@
            continue
        
        #BuffSkill.DoBuffDisApper(curPlayer, curBuff, tick)
        buffSkillIDList.append([curBuff.GetSkill().GetSkillID(), curBuff.GetOwnerID(), curBuff.GetOwnerType()])
        #buffSkillIDList.append([curBuff.GetSkill().GetSkillID(), curBuff.GetOwnerID(), curBuff.GetOwnerType()])
        buffSkillIDList.append(curBuff.GetSkill().GetSkillID())
        #GameWorld.DebugLog("死亡清理-----%s"%curBuff.GetSkill().GetSkillID())
        if passiveEff:
            passiveEff.DelBuffInfo(curBuff.GetSkill())
        #删除这个buff
        buffState.DeleteBuffByIndex(index)
    SkillShell.ClearBuffEffectBySkillIDList(curPlayer, buffState, buffSkillIDList)
    return
@@ -5512,6 +5735,9 @@
# SetExAttr1 ~ SetExAttr14(DWORD uiExAttr10, bool bNotifyAll = false, bool bNotifyGameServer = false)
# @param bNotifyAll: 是否广播所周围玩家,默认false,只会发给自己
# @param bNotifyGameServer: 是否同步GameServer,默认false
# SetExAttr15 ~ SetExAttr20(DWORD uiExAttr15)
#     15~20扩展属性同步封包:0309 选角登录简短信息、0434 周围玩家出现、0102 登录地图玩家信息;
#     同步前端及GameServer需要自己写通知,设置函数自身不带通知参数
#
# 发送0418包
# SendPropertyRefresh(int inputType, int inputValue, bool boardCast, bool includeSelf = true)
@@ -5520,10 +5746,13 @@
# ֪ͨGsmeServer; 
# SendGameServerRefreshState(int inputType, int inputValue)
# SetExAttr15 ~ 18 对应神兵类型等级,场景特效用
# 禁言 通知gameServer
def SetGMForbidenTalk(curPlayer, value):
    curPlayer.SetGMForbidenTalk(value)
    curPlayer.SendGameServerRefreshState(ShareDefine.CDBPlayerRefresh_ForbidenTalk, value)
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_ForbidenTalk, value, False)
    return
@@ -5542,6 +5771,14 @@
def SetFBFuncLineID(curPlayer, funcLineID): return curPlayer.SetExAttr3(funcLineID, False, False)
def GetFBFuncLineID(curPlayer): return curPlayer.GetExAttr3()
## 跨服状态所在地图ID: 0-非跨服状态,非0-跨服状态对应的地图ID
def GetCrossMapID(curPlayer): return curPlayer.GetExAttr5()
def SetCrossMapID(curPlayer, value):
    curPlayer.SetExAttr5(value, False, True)
    if not value:
        CrossPlayerData.ClearCrossSyncDataCache(curPlayer)
    return
## 铜钱点, 支持铜钱超20亿
def GetSilver(curPlayer): return curPlayer.GetExAttr6() * ChConfig.Def_PerPointValue + curPlayer.GetSilver()
def SetSilver(curPlayer, totalSilver):
@@ -5553,9 +5790,33 @@
        curPlayer.SetExAttr6(silverPoint)
    return
## 玩家今日已获得仙缘币
def GetTodayXianyuanCoin(curPlayer): return curPlayer.GetExAttr11()
def SetTodayXianyuanCoin(curPlayer, value): return curPlayer.SetExAttr11(value, False, True)
def AddTodayXianyuanCoin(curPlayer, addValue): return curPlayer.SetExAttr11(curPlayer.GetExAttr11() + addValue, False, True)
##VIP到期时间, 需要同步GameServer
def GetVIPExpireTime(curPlayer): return curPlayer.GetExAttr9()
def SetVIPExpireTime(curPlayer, expireTime): return curPlayer.SetExAttr9(expireTime, False, True)
##聊天气泡框
def GetChatBubbleBox(curPlayer): return curPlayer.GetExAttr10()
def SetChatBubbleBox(curPlayer, value): return curPlayer.SetExAttr10(value, False, True)
## 玩家所属服务器组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 GetPrestige(curPlayer): return 0
@@ -5652,10 +5913,6 @@
    
    SetZhenQi(curPlayer, value)
    #EventReport.WriteEvent_add_zhenqi(curPlayer, eventName, eventData, addValue, value)
    if GameWorld.IsMergeServer():
        eventInfo = [addValue, eventName, eventData]
        PlayerMergeEvent.AddMSPlayerEvent(curPlayer.GetPlayerID(), PlayerMergeEvent.Def_MSPEvent_AddZhenQi, eventInfo)
    return True
@@ -5747,37 +6004,6 @@
def SetLongMaiLV(curPlayer, value):
    return
#---------------------------------------------------------------------------
## 设置玩家跨服预赛排位
#  @param curPlayer: 玩家实例
#  @param value: 威望值
#  @return:
def SetMergeWarRank(curPlayer, value):
    curPlayer.SetExAttr10(value, True, True)
    return
## 获取玩家跨服预赛排位
#  @param curPlayer: 玩家实例
#  @return: 威望值
def GetMergeWarRank(curPlayer):
    return curPlayer.GetExAttr10()
## 设置玩家官爵星级
#  @param curPlayer: 玩家实例
#  @param value: 星级
#  @return:
def SetOfficeStar(curPlayer, value):
    curPlayer.SetExAttr11(value)
    return
## 获取玩家玩家官爵星级
#  @param curPlayer: 玩家实例
#  @return: 星级
def GetOfficeStar(curPlayer):
    return curPlayer.GetExAttr11()
##获取可免费开启的格子数
# @param curPlayer 玩家对象
@@ -6363,22 +6589,27 @@
    
#---当前防护值,需存DB----
def GetProDef(curPlayer): return curPlayer.GetExAttr4()
def SetProDef(curPlayer, value): curPlayer.SetExAttr4(value)
def SetProDef(curPlayer, value):
    if GameWorld.IsCrossServer():
        curPlayer.SetExAttr4(value, True) # 跨服服务器需要广播周围玩家
    else:
        curPlayer.SetExAttr4(value)
#---最大防护值----
def GetMaxProDef(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrMaxProDef)
def SetMaxProDef(curPlayer, value):
    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrMaxProDef, value)
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_MaxProDef, value, True) # 周围玩家需要通知
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_MaxProDef, value, False) # 周围玩家需要通知
#---生命上限换算为防护值的百分比----
def GetProDefHPPer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrProDefHPPer)
def SetProDefHPPer(curPlayer, value):
    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrProDefHPPer, value)
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_ProDefHPPer, value, False)
    #curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_ProDefHPPer, value, False)
#---防护值吸收伤害比率----
def GetProDefAbsorb(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrProDefAbsorb)
def SetProDefAbsorb(curPlayer, value):
    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrProDefAbsorb, value)
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_ProDefAbsorb, value, False)
    #curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_ProDefAbsorb, value, False)
    
#---宠物攻击提升值----
def GetPetMinAtk(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrPetMinAtk)
@@ -6386,11 +6617,13 @@
def GetPetMaxAtk(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrPetMaxAtk)
def SetPetMaxAtk(curPlayer, value): curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrPetMaxAtk, value)
#---宠物伤害百分比提升----
def GetPetDamPer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrPetDamPer)
def SetPetDamPer(curPlayer, value):
    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrPetDamPer, value)
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_PetDamPer, value, False)
#---宠物伤害百分比提升----移到GameObj下
#===============================================================================
# def GetPetDamPer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrPetDamPer)
# def SetPetDamPer(curPlayer, value):
#    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrPetDamPer, value)
#    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_PetDamPer, value, False)
#===============================================================================
#---宠物技能伤害百分比提升----
def GetPetSkillAtkRate(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_PetSkillAtkRate)
def SetPetSkillAtkRate(curPlayer, value): curPlayer.SetDict(ChConfig.Def_PlayerKey_PetSkillAtkRate, value)
@@ -6409,6 +6642,18 @@
#---功能层防御值----
def GetFuncDef(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_CopyFuncAttr % (ChConfig.TYPE_Calc_AttrDEF - 1))
def SetFuncDef(curPlayer, value): curPlayer.SetDict(ChConfig.Def_PlayerKey_CopyFuncAttr % (ChConfig.TYPE_Calc_AttrDEF - 1), value)
#---诛仙一击概率---
def GetZhuXianRate(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ZhuxianRate)
def SetZhuXianRate(curPlayer, value):
    curPlayer.SetDict(ChConfig.Def_PlayerKey_ZhuxianRate, value)
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_ZhuxianRate, value, False)
#---诛仙一击伤害百分比---
def GetZhuXianHurtPer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ZhuxianHurtPer)
def SetZhuXianHurtPer(curPlayer, value):
    curPlayer.SetDict(ChConfig.Def_PlayerKey_ZhuxianHurtPer, value)
    curPlayer.SendPropertyRefresh(ShareDefine.CDBPlayerRefresh_ZhuxianHurtPer, value, False)
## 增加天梯竞技场积分
@@ -6529,12 +6774,24 @@
def GetCalcAttrListValue(curPlayer, funcIndex):
    ## 获取功能点预先计算的所加属性值
    attrList = [{} for _ in range(4)]
    for attrIndex, attrDict in enumerate(attrList):
        for i in xrange(Def_MaxAddAttrTypeCnt):
            attrType = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_CalcAddAttrType % (funcIndex, attrIndex, i))
            if attrType == 0:
                break
            attrDict[attrType] = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_CalcAddAttrValue % (funcIndex, attrIndex, i))
    if isinstance(funcIndex, int):
        funcIndexList = [funcIndex]
    elif isinstance(funcIndex, list):
        funcIndexList = funcIndex
    else:
        return attrList
    for funcIndex in funcIndexList:
        for attrIndex, attrDict in enumerate(attrList):
            for i in xrange(Def_MaxAddAttrTypeCnt):
                attrType = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_CalcAddAttrType % (funcIndex, attrIndex, i))
                if attrType == 0:
                    break
                attrValue = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_CalcAddAttrValue % (funcIndex, attrIndex, i))
                if attrType in attrDict:
                    attrDict[attrType] = attrValue + attrDict[attrType]
                else:
                    attrDict[attrType] = attrValue
    return attrList
## 刷属性时累加功能事先计算好的属性值
@@ -6586,6 +6843,13 @@
#-------------------------------------------------------------------------------
## 设置玩家字典值, 存库
def NomalDictSetProperty(curPlayer, key, value, dType=0):
    if CrossPlayerData.IsNeedProcessCrossPlayer(curPlayer) and key not in \
        [ChConfig.Def_PDict_FightPower_Total, 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