ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_BZZD.py
@@ -25,21 +25,19 @@
import EventReport
import ChConfig
import IpyGameDataPY
import AttackCommon
import PyGameData
import NPCCommon
import GameObj
import PlayerSuccess
import PlayerBossReborn
import PlayerFairyCeremony
import PlayerNewFairyCeremony
import PlayerActLogin
import PlayerWeekParty
import ItemControler
import GameFuncComm
import SkillShell
import BuffSkill
import EventShell
import PyGameData
import random
import math
#---副本配置对应key值---
(
@@ -57,121 +55,40 @@
FB_Step_Close, # 副本关闭
) = range(5)
BZZD_LastCheckExpTick = 'BZZD_LastCheckExpTick'   # 上次检查保底奖励tick
BZZD_LastCheckTick = 'BZZD_LastCheckTick'   # 上次检查npc刷新时间
#BZZD_KillCnt = 'BZZD_KillCnt'   # 击杀数
BZZD_WaveIndex = 'BZZD_WaveIndex'   # 波数
BZZD_TotalNPCCount = 'BZZD_TotalNPCCount'   # 总需击杀NPC数量
BZZD_LastNPCID = 'BZZD_LastNPCID'   # 最后一只怪NPCID,指定怪物刷完后,后续补刷的怪直接刷该NPCID,防止异常导致怪物不够杀无法结算
BZZD_IsKillAll = 'BZZD_IsKillAll'   # 是否已经击杀到指定怪物数量
BZZD_ReExp = 'BZZD_ReExp_%s' # 参考经验, 参数 (npcLV)
BZZD_NPCMaxHP = 'BZZD_NPCMaxHP_%s_%s' # NPC最大生命值, 参数(npcID, enterLV)
FBPlayerDict_KillCnt = 'FBPlayerDict_KillCnt'   # 击杀数
FBPlayerDict_EnterLV = 'FBPlayerDict_EnterLV'   # 进入副本时的等级
FBPlayerDict_TotalExp = 'FBPlayerDict_TotalExp'   # 获得的总经验
FBPlayerDict_TotalExpPoint = 'FBPlayerDict_TotalExpPoint'   # 获得的总经验点
FBPlayerDict_EncourageLV = 'FBPlayerDict_EncourageLV'   # 鼓舞等级
FirstEnterExpTimeMax = 99999 # 当单次补时满时记录的一个特殊数值,防止策划调整单次战斗时长时,老号可能会多余补经验
def GetBZZDNPCID(passSecond):
    ##当前应该刷新的NPCID
    curNpcid = 0
    curIndex = 0
    fairylandNPCList = IpyGameDataPY.GetFuncEvalCfg('FairylandNPC', 1)
    for i, info in enumerate(fairylandNPCList, 1):
        timeRange, npcid = info
        if timeRange[0] <= passSecond <= timeRange[1]:
            curNpcid = npcid
            curIndex = i
            break
    return curNpcid, curIndex
## OnDay处理
#  @param curPlayer
#  @return None
def BZZDOnDay(curPlayer):
#    joinLineID = __GetPlayerFBLineID(curPlayer)
#    if joinLineID == None:
#        GameWorld.DebugLog("BZZDOnDay() level(%s) no enough" % curPlayer.GetLV())
#        return
#
#    hadEnterCnt = FBCommon.GetEnterFBCount(curPlayer, ChConfig.Def_FBMapID_BZZD)
#    maxEnterCnt = GetMaxEnterCnt(curPlayer)
    return
def OnFBPlayerOnLogin(curPlayer):
    playerID = curPlayer.GetPlayerID()
    playerExpTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BZZD_FirstEnterExpTime)
    if not playerExpTime:
        #GameWorld.DebugLog("还没进入过仙界秘境,不处理经验补时!", playerID)
        return
    mapID = GameWorld.GetMap().GetMapID()
    mapID = FBCommon.GetRecordMapID(mapID)
    if mapID == ChConfig.Def_FBMapID_BZZD:
        GameWorld.DebugLog("仙界秘境副本中,不处理经验补时!", playerID)
        return
    fbCfg = FBCommon.GetFBLineStepTime(ChConfig.Def_FBMapID_BZZD)
    fightTime = fbCfg[Def_FightTime]
    giveTime = max(0, fightTime - playerExpTime)
    if not giveTime:
        #GameWorld.DebugLog("经验补时时间已超过单次挑战时长,不处理经验补时!playerExpTime=%s" % playerExpTime, playerID)
        return
    expPerSecond = IpyGameDataPY.GetFuncCfg("XjmjFirstEnter", 2)
    giveExp = expPerSecond * giveTime
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_FirstEnterExpTime, FirstEnterExpTimeMax)
    if giveExp:
        giveExp = PlayerControl.PlayerControl(curPlayer).AddExp(giveExp, ShareDefine.Def_ViewExpType_Sys)
        GameWorld.DebugLog("玩家首次仙界秘境经验补时:%s秒,giveExp=%s" % (giveTime, giveExp), playerID)
    return
## 是否能够通过活动查询进入
#  @param curPlayer 玩家实例
#  @param mapID 地图ID
#  @param lineID 线路id
#  @param tick 时间戳
#  @return 布尔值
def OnEnterFBEvent(curPlayer, mapID, lineID, tick):
    return __CheckCanEnterBZZD(curPlayer, mapID, lineID)
## 检查可否进入百战之地
def __CheckCanEnterBZZD(curPlayer, mapID, lineID, enterCnt=1):
    return True
## 进入传送点
#  @param curPlayer
#  @param mapID 地图ID
#  @param lineId 分线ID
#  @param tick
#  @return 坐标列表(X,Y)
def OnGetFBEnterPos(curPlayer, mapID, lineId, ipyEnterPosInfo, tick):
    if len(ipyEnterPosInfo) == 3:
        return ipyEnterPosInfo
    return ipyEnterPosInfo + [3]
##查询是否可以进入地图
# @param ask:请求结构体(IPY_BMChangeMapAsk)
# @param tick:时间戳
# @return IPY_GameWorld.cme 枚举
## 查询是否可以进入地图
def OnChangeMapAsk(ask, tick):
    return IPY_GameWorld.cmeAccept
## 开启副本
def OnOpenFB(tick):
    return
## 进副本
#  @param curPlayer
#  @param tick
#  @return None
def DoEnterFB(curPlayer, tick):
    playerID = curPlayer.GetPlayerID()
    playerLV = curPlayer.GetLV()
    curCopyMapID = GameWorld.GetGameWorld().GetCopyMapID()
    teamID = curPlayer.GetTeamID()
    GameWorld.Log("DoEnterFB...playerCopyMapID=%s,curCopyMapID=%s,playerLV=%s,teamID=%s"
                  % (curPlayer.GetCopyMapID(), curCopyMapID, playerLV, teamID), playerID)
    lineID = GameWorld.GetGameWorld().GetLineID()
    GameWorld.Log("DoEnterFB..lineID=%s,playerLV=%s" % (lineID, playerLV), playerID)
    gameFB = GameWorld.GetGameFB()
    hadDelTicket = FBCommon.GetHadDelTicket(curPlayer)
    if not hadDelTicket:
@@ -186,26 +103,34 @@
        FBCommon.AddEnterFBCount(curPlayer, ChConfig.Def_FBMapID_BZZD)
        PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_XJMJ, 1)
        PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_XJMJ, 1)
        PlayerNewFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_XJMJ, 1)
        PlayerWeekParty.AddWeekPartyActionCnt(curPlayer, ChConfig.Def_WPAct_XJMJ, 1)
        PlayerActLogin.AddLoginAwardActionCnt(curPlayer, ChConfig.Def_LoginAct_XJMJ, 1)
        EventShell.EventRespons_FBEvent(curPlayer, 'passxjmj')
        FBCommon.UpdateFBEnterTick(curPlayer)
        gameFB.SetPlayerGameFBDict(playerID, FBPlayerDict_EnterLV, playerLV)
        PyGameData.g_bzzdPlayerKillNPCCntDict.pop(playerID, 0)
        
        logType = FBCommon.GetFBJoinType(curPlayer, False)
        EventReport.WriteEvent_FB(curPlayer, ChConfig.Def_FBMapID_BZZD, 0, ChConfig.CME_Log_Start, logType)
    fbPlayerCnt = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NPCStrengthenPlayerCnt)
    isTeamEnter = (teamID and fbPlayerCnt > 1)
    if not isTeamEnter:
        CheckHurtBuff(curPlayer, tick)
        historyEnterCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BZZD_HistoryEnterCnt)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_HistoryEnterCnt, min(historyEnterCnt + 1, 999))
        __GiveFirstEnterPrize(curPlayer, historyEnterCnt + 1)
        # 初始化所需击杀NPC
        fairylandNPCList = IpyGameDataPY.GetFuncEvalCfg('FairylandNPC', 1)
        refreshNPCList = []
        for npcID, npcCount in fairylandNPCList:
            refreshNPCList += [npcID] * npcCount
        PyGameData.g_bzzdRefreshNPCListDict[lineID] = refreshNPCList
        gameFB.SetGameFBDict(BZZD_TotalNPCCount, len(refreshNPCList))
    CheckHurtBuff(curPlayer, tick)
    showState = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_Player_Dict_GuideState, ChConfig.GuideState_BZZDShow)
    if not showState:
        if not isTeamEnter:
            FBCommon.SendFBEncourageInfo(curPlayer, 0)
            GameWorld.Log("首次单人进入该副本,需要等前端播完副本场景引导秀才正常进入准备阶段!", playerID)
            return
        else:
            GameWorld.Log("队伍方式进入该副本,不播放副本场景引导秀!", playerID)
        FBCommon.SendFBEncourageInfo(curPlayer, 0)
        GameWorld.Log("前端场景秀还没播完,需要等前端播完副本场景引导秀才正常进入准备阶段!", playerID)
        return
    fbStep = gameFB.GetFBStep()
    if fbStep < FB_Step_Prepare:
        FBCommon.SetFBStep(FB_Step_Prepare, tick)
@@ -220,40 +145,35 @@
        mapID = GameWorld.GetMap().GetMapID()
        notify_tick = FBCommon.GetFBLineStepTime(mapID)[Def_FightTime] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
        curPlayer.Sync_TimeTick(IPY_GameWorld.tttTowerTake, 0, max(notify_tick, 0), True)
        FBCommon.UpdateFBGrade(tick, FBCommon.GetFBLineGrade(mapID), curPlayer)
    # 上鼓舞buff
    encourageLV = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_EncourageLV)
    if encourageLV > 0:
        FBCommon.AddFbEncourageBuff(curPlayer, FBPlayerDict_EncourageLV, tick)
    else:
        FBCommon.SendFBEncourageInfo(curPlayer, encourageLV)
    DoFBHelp(curPlayer, tick)
    isFirstEnter = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BZZD_FirstEnterExpTime)
    # 没有首次进入经验时间记录,可视为首次进入
    if not isFirstEnter:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_FirstEnterExpTime, 1)
        __GiveFirstEnterPrize(curPlayer)
        
    DoFBHelp(curPlayer, tick)
    return
def __GiveFirstEnterPrize(curPlayer):
    XjmjFirstEnterPrize = IpyGameDataPY.GetFuncEvalCfg("XjmjFirstEnter", 1)
    if not XjmjFirstEnterPrize:
def __GiveFirstEnterPrize(curPlayer, historyEnterCnt):
    XjmjFirstEnterPrizeDict = IpyGameDataPY.GetFuncEvalCfg("XjmjFirstEnter", 1, {})
    if historyEnterCnt not in XjmjFirstEnterPrizeDict:
        return
    silverMoney, goldPaper, itemID = XjmjFirstEnterPrize
    silverMoney, goldMoney, itemID = XjmjFirstEnterPrizeDict[historyEnterCnt]
    if silverMoney:
        PlayerControl.GiveMoney(curPlayer, IPY_GameWorld.TYPE_Price_Silver_Money, silverMoney)
    if goldPaper:
        PlayerControl.GiveMoney(curPlayer, IPY_GameWorld.TYPE_Price_Gold_Paper, goldPaper)
    if goldMoney:
        PlayerControl.GiveMoney(curPlayer, IPY_GameWorld.TYPE_Price_Gold_Money, goldMoney)
    if itemID:
        ItemControler.GivePlayerItem(curPlayer, itemID, 1, 1, [IPY_GameWorld.rptItem])
    GameWorld.DebugLog("首次进入给奖励:silverMoney=%s,goldPaper=%s,itemID=%s"
                       % (silverMoney, goldPaper, itemID), curPlayer.GetPlayerID())
        ItemControler.GivePlayerItem(curPlayer, itemID, 1, 0, [IPY_GameWorld.rptItem])
    GameWorld.DebugLog("首次进入给奖励:silverMoney=%s,goldMoney=%s,itemID=%s"
                       % (silverMoney, goldMoney, itemID), curPlayer.GetPlayerID())
    return
def CheckHurtBuff(curPlayer, tick, isAdd=True):
    #人物等级低于世界等级X级,单人挑战仙界密境获得伤害BUFF加成
    ## 人物等级低于世界等级X级,单人挑战仙界密境获得伤害BUFF加成
    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_WorldLV):
        return
    playerID = curPlayer.GetID()
@@ -263,8 +183,8 @@
    curLV = curPlayer.GetLV()
    worldlv = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
    buffID = IpyGameDataPY.GetFuncCfg('XjmjAddHarm', 2)
    lvRange = IpyGameDataPY.GetFuncCfg('XjmjAddHarm')
    if isAdd and worldlv - curLV >=lvRange:
    lvRange = IpyGameDataPY.GetFuncCfg('XjmjAddHarm', 1)
    if isAdd and worldlv - curLV >= lvRange:
        curSkill = GameWorld.GetGameData().GetSkillBySkillID(buffID)
        SkillShell.__DoLogic_AddBuff(curPlayer, curPlayer, curSkill, False, tick, 0, 0)
    if not isAdd and worldlv - curLV < lvRange:
@@ -286,18 +206,16 @@
    curPlayer.Sync_TimeTick(IPY_GameWorld.tttAddUpTime, 0, max(notify_tick, 0), True)
    curPlayer.Sync_TimeTick(IPY_GameWorld.tttWaitStart, 0, max(notify_tick, 0), True)
    DoFBHelp(curPlayer, tick)
    isFirstEnter = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BZZD_FirstEnterExpTime)
    # 没有首次进入经验时间记录,可视为首次进入
    if not isFirstEnter:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_FirstEnterExpTime, 1)
        __GiveFirstEnterPrize(curPlayer)
    return
##玩家退出副本
# @param curPlayer 玩家实例
# @param tick 时间戳
# @return 无意义
## 关闭副本
def OnCloseFB(tick):
    #GameWorld.GetGameWorld().SetPropertyID(0)
    lineID = GameWorld.GetGameWorld().GetLineID()
    PyGameData.g_bzzdRefreshNPCListDict.pop(lineID, None)
    return
## 玩家退出副本
def DoExitFB(curPlayer, tick):
    # 清除鼓舞buff
    FBCommon.ClearEncourageBuff(curPlayer, tick)
@@ -306,47 +224,36 @@
    FBCommon.UpdFBLineNPCStrengthenLV(curPlayer.GetPlayerID(), True)
    return
##玩家主动离开副本.
# @param curPlayer 玩家实例
# @param tick 时间戳
# @return 返回值无意义
## 玩家主动离开副本
def DoPlayerLeaveFB(curPlayer, tick):
    return
## 玩家升级
def OnPlayerLVUp(curPlayer):
    CheckHurtBuff(curPlayer, GameWorld.GetGameWorld().GetTick(), False)
    FBCommon.UpdFBLineNPCStrengthenLV(curPlayer.GetPlayerID(), False)
    return
## 获得副本帮助信息
#  @param curPlayer 当前玩家(被通知对象)
#  @param tick 当前时间
#  @return None
def DoFBHelp(curPlayer, tick):
    gameFB = GameWorld.GetGameFB()
    playerID = curPlayer.GetID()
    
    # 获得副本信息
    #killNPCCnt = gameFB.GetPlayerGameFBDictByKey(playerID, BZZD_KillCnt)
    killNPCCnt = int(PyGameData.g_bzzdPlayerKillNPCCntDict.get(playerID, 0))
    passCnt = gameFB.GetGameFBDictByKey(BZZD_WaveIndex)
    passAllCnt = len(IpyGameDataPY.GetFuncEvalCfg('FairylandNPC'))
    killNPCCnt = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_KillCnt)
    totalNPCCount = gameFB.GetGameFBDictByKey(BZZD_TotalNPCCount)
    exp = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_TotalExp)
    expPoint = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_TotalExpPoint)
    grade = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_Grade)
    #副本帮助
    helpDict = {FBCommon.Help_npcTotal:killNPCCnt, FBCommon.Help_exp:exp, FBCommon.Help_expPoint:expPoint, FBCommon.Help_wheel:passCnt, 'passAllCnt':passAllCnt}
    helpDict = {FBCommon.Help_npcTotal:killNPCCnt, FBCommon.Help_npcTotalNeed:totalNPCCount,
                FBCommon.Help_exp:exp, FBCommon.Help_expPoint:expPoint, FBCommon.Help_grade:grade}
    GameWorld.DebugLog("DoFBHelp %s" % str(helpDict))
    FBCommon.Notify_FBHelp(curPlayer, helpDict)
    return
##---副本总逻辑计时器---
# @param tick:时间戳
# @return 无意义
# @remarks 副本总逻辑计时器
def OnProcess(tick):
    fbStep = GameWorld.GetGameFB().GetFBStep()
    
@@ -363,8 +270,6 @@
    return
## 副本准备逻辑
#  @param tick:时间戳
#  @return 无意义
def __DoLogic_FB_Prepare(tick):
    #gameFB = GameWorld.GetGameFB()
    mapID = GameWorld.GetMap().GetMapID()
@@ -375,133 +280,63 @@
    
    # 设置开始刷怪
    CheckNPCRefresh(tick)
    
    # 副本开始
    FBCommon.SetFBStep(FB_Step_Fighting, tick)
    FBCommon.Sync_Player_TimeTick(IPY_GameWorld.tttTowerTake, fbCfg[Def_FightTime] * 1000)
    return
def CheckNPCRefresh(tick):
    #npc刷新检查
    gameFB = GameWorld.GetGameFB()
    lastCheckTick = gameFB.GetGameFBDictByKey(BZZD_LastCheckTick)
    playerManager = GameWorld.GetMapCopyPlayerManager()
    heroCnt = playerManager.GetPlayerCount()
    checkCD = eval(IpyGameDataPY.GetFuncCompileCfg('FairylandNPC', 2))
    
    if lastCheckTick and tick - lastCheckTick < checkCD:
    lineID = GameWorld.GetGameWorld().GetLineID()
    if lineID not in PyGameData.g_bzzdRefreshNPCListDict:
        return
    gameFB.SetGameFBDict(BZZD_LastCheckTick, tick)
    passSecond = (tick - GameWorld.GetGameFB().GetFBStepTick())/1000
    npcID, waveIndex = GetBZZDNPCID(passSecond)
    if not npcID:
        return
    if gameFB.GetGameFBDictByKey(BZZD_WaveIndex) != waveIndex:
        gameFB.SetGameFBDict(BZZD_WaveIndex, waveIndex)
        for i in range(playerManager.GetPlayerCount()):
            curPlayer = playerManager.GetPlayerByIndex(i)
            DoFBHelp(curPlayer, tick)
    rMarkList = IpyGameDataPY.GetFuncEvalCfg('FairylandPoint')
    maxCnt = IpyGameDataPY.GetFuncCfg('FairylandPoint',2)
    firstRMarkList = IpyGameDataPY.GetFuncEvalCfg('FairylandPoint',3)
    allRmark = rMarkList+firstRMarkList
    npcCntDict = {mark:0 for mark in allRmark} #标识点对应数量
    npcCnt = 0
    refreshNPCList = PyGameData.g_bzzdRefreshNPCListDict[lineID]
    #if not refreshNPCList:
    #    return
    isRefresh = False
    gameFB = GameWorld.GetGameFB()
    gameNPC = GameWorld.GetNPCManager()
    for i in range(0, gameNPC.GetCustomNPCRefreshCount()):
    customNPCRefreshCount = gameNPC.GetCustomNPCRefreshCount()
    for i in xrange(customNPCRefreshCount):
        npcRefresh = gameNPC.GetCustomNPCRefreshAt(i)
        npcCnt += npcRefresh.GetCount()
        rmark = npcRefresh.GetRefreshMark()
        npcCntDict[rmark] = npcCntDict.get(rmark, 0)+npcRefresh.GetCount()
    if not lastCheckTick:
        needAddCnt = IpyGameDataPY.GetFuncCfg('FairylandNPC', 3)
    else:
        needAddCnt = eval(IpyGameDataPY.GetFuncCompileCfg('FairylandNPCCnt'))
    if not needAddCnt:
        return
    markCntList = sorted(npcCntDict.iteritems(), key=lambda asd:asd[1])
    #GameWorld.Log('11111111111111111markCntList=%s'%markCntList)
    hasRefreshCnt = 0
    for rMark, curCnt in markCntList:
        if hasRefreshCnt >=needAddCnt:
            break
        cnt = min(maxCnt, needAddCnt-hasRefreshCnt)
        hasRefreshCnt +=cnt
        curMaxCnt = curCnt+cnt
        NPCCustomRefresh.SetNPCRefresh(rMark, [(npcID, cnt)], curMaxCnt, cnt)
    NPCCustomRefresh.ProcessAllNPCRefresh(tick) # 立即出发一次标识点刷新
    return
def CheckFirstEnterExp(tick, isOver=False):
    gameFB = GameWorld.GetGameFB()
    if not isOver:
        lastCheckExpTick = gameFB.GetGameFBDictByKey(BZZD_LastCheckExpTick)
        if not lastCheckExpTick:
            gameFB.SetGameFBDict(BZZD_LastCheckExpTick, tick)
            return
        if tick - lastCheckExpTick < 5000:
            return
    gameFB.SetGameFBDict(BZZD_LastCheckExpTick, tick) # 这个全局tick只是处理多久执行一次而已,不参与其他逻辑
    fightTime = int(math.ceil((tick - GameWorld.GetGameFB().GetFBStepTick()) / 1000.0)) # 已经战斗时长,秒
    expPerSecond = IpyGameDataPY.GetFuncCfg("XjmjFirstEnter", 2)
    minTotalExp = expPerSecond * fightTime # 理论保底最少经验
    playerManager = GameWorld.GetMapCopyPlayerManager()
    playerCount = playerManager.GetPlayerCount()
    for i in xrange(playerCount):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if not curPlayer:
        if npcRefresh.GetCount():
            continue
        playerID = curPlayer.GetPlayerID()
        playerExpTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BZZD_FirstEnterExpTime)
        if playerExpTime >= fightTime:
            #GameWorld.DebugLog("已经处理完保底经验时间,不再处理!", playerID)
            continue
        updExpTime = FirstEnterExpTimeMax if isOver else fightTime
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_FirstEnterExpTime, updExpTime)
        exp = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_TotalExp)
        expPoint = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_TotalExpPoint)
        playerTotalExp = expPoint * ChConfig.Def_PerPointValue + exp
        giveExp = max(0, minTotalExp - playerTotalExp)
        if giveExp > 0:
            giveExp = PlayerControl.PlayerControl(curPlayer).AddExp(giveExp, ShareDefine.Def_ViewExpType_Sys)
            __RecordAddExp(curPlayer, giveExp, True)
        rMark = npcRefresh.GetRefreshMark()
        if refreshNPCList:
            npcID = refreshNPCList.pop(0) # 直接按顺序取NPCID
            gameFB.SetGameFBDict(BZZD_LastNPCID, npcID)
        else:
            npcID = gameFB.GetGameFBDictByKey(BZZD_LastNPCID)
            GameWorld.DebugLog("怪全部刷完了,使用最后一次刷怪的NPCID=%s" % npcID)
            
        GameWorld.DebugLog("保底经验: updExpTime=%s,minTotalExp=%s,playerTotalExp=%s,giveExp=%s"
                           % (updExpTime, minTotalExp, playerTotalExp, giveExp), playerID)
        if not npcID:
            break
        isRefresh = True
        NPCCustomRefresh.SetNPCRefresh(rMark, [npcID])
    if isRefresh:
        NPCCustomRefresh.ProcessAllNPCRefresh(tick) # 立即触发一次标识点刷新
    return
## 副本进行中
#  @param tick:时间戳
#  @return 无意义
def __DoLogic_FB_Fighting(tick):
    #gameFB = GameWorld.GetGameFB()
    mapID = GameWorld.GetMap().GetMapID()
    FBCommon.UpdateFBGrade(tick, FBCommon.GetFBLineGrade(mapID))
    fbCfg = FBCommon.GetFBLineStepTime(mapID)
    # 间隔未到
    if tick - GameWorld.GetGameFB().GetFBStepTick() < fbCfg[Def_FightTime] * 1000:
        CheckNPCRefresh(tick)
        CheckFirstEnterExp(tick)
        return
    __DoBZZDOver()
    return
        
##副本关闭中
# @param tick:时间戳
# @return 无意义
# @remarks 副本关闭中
def __DoLogic_FB_Over(tick):
    #gameFB = GameWorld.GetGameFB()
@@ -517,13 +352,15 @@
    return
## 检查是否可攻击, 主判定不可攻击的情况,其他逻辑由外层决定
#  @param attacker 攻击方
#  @param defender 防守方
#  @return bool
def CheckCanAttackTagObjInFB(attacker, defender):
    gameFB = GameWorld.GetGameFB()
    if gameFB.GetFBStep() != FB_Step_Fighting:
        return False
    if gameFB.GetGameFBDictByKey(BZZD_IsKillAll):
        #GameWorld.DebugLog("击杀怪物数已达到最大,无法再攻击!")
        return False
    return True
@@ -531,75 +368,48 @@
def OnGetNPCExp(curPlayer, curNPC):
    if not curPlayer:
        return 0
    #参数
    #baseExp     基础值
    #expMulti    仙界秘境经验效率
    #hurtValue   伤害值
    #npcMaxHP    怪物血量, 玩家进入副本时的等级对应怪物成长血量
    #reExp       等级经验效率, 取副本NPC等级
    #playerCnt   队员人数
    playerID = curPlayer.GetPlayerID()
    npcLV = max(curNPC.GetCurLV(), curNPC.GetLV())
    npcID = curNPC.GetNPCID()
    gameFB = GameWorld.GetGameFB()
    reExp = gameFB.GetGameFBDictByKey(BZZD_ReExp % npcLV)
    if not reExp:
        lvIpyData = PlayerControl.GetPlayerLVIpyData(npcLV)
        reExp = 0 if not lvIpyData else lvIpyData.GetReExp()
        gameFB.SetGameFBDict(BZZD_ReExp % npcLV, reExp)
        GameWorld.DebugLog("初始化参数: npcID=%s,npcLV=%s,reExp=%s" % (npcID, npcLV, reExp), playerID)
    playerCnt = max(1, gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NPCStrengthenPlayerCnt))
    enterLV = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_EnterLV)
    npcMaxHP = gameFB.GetGameFBDictByKey(BZZD_NPCMaxHP % (npcID, enterLV))
    if not npcMaxHP:
        enterLVAttrDict = NPCCommon.GetNPCStrengthenAttrDict(npcID, enterLV)
        npcMaxHP = enterLVAttrDict.get("MaxHP", 0)
        gameFB.SetGameFBDict(BZZD_NPCMaxHP % (npcID, enterLV), npcMaxHP)
        GameWorld.DebugLog("初始化参数: npcID=%s,enterLV=%s,npcMaxHP=%s" % (npcID, enterLV, npcMaxHP), playerID)
        if not npcMaxHP:
            GameWorld.ErrLog("无法获得NPC最大生命值,npcID=%s,enterLV=%s" % (npcID, enterLV), playerID)
            return 0
    isKillAll = gameFB.GetGameFBDictByKey(BZZD_IsKillAll)
    if isKillAll:
        GameWorld.DebugLog("已经杀到指定怪物数了,无法再获得经验! isKillAll=%s" % isKillAll)
        return 0
    npcID = curNPC.GetNPCID()
    playerID = curPlayer.GetPlayerID()
    reExp = PlayerControl.GetPlayerReExp(curPlayer)
    baseExp = curNPC.GetExp()
    expMulti = IpyGameDataPY.GetFuncCfg("XjmjMonsterExp", 2)
    curNPCMaxHP = GameObj.GetMaxHP(curNPC)
    teamPlayerHurtValue = AttackCommon.GetTeamPlayerHurtValue(curPlayer, curNPC)
    hurtValue = npcMaxHP if not curPlayer.GetTeamID() else teamPlayerHurtValue
    #GameWorld.DebugLog("击杀NPC: npcID=%s,baseExp=%s,expMulti=%s,hurtValue=%s,npcMaxHP=%s,curNPCMaxHP=%s,playerCnt=%s"
    #                   % (npcID, baseExp, expMulti, hurtValue, npcMaxHP, curNPCMaxHP, playerCnt), playerID)
    addExp = eval(IpyGameDataPY.GetFuncCompileCfg("XjmjMonsterExp", 1))
    
    addExp = eval(IpyGameDataPY.GetFuncCompileCfg("XjmjMonsterExp"))
    #GameWorld.DebugLog("    addExp=%s" % (addExp), playerID)
    if addExp <= 0:
        GameWorld.ErrLog("经验计算异常:  npcID=%s,npcLV=%s,reExp=%s,baseExp=%s,expMulti=%s,hurtValue=%s,npcMaxHP=%s,curNPCMaxHP=%s,playerCnt=%s"
                         % (npcID, npcLV, reExp, baseExp, expMulti, hurtValue, npcMaxHP, curNPCMaxHP, playerCnt), playerID)
    #GameWorld.DebugLog("击杀NPC: npcID=%s,reExp=%s,baseExp=%s,expMulti=%s,addExp=%s"
    #                   % (npcID, reExp, baseExp, expMulti, addExp), playerID)
    #累计击杀数
    curHurtValue = curNPCMaxHP if not curPlayer.GetTeamID() else teamPlayerHurtValue
    addKillCnt = curHurtValue / float(curNPCMaxHP)
    PyGameData.g_bzzdPlayerKillNPCCntDict[playerID] = PyGameData.g_bzzdPlayerKillNPCCntDict.get(playerID, 0) + addKillCnt
    #GameWorld.DebugLog("    增加玩家杀怪数: %s / %s = %s, %s" % (curHurtValue, curNPCMaxHP, addKillCnt, PyGameData.g_bzzdPlayerKillNPCCntDict), playerID)
    killNPCCnt = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_KillCnt) + 1
    gameFB.SetPlayerGameFBDict(playerID, FBPlayerDict_KillCnt, killNPCCnt)
    DoFBHelp(curPlayer, GameWorld.GetGameWorld().GetTick())
    totalNPCCount = gameFB.GetGameFBDictByKey(BZZD_TotalNPCCount)
    if killNPCCnt >= totalNPCCount:
        gameFB.SetGameFBDict(BZZD_IsKillAll, killNPCCnt)
        GameWorld.DebugLog("怪物已击杀到指定数量: killNPCCnt=%s,totalNPCCount=%s" % (killNPCCnt, totalNPCCount))
    return addExp
## 获得经验
#  @param curPlayer 当前玩家
#  @param addExp 获得的经验
#  @param expViewType 经验类型
#  @return True or False
def OnGetExp(curPlayer, addExp, expViewType):
    
    if expViewType != ShareDefine.Def_ViewExpType_KillNPC:
        return
    
    __RecordAddExp(curPlayer, addExp)
    return
def __RecordAddExp(curPlayer, addExp, isSys=False):
    playerID = curPlayer.GetID() 
    gameFB = GameWorld.GetGameFB()
    if gameFB.GetFBStep() != FB_Step_Fighting:
        #GameWorld.DebugLog("非战斗阶段,不处理! addExp=%s" % addExp)
        return
    exp = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_TotalExp)
    expPoint = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_TotalExpPoint)
    totalExp = expPoint * ChConfig.Def_PerPointValue + exp
@@ -609,37 +419,16 @@
    gameFB.SetPlayerGameFBDict(playerID, FBPlayerDict_TotalExp, updExp)
    gameFB.SetPlayerGameFBDict(playerID, FBPlayerDict_TotalExpPoint, updExpPoint)
    
    GameWorld.DebugLog("OnGetExp() totalExp=%s,addExp=%s,updTotalExp=%s,isSys=%s"
                       % (totalExp, addExp, updTotalExp, isSys), playerID)
    #GameWorld.DebugLog("OnGetExp() totalExp=%s,addExp=%s,updTotalExp=%s" % (totalExp, addExp, updTotalExp), playerID)
    if gameFB.GetGameFBDictByKey(BZZD_IsKillAll):
        GameWorld.DebugLog("怪物已全部杀完,结算副本!")
        __DoBZZDOver()
    return
### 执行副本杀怪逻辑
##  @param curPlayer 杀怪的人
##  @param curNPC 被杀的怪
##  @param tick 当前时间
##  @return None
#def DoFB_DropOwner(curPlayer, curNPC):
#    playerID = curPlayer.GetID()
#    gameFB = GameWorld.GetGameFB()
#    killNPCCnt = gameFB.GetPlayerGameFBDictByKey(playerID, BZZD_KillCnt)
#    gameFB.SetPlayerGameFBDict(playerID, BZZD_KillCnt, killNPCCnt + 1)
#
#    DoFBHelp(curPlayer, GameWorld.GetGameWorld().GetTick())
#    return
#关系有3层,无-友好-敌人
##判断是否可释放(增/减)技能或普攻
# @param curPlayer 玩家实例
# @param curTagPlayer 目标玩家实例
# @return 布尔值
def CheckPlayersRelation_IsNone(curPlayer, curTagPlayer):
    #PlayerControl.NotifyCode(curPlayer, "PK_lhs_272921")
    return True
## 副本结束处理
def __DoBZZDOver():
    tick = GameWorld.GetGameWorld().GetTick()
    CheckFirstEnterExp(tick, True) # 结算前强制处理一次
    
    playerManager = GameWorld.GetMapCopyPlayerManager()
    playerCount = playerManager.GetPlayerCount()
@@ -650,6 +439,10 @@
    fbCfg = FBCommon.GetFBLineStepTime(mapID)
    gameFB = GameWorld.GetGameFB()
    costTime = tick - gameFB.GetFBStepTick()
    FBCommon.UpdateFBGrade(tick, FBCommon.GetFBLineGrade(mapID)) # 结算前强制刷新一次评级
    grade = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FB_Grade)
    rewardRateList = FBCommon.GetFBGradeRewardRateList(mapID)
    maxGrade = len(rewardRateList)
    
    for i in xrange(playerCount):
        curPlayer = playerManager.GetPlayerByIndex(i)
@@ -661,31 +454,42 @@
        exp = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_TotalExp)
        expPoint = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_TotalExpPoint)
        totalExp = expPoint * ChConfig.Def_PerPointValue + exp
        gradeAddExpRate = rewardRateList[maxGrade - grade]
        gradeExp = int(totalExp * gradeAddExpRate / 100.0)
        totalExp += gradeExp
        playerControl = PlayerControl.PlayerControl(curPlayer)
        playerControl.AddExp(gradeExp)
        
        expRecord = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BZZD_TotalFightExp)
        expPointRecord = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BZZD_TotalFightExpPoint)
        totalExpRecord = expPointRecord * ChConfig.Def_PerPointValue + expRecord
        upPer = 0 #提升比例
        if totalExp > totalExpRecord:#超过旧记录
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_TotalFightExp, exp)
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_TotalFightExpPoint, expPoint)
            upPer = (totalExp - totalExpRecord)*100/totalExpRecord if totalExpRecord else 0
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_TotalFightExp, totalExp % ChConfig.Def_PerPointValue)
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BZZD_TotalFightExpPoint, totalExp / ChConfig.Def_PerPointValue)
            upPer = (totalExp - totalExpRecord) * 100 / totalExpRecord if totalExpRecord else 0
            #单场总经验成就
            PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_XJMJGetExp, 1, [expPoint])
            
        #killNPCCnt = gameFB.GetPlayerGameFBDictByKey(playerID, BZZD_KillCnt)
        killNPCCnt = int(PyGameData.g_bzzdPlayerKillNPCCntDict.get(playerID, 0))
        killNPCCnt = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_KillCnt)
        
        # 通知结果
        enterLV = gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_EnterLV)
        __SendBZZDOverInfo(curPlayer, {FBCommon.Over_enterLV:enterLV,FBCommon.Over_exp:exp, FBCommon.Over_expPoint:expPoint,FBCommon.Over_costTime:costTime, FBCommon.Over_npcTotal:killNPCCnt, 'upPer':upPer})
        overDict = {FBCommon.Over_enterLV:enterLV, FBCommon.Over_exp:totalExp % ChConfig.Def_PerPointValue,
                    FBCommon.Over_expPoint: totalExp / ChConfig.Def_PerPointValue,
                    'gradeExp':gradeExp % ChConfig.Def_PerPointValue, 'gradeExpPoint':gradeExp / ChConfig.Def_PerPointValue,
                    FBCommon.Over_costTime:costTime, FBCommon.Over_npcTotal:killNPCCnt, 'upPer':upPer, FBCommon.Over_grade:grade}
        __SendBZZDOverInfo(curPlayer, overDict)
    # 进入离开阶段
    FBCommon.SetFBStep(FB_Step_Over, tick)
    FBCommon.Sync_Player_TimeTick(IPY_GameWorld.tttLeaveMap, fbCfg[Def_LeaveTime] * 1000)
    
    rMarkList = IpyGameDataPY.GetFuncEvalCfg('FairylandPoint')
    for rMark in rMarkList:
    gameNPC = GameWorld.GetNPCManager()
    for i in xrange(gameNPC.GetCustomNPCRefreshCount()):
        npcRefresh = gameNPC.GetCustomNPCRefreshAt(i)
        rMark = npcRefresh.GetRefreshMark()
        NPCCustomRefresh.CloseNPCRefresh(rMark, tick)
    return
@@ -699,16 +503,11 @@
## 副本行为
#  @param curPlayer 玩家
#  @param actionType 行为类型
#  @param actionInfo 行为信息
#  @param tick 当前时间
#  @return None
def DoFBAction(curPlayer, actionType, actionInfo, tick):
    if actionType == 0:
        #第一次在百战之地鼓舞一次就满级
        FBCommon.FbEncourageBuff(curPlayer, FBPlayerDict_EncourageLV, actionInfo, tick)
        return
        return
    
    return