ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
@@ -205,11 +205,6 @@
                PlayerControl.NotifyCode(curPlayer, notifyMark, [mapID])
            return ShareDefine.EntFBAskRet_NoEnterCnt
        
        #进入CD判断
        if CheckIsEnterCD(curPlayer, mapID):
            if isNotify:
                PlayerControl.NotifyCode(curPlayer, "SingleEnterCD", [mapID])
            return ShareDefine.EntFBAskRet_EnterCD
    # 功能线路通用检查
    if fbLineIpyData:
        #等级判断
@@ -482,15 +477,6 @@
    GameWorld.GetGameFB().SetPlayerGameFBDict(curPlayer.GetID(), ChConfig.FBPlayerDict_IsDelTicket, delSign)
    return
## 自定义场景阶段
def GetCustomMapStep(curPlayer, mapID, lineID):
    return curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_CustomMapStep % (mapID, lineID))
def SetCustomMapStep(curPlayer, mapID, lineID, step):
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_CustomMapStep % (mapID, lineID), step)
    if step == ChConfig.CustomMapStep_Over:
        PlayerControl.SetCustomMap(curPlayer, 0, 0)
    return
def GetCurSingleFBPlayer():
    ''' 获取当前单人副本玩家 '''
    curPlayer = None
@@ -564,44 +550,6 @@
            FBLogic.DoFBHelp(curPlayer, tick)
            if updGrade != lowest:
                curPlayer.Sync_TimeTick(timeType, 0, diffSecond * 1000, True)
    return updGrade
def UpdateCustomFBGrade(curPlayer, tick, gradeTimeList, timeType=IPY_GameWorld.tttFlagTake):
    '''更新当前副本星级、评级
    @param gradeTimeList: 评级分段时间列表,单位秒 [最高评级可用时间, 下级可用时间, ..., 最低级可用时间]
    @param curPlayer: 触发的玩家,一般是DoEnter时调用,会同步更新一次副本评级并将信息通知该玩家
    @note: 星级:1-1星;2-2星 ...            [60, 20, 10]
            评级:1-D;2-C;3-B;4-A;5-S;    [60, 30, 30, 20, 10]
    '''
    lowest = 1
    curGrade = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneGrade)
    #最后一个评级了,不再处理
    if curGrade == lowest:
        return curGrade
    fbStepTick = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneStepTick)
    useSecond = int((tick - fbStepTick) / 1000.0) # 战斗阶段已耗总秒数
    diffSecond = 0
    updGrade = len(gradeTimeList)
    gSecondTotal = 0
    for gSecond in gradeTimeList:
        gSecondTotal += gSecond
        diffSecond = gSecondTotal - useSecond
        # 还在当前评级段
        if diffSecond > 0:
            break
        updGrade -= 1 # 用时超过当前评级段,降级
    updGrade = max(lowest, updGrade) # 保底最低级
    if curGrade == updGrade:
        return curGrade
    curPlayer.SetDict(ChConfig.Def_PlayerKey_ClientCustomSceneGrade, updGrade)
    GameWorld.DebugLog("UpdateCustomFBGrade useSecond=%s,gradeTimeList=%s,curGrade=%s,updGrade=%s,diffSecond=%s"
                       % (useSecond, gradeTimeList, curGrade, updGrade, diffSecond))
    Notify_FBHelp(curPlayer, {Help_grade:updGrade})
    if updGrade != lowest:
        curPlayer.Sync_TimeTick(timeType, 0, diffSecond * 1000, True)
    return updGrade
def NotifyFBOver(curPlayer, dataMapID, lineID, isPass, overDict={}):
@@ -1579,54 +1527,15 @@
    if not fbIpyData:
        return 0
    maxTimes = fbIpyData.GetDayTimes()
    MWPrivilegeID = fbIpyData.GetExtraTimesMWPriID()
    
    mwAddCnt = 0#wmpIpyData.GetEffectValue() if wmpIpyData else 0
    extraTimesVIPPriID = fbIpyData.GetExtraTimesVIPPriID()
    extraCnt = 0
    buyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_BuyFbCntDay % mapID)
    recoverFbCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RecoverFbCnt % mapID)
    itemAddCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ItemAddFbCnt % mapID)
    regainFbCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RegainFbCnt % mapID)
    regainFbCnt = 0
    investFBCnt = PlayerGoldInvest.GetAddFBCnt(curPlayer, mapID)
    maxCnt = maxTimes + regainFbCnt + extraCnt + buyCnt + recoverFbCnt + mwAddCnt + itemAddCnt + investFBCnt
    maxCnt = maxTimes + regainFbCnt + extraCnt + buyCnt + mwAddCnt + itemAddCnt + investFBCnt
    return maxCnt
def GetFBDetailCntInfo(curPlayer, mapID):
    #返回FB剩余正常次数、时间恢复次数、vip额外次数、已买次数、道具增加次数、未买次数
    fbIpyData = GetFBIpyData(mapID)
    enterCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_EnterFbCntDay % mapID)
    maxTimes = fbIpyData.GetDayTimes()
    maxRegainFbCnt = 0
    recoverIntervalDict = IpyGameDataPY.GetFuncEvalCfg('FBCntRegainInterval', 1)
    if str(mapID) in recoverIntervalDict:
        maxRegainFbCnt = recoverIntervalDict[str(mapID)][0]
    MWPrivilegeID = fbIpyData.GetExtraTimesMWPriID()
    mwAddCnt = 0#wmpIpyData.GetEffectValue() if wmpIpyData else 0
    maxTimes += mwAddCnt #法宝增加的次数加到基础次数里
    extraTimesVIPPriID = fbIpyData.GetExtraTimesVIPPriID()
    extraCnt = 0
    buyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_BuyFbCntDay % mapID)
    recoverFbCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RecoverFbCnt % mapID)
    itemAddCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ItemAddFbCnt % mapID)
    #先用找回来的次数,再用Vip额外次数,再用每日刷新次数、时间恢复次数、和Vip买回来的次数,最后用卷轴加的次数
    cntList = [recoverFbCnt, extraCnt, maxTimes, maxRegainFbCnt, buyCnt, itemAddCnt]
    rCntList = copy.deepcopy(cntList)
    sumCnt = 0
    for i, cnt in enumerate(cntList):
        sumCnt += cnt
        rCntList[i] = max(0, sumCnt - enterCnt)
        if enterCnt <= sumCnt:
            break
    rRecoverFbCnt, rExtraCnt, rCommonCnt, rRegainFbCnt, rBuyCnt, rItemAddCnt = rCntList
    #未买次数
    buyTimesVIPPriID = fbIpyData.GetBuyTimesVIPPriID()
    canBuyCnt = 0
    noBuyCnt = max(0, canBuyCnt - buyCnt)
    return [[rCommonCnt, rRegainFbCnt, rExtraCnt, rBuyCnt, rItemAddCnt, noBuyCnt], [maxTimes, maxRegainFbCnt, extraCnt, canBuyCnt, 10000, canBuyCnt]]
## 玩家进入副本次数
#  @param curPlayer 玩家实例
@@ -1646,7 +1555,6 @@
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_ItemAddFbCnt % mapID, itemAddCnt + addCnt)
    Sync_FBPlayerFBInfoData(curPlayer, mapID)
    PlayerControl.NotifyCode(curPlayer, 'AddActivityCount_1', [itemID, mapID, addCnt])
    OnFBCountChangeEffectRecoverCount(curPlayer, mapID)
    return
## 增加玩家进入副本次数
@@ -1683,139 +1591,7 @@
    GameWorld.DebugLog("    AddEnterFBCount fbID=%s, addCount=%s, lineBit=%s, enterCnt=%s,updValue=%s,enterCntTotal=%s" 
                       % (fbID, addCount, lineBit, enterCnt, updValue, enterCntTotal), curPlayer.GetPlayerID())
    Sync_FBPlayerFBInfoData(curPlayer, fbID)
    OnFBCountChangeEffectRecoverCount(curPlayer, fbID)
    return True
def OnFBCountChangeEffectRecoverCount(curPlayer, mapID):
    ## 副本相关次数变更,包含增加、扣除等影响副本按时间恢复次数相关逻辑处理
    recoverIntervalDict = IpyGameDataPY.GetFuncEvalCfg('FBCntRegainInterval', 1)
    if str(mapID) not in recoverIntervalDict:
        return
    maxCanRecoverCnt, recoverInterval = recoverIntervalDict[str(mapID)]
    curRegainFbCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RegainFbCnt % mapID) # 当前已恢复次数
    if curRegainFbCnt >= maxCanRecoverCnt:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FbCntRegainStartTime % mapID, 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FbCntRegainTotalTime % mapID, 0)
        GameWorld.DebugLog("副本时间恢复次数已达当日上限!")
    else:
        curTime = int(time.time())
        fbIpyData = GetFBIpyData(mapID)
        maxFreeTimes = fbIpyData.GetDayTimes() # 常规封顶次数
        enterCnt = GetEnterFBCount(curPlayer, mapID) # 已经进入次数
        maxCnt = GetEnterFBMaxCnt(curPlayer, mapID) # 当前可用最大次数
        remainCanEnterCnt = maxCnt - enterCnt # 剩余可进入次数
        regainStartTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FbCntRegainStartTime % mapID)
        passTime = curTime - regainStartTime
        # 到达常规次数上限,暂时恢复时间
        if remainCanEnterCnt >= maxFreeTimes:
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FbCntRegainStartTime % mapID, 0)
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FbCntRegainTotalTime % mapID, 0)
            GameWorld.DebugLog("副本时间恢复次数已达常规次数!")
        elif passTime < recoverInterval:
            pass
            #GameWorld.DebugLog("副本时间恢复次数还未到达恢复CD!")
        else:
            if not regainStartTime:
                updRegainTotalTime = recoverInterval
            else:
                updRegainTotalTime = recoverInterval - passTime % recoverInterval
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FbCntRegainStartTime % mapID, curTime)
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FbCntRegainTotalTime % mapID, updRegainTotalTime)
            GameWorld.DebugLog("副本还可按时间恢复次数!curTime=%s,regainStartTime=%s,passTime=%s,updRegainTotalTime=%s"
                               % (curTime, regainStartTime, passTime, updRegainTotalTime))
    NotifyFBCntRegainInfo(curPlayer, [mapID])
    return
def RegainFBCntProcess(curPlayer):
    ## 按时间恢复副本次数
    curTime = int(time.time())
    recoverIntervalDict = IpyGameDataPY.GetFuncEvalCfg('FBCntRegainInterval', 1)
    for mapIDStr, recoverInfo in recoverIntervalDict.items():
        mapID = int(mapIDStr)
        maxCanRecoverCnt, recoverInterval = recoverInfo
        curRegainFbCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RegainFbCnt % mapID) # 当前已恢复次数
        if maxCanRecoverCnt and curRegainFbCnt >= maxCanRecoverCnt:
            #GameWorld.DebugLog("已到达副本恢复次数上限!mapID=%s,curRegainFbCnt(%s) >= maxCanRecoverCnt(%s)" % (mapID, curRegainFbCnt, maxCanRecoverCnt))
            continue
        fbIpyData = GetFBIpyData(mapID)
        maxFreeTimes = fbIpyData.GetDayTimes() # 常规封顶次数
        enterCnt = GetEnterFBCount(curPlayer, mapID) # 已经进入次数
        maxCnt = GetEnterFBMaxCnt(curPlayer, mapID) # 当前可用最大次数
        remainCanEnterCnt = maxCnt - enterCnt # 剩余可进入次数
        if remainCanEnterCnt >= maxFreeTimes:
            #GameWorld.DebugLog("可进入次数已经到达常规封顶次数!mapID=%s,remainCanEnterCnt=%s" % (mapID, remainCanEnterCnt))
            continue
        regainStartTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FbCntRegainStartTime % mapID)
        if not regainStartTime:
            #GameWorld.DebugLog("还未设置副本恢复次数倒计时!mapID=%s" % mapID)
            continue
        needTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FbCntRegainTotalTime % mapID)
        passTime = curTime - regainStartTime
        if passTime < needTime:
            continue
        remainTime = passTime - needTime # 扣除恢复单次后剩余的时间,离线上线后可能一次性恢复多次
        recoverCnt = 1 + remainTime / recoverInterval # 倒计时时间可恢复总次数
        realRecoverCnt = min(maxFreeTimes - remainCanEnterCnt, recoverCnt) # 实际最大可恢复总次数
        if maxCanRecoverCnt:
            realRecoverCnt = min(realRecoverCnt, maxCanRecoverCnt - curRegainFbCnt)
        if realRecoverCnt <= 0:
            continue
        # 更新时间倒计时已恢复次数
        updRegainFbCnt = curRegainFbCnt + realRecoverCnt
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RegainFbCnt % mapID, updRegainFbCnt)
        OnFBCountChangeEffectRecoverCount(curPlayer, mapID)
    return
def NotifyFBCntRegainInfo(curPlayer, syncMapIDList=[]):
    ##通知副本次数恢复剩余时间
    recoverIntervalDict = IpyGameDataPY.GetFuncEvalCfg('FBCntRegainInterval', 1) # {mapID:[次数, 每次间隔], ...}
    if not recoverIntervalDict:
        return
    if not syncMapIDList:
        syncMapIDList = [int(mapIDStr) for mapIDStr in recoverIntervalDict.keys()]
    infoList = []
    curTime = int(time.time())
    for mapID in syncMapIDList:
        if str(mapID) not in recoverIntervalDict:
            continue
        mapInfo = ChPyNetSendPack.tagMCFBCntRegain()
        mapInfo.Clear()
        mapInfo.DataMapID = mapID
        lastRegainTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FbCntRegainStartTime % mapID)
        needTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FbCntRegainTotalTime % mapID)
        passTime = curTime - lastRegainTime
        mapInfo.RemainTime = max(0, needTime - passTime)
        mapInfo.RegainCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RegainFbCnt % mapID)
        infoList.append(mapInfo)
    if not infoList:
        return
    regainData = ChPyNetSendPack.tagMCFBCntRegainRemainTime()
    regainData.Clear()
    regainData.InfoList = infoList
    regainData.Cnt = len(regainData.InfoList)
    NetPackCommon.SendFakePack(curPlayer, regainData)
    return
def SetIsHelpFight(curPlayer):
    ##设置是否助战 注意!需要在增加副本次数之前设置
    mapID = GameWorld.GetMap().GetMapID()
    mapID = GetRecordMapID(mapID)
    enterCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_EnterFbCntDay % mapID)
    maxCnt = GetEnterFBMaxCnt(curPlayer, mapID)
    if enterCnt >= maxCnt:
        GameWorld.GetGameFB().SetPlayerGameFBDict(curPlayer.GetID(), ChConfig.FBPlayerDict_IsHelpFight, 1)
        return True
    return False
def GetIsHelpFight(curPlayer):
    ##获取是否助战
    return GameWorld.GetGameFB().GetPlayerGameFBDictByKey(curPlayer.GetID(), ChConfig.FBPlayerDict_IsHelpFight)
def FBOnWeek(curPlayer, onWeekType):
    
@@ -1880,32 +1656,19 @@
        buyCnt = curPlayer.NomalDictGetProperty(buyCntKey)
        PlayerControl.NomalDictSetProperty(curPlayer, buyCntKey, 0)
        
        # 找回次数
        recoverCntKey = ChConfig.Def_Player_Dict_RecoverFbCnt % mapID
        recoverCnt = curPlayer.NomalDictGetProperty(recoverCntKey)
        PlayerControl.NomalDictSetProperty(curPlayer, recoverCntKey, 0)
        # 物品增加次数
        itemAddCntKey = ChConfig.Def_Player_Dict_ItemAddFbCnt % mapID
        itemAddCnt = curPlayer.NomalDictGetProperty(itemAddCntKey)
        PlayerControl.NomalDictSetProperty(curPlayer, itemAddCntKey, 0)
        
        # 时间恢复次数
        regainFBCntKey = ChConfig.Def_Player_Dict_RegainFbCnt % mapID
        regainFBCnt = curPlayer.NomalDictGetProperty(regainFBCntKey)
        PlayerControl.NomalDictSetProperty(curPlayer, regainFBCntKey, 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FbCntRegainStartTime % mapID, 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FbCntRegainTotalTime % mapID, 0)
        GameWorld.DebugLog("    重置:mapID=%s,dayTimes=%s,buyCnt=%s,recoverCnt=%s,itemAddCnt=%s,regainFBCnt=%s,maxCnt=%s,enterCnt=%s"
                           % (mapID, dayTimes, buyCnt, recoverCnt, itemAddCnt, regainFBCnt, maxCnt, enterCnt))
        GameWorld.DebugLog("    重置:mapID=%s,dayTimes=%s,buyCnt=%s,itemAddCnt=%s,maxCnt=%s,enterCnt=%s"
                           % (mapID, dayTimes, buyCnt, itemAddCnt, maxCnt, enterCnt))
        
        mapIDInfo.append(mapID)
        
    if mapIDInfo:
        Sync_FBPlayerFBInfoData(curPlayer, mapIDInfo)
        Sync_FBPlayerFBBuyCount(curPlayer, mapIDInfo)
        NotifyFBCntRegainInfo(curPlayer, mapIDInfo)
    
    return
@@ -1915,27 +1678,7 @@
def FBOnLogin(curPlayer):
    FBLogic.OnFBPlayerOnLogin(curPlayer)
    Sync_FBPlayerFBInfoData(curPlayer)
    SyncFBEnterTick(curPlayer)
    Sync_FBPlayerFBBuyCount(curPlayer)
    #PlayerFB.Sync_PubFBSweepData(curPlayer)
    NotifyFBCntRegainInfo(curPlayer)
    #判断副本里离线超过一定时间则退出副本
    CheckFBPlayerOffine(curPlayer)
    return
def CheckFBPlayerOffine(curPlayer):
    mapid = curPlayer.GetMapID()
    ipyData = GetFBIpyData(mapid)
    if not ipyData:
        return
    OfflineTime = ipyData.GetOfflineTime()
    if not OfflineTime:
        return
    leaveServerSecond = PlayerControl.GetPlayerLeaveServerSecond(curPlayer) #离线时间秒
    if leaveServerSecond >= OfflineTime:
        GameWorld.DebugLog('判断副本里离线超过一定时间则退出副本 leaveServerSecond=%s'%leaveServerSecond)
        PlayerControl.PlayerLeaveFB(curPlayer)
    return
#//A5 75 购买副本进入次数#tagCMBuyEnterCount
@@ -1996,333 +1739,12 @@
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_BuyFbCntDay % mapID, hasBuyCnt + 1)
    Sync_FBPlayerFBBuyCount(curPlayer, [mapID])
    PlayerControl.NotifyCode(curPlayer, 'FBEnterTimeBuy', [mapID])
    OnFBCountChangeEffectRecoverCount(curPlayer, mapID)
    if mapID == ChConfig.Def_FBMapID_Love:
        coupleID = PlayerControl.GetCoupleID(curPlayer)
        if coupleID:
            addItemList = IpyGameDataPY.GetFuncEvalCfg("LoveFB", 3)
            paramList = [curPlayer.GetPlayerName()]
            PlayerControl.SendMailByKey("BuyLoveFBCntCoupleMail", [coupleID], addItemList, paramList)
    return
def DoFuncOpen_RunDaily(curPlayer): return EventReport.WriteFuncCMEAcceptable(curPlayer, ShareDefine.GameFuncID_RunDaily)
def DoFuncOpen_RunFamily(curPlayer): return EventReport.WriteFuncCMEAcceptable(curPlayer, ShareDefine.GameFuncID_RunFamily)
#---------------------------------------------------------------------
## 副本开启提示(几分钟)
#  @param openTimeList 开启时间列表[(开启时钟,开启分钟), ]
#  @param notifyMsg 提示mark
#  @param notifyTimeList 提示时间列表
#  @param mergeMinOSD 该提示针对跨服子服有效的最小开服天, >=0时有限制
#  @param mergeMaxOSD 该提示针对跨服子服有效的最大开服天, >=0时有限制
#  @param mergeMapInfo 该提示所属的跨服活动地图信息, 主要用于不同子服对应所跨的活动地图ID
#  @return None
def FBOpenNotify(openTimeList, notifyMsg, notifyTimeList, mapID = 0, mergeMinOSD=-1,
                 mergeMaxOSD=-1, mergeMapInfo=[]):
    # 只在第一线中提示
    if GameWorld.GetGameWorld().GetCurGameWorldIndex() != 0 or not notifyTimeList:
        return
    curTime = datetime.datetime.today()
    nextTime = curTime + datetime.timedelta(hours=1)
    curYear, curMonth, curDay, curHour = curTime.year, curTime.month, curTime.day, curTime.hour
    nextYear, nextMonth, nextDay, nextHour = nextTime.year, nextTime.month, nextTime.day, nextTime.hour
    startTimeStrFormat = "%s-%s-%s %s:%s:%s"
    # 需要提示的最大分钟
    maxNotifyMinute = max(notifyTimeList)
    remaindMinute = -1
    for hour, minute in openTimeList:
        # 当前小时的和下一个小时才有可能需要提示
        if hour not in [curHour, nextHour]:
            continue
        # 获取本次比较的开启时间
        if hour == curHour:
            startTimeStr = startTimeStrFormat % (curYear, curMonth, curDay, hour, minute, "00")
        else:
            startTimeStr = startTimeStrFormat % (nextYear, nextMonth, nextDay, hour, minute, "00")
        # 字符串转化为datetime
        startTime = datetime.datetime.strptime(startTimeStr, ChConfig.TYPE_Time_Format)
        # 还剩多少时间开启
        remainTime = startTime - curTime
        # 计算剩余秒数
        seconds = remainTime.seconds
        # 计算剩余分钟,提前一秒通知
        remaindMinute = (seconds - 1) / 60 + 1
        if 0 < remaindMinute <= maxNotifyMinute:
            break
    # 在配置中需要提示,且未提示过
    if remaindMinute in notifyTimeList \
    and remaindMinute != GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Map_FBDict_NotifyOpen):
        if mapID > 0:
            msgParamList = [remaindMinute, mapID]
        else:
            msgParamList = [remaindMinute]
        PlayerControl.WorldNotify(0, notifyMsg, msgParamList, 0, mergeMinOSD, mergeMaxOSD, mergeMapInfo)
        GameWorld.GetGameFB().SetGameFBDict(ChConfig.Map_FBDict_NotifyOpen, remaindMinute)
    return
## 判断当前是否时副本进场时间(准备时间)
#  @param openTimeList 开启时间列表[(开启时钟,开启分钟), ]
#  @param prepareSeconds 准备时间(秒)
#  @return bool
def CheckIsFBPrepareTime(openTimeList, prepareSeconds):
    curTime = GameWorld.GetCurrentTime()
    prevTime = curTime - datetime.timedelta(hours=1)
    curYear, curMonth, curDay, curHour = curTime.year, curTime.month, curTime.day, curTime.hour
    prevYear, prevMonth, prevDay, prevHour = prevTime.year, prevTime.month, prevTime.day, prevTime.hour
    startTimeStrFormat = "%s-%s-%s %s:%s:%s"
    for hour, minute in openTimeList:
        # 当前小时的和上一小时才有可能时进入时间
        if hour not in [curHour, prevHour]:
            continue
        # 获取本次比较的开启时间
        if hour == curHour:
            startTimeStr = startTimeStrFormat % (curYear, curMonth, curDay, hour, minute, "00")
        else:
            startTimeStr = startTimeStrFormat % (prevYear, prevMonth, prevDay, hour, minute, "00")
        # 字符串转化为datetime
        startTime = datetime.datetime.strptime(startTimeStr, ChConfig.TYPE_Time_Format)
        # 距开始时间的时间差
        pastTime = curTime - startTime
        # 换算成秒数
        pastSeconds = pastTime.seconds
        # 如果在规定准备秒数内,则返回True
        if 0 <= pastSeconds <= prepareSeconds:
            return True
    return False
## 副本行为 - 鼓舞buff
#  @param curPlayer 玩家
#  @param key 副本玩家字典key
#  @param encourageType 金钱类型
#  @param tick 当前时间
#  @param isMaxlv 是否直接满级
#  @return None
def FbEncourageBuff(curPlayer, key, encourageType, tick, ownerID=0):
    #GameWorld.Log("FbEncourageBuff moneyType=%s" % (moneyType))
    curMapID = curPlayer.GetMapID()
    curMapID = GetRecordMapID(curMapID)
    ipyData = IpyGameDataPY.GetIpyGameData('FbEncourage', curMapID, encourageType)
    if not ipyData:
        return
    maxCnt = ipyData.GetInspireMaxLV()
    gameFB = GameWorld.GetGameFB()
    ownerID = ownerID or curPlayer.GetID()
    encourageLV = gameFB.GetPlayerGameFBDictByKey(ownerID, key)
    encourageCntKey = 'FbEncourageCnt_%s' % encourageType
    encourageCnt = gameFB.GetPlayerGameFBDictByKey(ownerID, encourageCntKey)
    maxLV = IpyGameDataPY.GetFuncEvalCfg('FBEncourageBuff', 2, {}).get(curMapID, 0)
    if encourageLV >= maxLV:
        PlayerControl.NotifyCode(curPlayer, "GeRen_chenxin_93643")
        return
    if encourageCnt >= maxCnt:
        if encourageType == IPY_GameWorld.TYPE_Price_Silver_Money:
            PlayerControl.NotifyCode(curPlayer, "Xjmj_CopperInspireFull")
        else:
            PlayerControl.NotifyCode(curPlayer, "GeRen_chenxin_93643")
        return
    encourageCost = eval(ipyData.GetMoneyCount())
    costMoneyList = PlayerControl.HaveMoneyEx(curPlayer, encourageType, encourageCost)
    if not costMoneyList:
        return
    # 扣除鼓舞消耗
    infoDict = {"MapID":curMapID}
    for moneyType, moneyCnt in costMoneyList:
        if not PlayerControl.PayMoney(curPlayer, moneyType, moneyCnt, ChConfig.Def_Cost_FBEncourage, infoDict):
            return
    buffTypeID = IpyGameDataPY.GetFuncEvalCfg('FBEncourageBuff', 1, {}).get(curMapID, ChConfig.Def_SkillID_FBEncourageBuff)
    skillBuffID = buffTypeID + encourageLV
    skillBuff = GameWorld.GetGameData().GetSkillBySkillID(skillBuffID)
    if not skillBuff:
        GameWorld.Log("FbEncourageBuff   找不到技能%s" % skillBuffID)
        return
    buffType = SkillCommon.GetBuffType(skillBuff)
    gameFB.SetPlayerGameFBDict(ownerID, key, encourageLV+1)
    gameFB.SetPlayerGameFBDict(ownerID, encourageCntKey, encourageCnt+1)
    #GameWorld.Log("FbEncourageBuff encourage nextLV=%s success" % encourageLV)
    addHurtNum = IpyGameDataPY.GetFuncEvalCfg('FBEncourageBuff', 3, {}).get(curMapID, 0)
    if curMapID == ChConfig.Def_FBMapID_AllFamilyBoss:
        #给副本里所有盟成员提示
        playerManager = GameWorld.GetMapCopyPlayerManager()
        for index in xrange(playerManager.GetPlayerCount()):
            player = playerManager.GetPlayerByIndex(index)
            if not player:
                continue
            if player.GetFamilyID() != ownerID:
                continue
            BuffSkill.DoAddBuff(player, buffType, skillBuff, tick)
            PlayerControl.NotifyCode(player, "AllianceBossText2", [curPlayer.GetName(), encourageLV+1])
            SendFBEncourageInfo(player, encourageLV+1, ownerID)
            if player.GetID() == curPlayer.GetID():
                PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_FBEncourage, 1, [curMapID])
    else:
        BuffSkill.DoAddBuff(curPlayer, buffType, skillBuff, tick)
        PlayerControl.NotifyCode(curPlayer, "GeRen_chenxin_628920", [(encourageLV+1)*addHurtNum])
        SendFBEncourageInfo(curPlayer, encourageLV+1, ownerID)
        #成就
        PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_FBEncourage, 1, [curMapID])
    return True
## 添加副本鼓舞buff,一般用于玩家掉线后,在规定时间内重新上线回到副本时重新上buff
#  @param curPlayer 玩家
#  @param key 副本玩家字典key
#  @param tick 当前时间
#  @return None
def AddFbEncourageBuff(curPlayer, key, tick, ownerID=0):
    curPlayerID = curPlayer.GetID()
    GameWorld.Log("AddFbEncourageBuff() curPlayerID=%s" % curPlayerID)
    ownerID = ownerID or curPlayer.GetID()
    gameFB = GameWorld.GetGameFB()
    encourageLV = gameFB.GetPlayerGameFBDictByKey(ownerID, key)
    if not encourageLV:
        return
    mapID = GameWorld.GetMap().GetMapID()
    mapID = GetRecordMapID(mapID)
    buffTypeID = IpyGameDataPY.GetFuncEvalCfg('FBEncourageBuff', 1, {}).get(mapID, ChConfig.Def_SkillID_FBEncourageBuff)
    skillBuffID = buffTypeID + encourageLV -1
    skillBuff = GameWorld.GetGameData().GetSkillBySkillID(skillBuffID)
    if not skillBuff:
        GameWorld.Log("FbEncourageBuff   找不到技能%s" % skillBuffID)
        return
    buffType = SkillCommon.GetBuffType(skillBuff)
    BuffSkill.DoAddBuff(curPlayer, buffType, skillBuff, tick)
    SendFBEncourageInfo(curPlayer, encourageLV, ownerID)
    return
## 清除鼓舞buff
#  @param curPlayer 玩家
#  @param tick 当前时间
#  @return True - 清除成功 ; False - 无该buff
def ClearEncourageBuff(curPlayer, tick):
    for buffTypeID in list(set(IpyGameDataPY.GetFuncEvalCfg('FBEncourageBuff', 1, {}).values())):
        if BuffSkill.DelBuffBySkillID(curPlayer, buffTypeID, tick):
            playerControl = PlayerControl.PlayerControl(curPlayer)
            playerControl.RefreshPlayerAttrByBuff()
            GameWorld.Log("ClearEncourageBuff() True")
            return True
    GameWorld.Log("ClearEncourageBuff() False")
    return False
## 发送副本鼓舞信息
#  @param curPlayer 玩家
#  @return None
def SendFBEncourageInfo(curPlayer, lv, ownerID=0):
    #//A3 0A 副本鼓舞信息通知 #tagMCFBEncourageInfo
    curMapID = curPlayer.GetMapID()
    curMapID = GetRecordMapID(curMapID)
    ipyDataList = IpyGameDataPY.GetIpyGameDataByCondition('FbEncourage', {'DataMapID':curMapID}, True)
    if not ipyDataList:
        return
    encourageInfo = ChPyNetSendPack.tagMCFBEncourageInfo()
    encourageInfo.Clear()
    encourageInfo.InfoList = []
    encourageCntKey = 'FbEncourageCnt_%s'
    gameFB = GameWorld.GetGameFB()
    ownerID = ownerID or curPlayer.GetID()
    for ipyData in ipyDataList:
        inspireType = ipyData.GetInspireType()
        packData = ChPyNetSendPack.tagMCFBEncourageCnt()
        packData.MoneyType = inspireType
        packData.EncourageCnt = gameFB.GetPlayerGameFBDictByKey(ownerID, encourageCntKey % inspireType)
        encourageInfo.InfoList.append(packData)
    encourageInfo.Cnt = len(encourageInfo.InfoList)
    NetPackCommon.SendFakePack(curPlayer, encourageInfo)
    return
## 获取玩家所在副本区域福利倍值
#  @param curPlayer 玩家
#  @return 倍值-默认为1
def GetAreaRewardMultiple(curPlayer):
    return max(1, curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AreaRewardMultiple))
## 设置玩家所在副本区域福利倍值
#  @param curPlayer 玩家
#  @param value 更新倍值
#  @return
def SetAreaRewardMultiple(curPlayer, value):
    curPlayer.SetDict(ChConfig.Def_PlayerKey_AreaRewardMultiple, value)
    return
## 检查是否进入副本CD中
#  @param curPlayer 玩家
#  @param mapID
#  @return True-CD中,不可进入;False-非CD中,可进入
def CheckIsEnterCD(curPlayer, mapID):
    return GetFBEnterCD(curPlayer, mapID) > 0
## 获取进入副本CD时间
#  @param curPlayer 玩家
#  @param mapID
#  @return <=0 代表没有CD, >0表示还剩下的CD tick
def GetFBEnterCD(curPlayer, mapID):
    mapID = GetRecordMapID(mapID)
    enterCDDict = IpyGameDataPY.GetFuncEvalCfg('FBEnterCD', 1)
    if mapID not in enterCDDict:
        return 0
    lvLimitDict = IpyGameDataPY.GetFuncEvalCfg('FBEnterCD', 3)
    if mapID in lvLimitDict:
        if curPlayer.GetLV() >= lvLimitDict[mapID]:
            return 0
    cdTick = enterCDDict[mapID]
    lastEnterTick = GetFBPDictValue(curPlayer, ChConfig.Def_PDict_LastEnterFBTick % mapID)
    if not lastEnterTick:
        return 0
    timeNum = GameWorld.ChangeTimeStrToNum(GameWorld.GetCurrentDataTimeStr())
    passTick = max(0, timeNum - lastEnterTick)
    curCDTick = max(0, cdTick - passTick)
    if curCDTick > 0:
        GameWorld.DebugLog("副本进入CD中!mapID=%s,timeNum=%s,lastEnterTick=%s,passTick=%s,剩余=%s"
                           % (mapID, timeNum, lastEnterTick, passTick, curCDTick))
    return curCDTick
## 更新玩家进入副本时间
#  @param curPlayer 玩家
#  @param tick 更新值
#  @return
def UpdateFBEnterTick(curPlayer):
    mapID = GameWorld.GetMap().GetMapID()
    mapID = GetRecordMapID(mapID)
    enterCDDict = IpyGameDataPY.GetFuncEvalCfg('FBEnterCD', 1)
    if mapID not in enterCDDict:
        return
    timeNum = GameWorld.ChangeTimeStrToNum(GameWorld.GetCurrentDataTimeStr())
    SetFBPDictValue(curPlayer, ChConfig.Def_PDict_LastEnterFBTick % mapID, timeNum)
    GameWorld.DebugLog("UpdateFBEnterTick mapID=%s,timeNum=%s" % (mapID, timeNum))
    SyncFBEnterTick(curPlayer, mapID)
    return
## 获取记录值的mapID
@@ -2384,48 +1806,6 @@
        
    return GeneralTrainMapIDList
def GetClientCustomScene():
    ## 获取前端自定义副本场景
    mapIDList = GetGeneralTrainMapIDList()
    return mapIDList + ChConfig.ClientCustomSceneList
## 同步进入副本时间
#  @param curPlayer 玩家
#  @param syncMapID 同步的地图,默认0为全部
#  @return None
def SyncFBEnterTick(curPlayer, syncMapID=0):
    enterCDDict = IpyGameDataPY.GetFuncEvalCfg('FBEnterCD', 1)
    if not enterCDDict:
        return
    timeNum = GameWorld.ChangeTimeStrToNum(GameWorld.GetCurrentDataTimeStr())
    enterList = ChPyNetSendPack.tagMCFBEnterTickList()
    enterList.Clear()
    enterList.EnterTickList = []
    for mapID in enterCDDict.keys():
        if syncMapID not in [0, mapID]:
            continue
        lastEnterTick = GetFBPDictValue(curPlayer, ChConfig.Def_PDict_LastEnterFBTick % mapID)
        # 修正副本CD时间
        if timeNum < lastEnterTick:
            SetFBPDictValue(curPlayer, ChConfig.Def_PDict_LastEnterFBTick % mapID, timeNum)
            lastEnterTick = timeNum
            GameWorld.DebugLog("修正玩家副本CD时间 mapID=%s,timeNum=%s" % (mapID, timeNum), curPlayer.GetPlayerID())
        enterTickObj = ChPyNetSendPack.tagMCFBEnterTick()
        enterTickObj.Clear()
        enterTickObj.MapID = mapID
        enterTickObj.LastEnterTick = lastEnterTick#max(enterCDDict.get(mapID) - (timeNum - lastEnterTick), 0)
        enterList.EnterTickList.append(enterTickObj)
    enterList.Cnt = len(enterList.EnterTickList)
    NetPackCommon.SendFakePack(curPlayer, enterList)
    return
## 通知个人通用副本信息
#  @param curPlayer: 玩家实例
#  @param runTime: 已经进行时间
@@ -2450,7 +1830,7 @@
        mapInfo.FBID = mID
        mapInfo.EnterCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_EnterFbCntDay % mID)
        mapInfo.EnterCntTotal = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_EnterFbCntTotal % mID)
        mapInfo.RecoverCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RecoverFbCnt % mID)
        mapInfo.RecoverCnt = 0
        mapInfo.ItemAddCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ItemAddFbCnt % mID)
        mapInfo.PassLineID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FBPassLineID % mID)
        
@@ -2463,8 +1843,6 @@
        
    NetPackCommon.SendFakePack(curPlayer, fbInfoData)
    return
#// A3 BD 通知玩家购买副本进入次数 #tagMCBuyEnterInfo
#
@@ -2544,100 +1922,6 @@
            group.extend(tmpList[gi])
            
    return groupList
def ClearAreaRewardRecord(playerID):
    ##清除战场区域福利记录
    gameWorld = GameWorld.GetGameWorld()
    gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetCnt%playerID, 0)
    gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetExp%playerID, 0)
    gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetExpPoint%playerID, 0)
    gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetZhenQiTotal%playerID, 0)
    gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetTechPoint%playerID, 0)
    return
##战场区域福利逻辑
# @param tick 时间戳
# @return 无意义
def DoLogicAreaReward(cfgKeyName, tick, needAlive=False):
    gameFB = GameWorld.GetGameFB()
    gameWorld = GameWorld.GetGameWorld()
    rewardInterval = IpyGameDataPY.GetFuncCfg(cfgKeyName, 2)
    rewardFormatDict = IpyGameDataPY.GetFuncEvalCfg(cfgKeyName)
    getCntLimit = IpyGameDataPY.GetFuncCfg(cfgKeyName, 3)
    lastAwardTick = gameFB.GetGameFBDictByKey(ChConfig.Map_FBDict_LastAreaRewardTick)
    if tick - lastAwardTick < rewardInterval:
        return
    gameFB.SetGameFBDict(ChConfig.Map_FBDict_LastAreaRewardTick, tick)
    GameWorld.DebugLog("给战场福利 tick=%s,needAlive=%s" % (tick, needAlive))
    # 更新玩家战场持续时间
    playerManager = GameWorld.GetMapCopyPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if not curPlayer:
            continue
        if needAlive and GameObj.GetHP(curPlayer) <= 0:
            continue
        playerID = curPlayer.GetPlayerID()
        if getCntLimit:
            getCnt = gameWorld.GetGameWorldDictByKey(ChConfig.Map_Player_AreaReward_GetCnt%playerID)
            if getCnt >= getCntLimit:
                continue
            gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetCnt%playerID, getCnt + 1)
        multiple = GetAreaRewardMultiple(curPlayer) # 福利倍数
        reLV = curPlayer.GetLV()
        reExp = PlayerControl.GetPlayerReExp(curPlayer)
        playerControl = PlayerControl.PlayerControl(curPlayer)
        if "Exp" in rewardFormatDict:
            addExp = eval(rewardFormatDict["Exp"])
            addExp = playerControl.AddExp(addExp)
            if addExp > 0:
                totalExp = GetFBAreaRewardExp(gameWorld, playerID) + addExp
                gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetExp%playerID, totalExp % ChConfig.Def_PerPointValue)
                gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetExpPoint%playerID, totalExp / ChConfig.Def_PerPointValue)
        if "ZhenQi" in rewardFormatDict:
            addZhenQi = eval(rewardFormatDict["ZhenQi"])
            PlayerControl.PlayerAddZhenQi(curPlayer, addZhenQi, True, True, "FB")
            totalZhenQi = gameWorld.GetGameWorldDictByKey(ChConfig.Map_Player_AreaReward_GetZhenQiTotal%playerID)
            totalZhenQi += addZhenQi
            gameWorld.SetGameWorldDict(ChConfig.Map_Player_AreaReward_GetZhenQiTotal%playerID, totalZhenQi)
        #GameWorld.DebugLog("    战场福利 倍区=%s,reLV=%s,reExp=%s,addExp=%s,addZQ=%s,totalExp=%s,totalZQ=%s"
        #                   % (multiple, reLV, reExp, addExp, addZhenQi, totalExp, totalZhenQi), playerID)
    return
def GetFBAreaRewardExp(gameWorld, playerID):
    exp = gameWorld.GetGameWorldDictByKey(ChConfig.Map_Player_AreaReward_GetExp%playerID)
    expPoint = gameWorld.GetGameWorldDictByKey(ChConfig.Map_Player_AreaReward_GetExpPoint%playerID)
    return expPoint * ChConfig.Def_PerPointValue + exp
def GetFBAreaRewardZhenQi(gameWorld, playerID):
    return gameWorld.GetGameWorldDictByKey(ChConfig.Map_Player_AreaReward_GetZhenQiTotal%playerID)
def GetFBAreaRewardTechPoint(gameWorld, playerID):
    return gameWorld.GetGameWorldDictByKey(ChConfig.Map_Player_AreaReward_GetTechPoint%playerID)
def NotifyCopyMapPlayerFBHelp(tick, fbHelpFunc, interval=10000, befLogicFunc=None):
    gameFB = GameWorld.GetGameFB()
    lastTick = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NotifyFBHelpTick)
    if tick - lastTick < interval:
        return
    gameFB.SetGameFBDict(ChConfig.Def_FB_NotifyFBHelpTick, tick)
    if befLogicFunc:
        befLogicFunc(tick)
    playerManager = GameWorld.GetMapCopyPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if not curPlayer:
            continue
        fbHelpFunc(curPlayer, tick)
    return
def GetCrossDynamicLineMapZoneID():
    ## 获取跨服动态线路地图本线路跨服分区