ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
@@ -24,7 +24,7 @@
import SkillShell
import BuffSkill
import BaseAttack
import PlayerTruck
import ChNetSendPack
import SkillCommon
import AttackCommon
import ItemControler
@@ -47,11 +47,13 @@
import BossHurtMng
import PlayerSuperMarket
import GameLogic_FamilyInvade
#import GameLogic_MunekadoTrial
import GameLogic_GatherSoul
import FormulaControl
import PlayerMagicWeapon
import PlayerBossReborn
import PlayerFairyCeremony
import PlayerWeekParty
import FamilyRobBoss
import IpyGameDataPY
import PyGameData
import PlayerTeam
@@ -71,9 +73,10 @@
NPCAttr_ParamDict, # 过程参数公式
NPCAttr_AttrStrengthenList, # 等级成长属性公式
NPCAttr_PlayerCntCoefficient, # 地图人数对应属性额外成长系数 {mapID:{"属性名":{组队进入人数:系数, ...}, ...}, ...}
NPCAttr_NPCPlayerCntCoefficient, # NPC特殊成长人数对应属性额外成长系数 {npcID:{"属性名":{人数:系数, ...}, ...}, ...}, 优先级大于地图人数系数
NPCAttr_DynNPCLVMap, # 动态等级的地图ID列表,默认已刷新出来的NPC等级不会再变更,下次刷出来的怪物等级变更 [地图ID, ...]
NPCAttr_DynPCCoefficientMap, # 动态人数系数的地图ID {地图ID:是否马上刷新属性, ...}
) = range(5)
) = range(6)
#---------------------------------------------------------------------
##NPC初始化->出生调用
@@ -139,11 +142,14 @@
    gameFB = GameWorld.GetGameFB()
    
    if strengthenIpyData.GetIsStrengthenByPlayerCount():
        strengthenPlayerCnt = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NPCStrengthenPlayerCnt)
        if not strengthenPlayerCnt:
            GameWorld.ErrLog("NPC配置了按玩家人数成长类型,但是无法获取到对应的玩家人数!npcID=%s" % (npcID))
            return
        if FamilyRobBoss.IsHorsePetRobBoss(npcID):
            strengthenPlayerCnt = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_HorsePetRobBossPlayerCount)
        else:
            strengthenPlayerCnt = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NPCStrengthenPlayerCnt)
            if not strengthenPlayerCnt:
                GameWorld.ErrLog("NPC配置了按玩家人数成长类型,但是无法获取到对应的玩家人数!npcID=%s" % (npcID))
                return
    lvStrengthenType = strengthenIpyData.GetLVStrengthenType()
    # 根据世界等级
    if lvStrengthenType == 3:
@@ -154,7 +160,15 @@
    # 根据平均等级
    elif lvStrengthenType == 1:
        strengthenLV = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NPCStrengthenAverageLV)
    # 根据按成长等级的上下限随机
    elif lvStrengthenType == 4:
        randMinLV = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NPCStrengthenMinLV)
        randMaxLV = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NPCStrengthenMaxLV)
        strengthenLV = random.randint(randMinLV, randMaxLV)
        
    if strengthenIpyData.GetCmpNPCBaseLV():
        strengthenLV = max(strengthenLV, curNPC.GetLV())
    if lvStrengthenType in [1, 2] and not strengthenLV:
        GameWorld.ErrLog("NPC配置了成长等级类型,但是无法获取到对应的成长等级值!npcID=%s,lvStrengthenType=%s" % (npcID, lvStrengthenType))
        return
@@ -201,6 +215,62 @@
            curNPC.Notify_HPEx()
            curNPC.Notify_MaxHPEx()
            #GameWorld.DebugLog("    aftHP=%s,aftMaxHP=%s" % (aftHP, aftMaxHP))
    # 机器人复活初始化给技能
    if isReborn and curNPC.GetType() == ChConfig.ntRobot:
        __OnFBRobotReborn(curNPC, strengthenLV)
    return
def __OnFBRobotReborn(curNPC, npcLV):
    tick = GameWorld.GetGameWorld().GetTick()
    lineID = GameWorld.GetGameWorld().GetLineID()
    objID = curNPC.GetID()
    jobSkillDict = IpyGameDataPY.GetFuncEvalCfg("FBRobotCfg", 1)
    robotJob = random.choice(jobSkillDict.keys())
    lineRobotJobDict = PyGameData.g_fbRobotJobDict.get(lineID, {})
    lineRobotJobDict[objID] = robotJob
    PyGameData.g_fbRobotJobDict[lineID] = lineRobotJobDict
    skillInfoDict = jobSkillDict[robotJob]
    skillIDList = []
    for skillInfo, needLV in skillInfoDict.items():
        if npcLV < needLV:
            continue
        if isinstance(skillInfo, int):
            skillIDList.append(skillInfo)
        else:
            skillIDList += list(skillInfo)
    GameWorld.DebugLog("给机器人NPC技能: objID=%s,robotJob=%s,npcLV=%s, %s" % (objID, robotJob, npcLV, skillIDList))
    skillManager = curNPC.GetSkillManager()
    for skillID in skillIDList:
        skillManager.LearnSkillByID(skillID)
    playerManager = GameWorld.GetMapCopyPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if not curPlayer:
            continue
        FBLogic.DoFBHelp(curPlayer, tick)
    return
def __DoGiveVSPlayerNPCSkill(curNPC, job, npcLV):
    skillManager = curNPC.GetSkillManager()
    jobSkillDict = IpyGameDataPY.GetFuncEvalCfg("XMZZRobotSkill", 1)
    if job not in jobSkillDict:
        return
    skillInfoDict = jobSkillDict[job]
    #{1:{(100, 101, 102, 103):1, 50000:100, 50100:200, 50400:300}, 2:{(200, 201, 202, 203):1, 55000:100, 55100:200, 55200:300}}
    skillIDList = []
    for skillInfo, needLV in skillInfoDict.items():
        if npcLV < needLV:
            continue
        if isinstance(skillInfo, int):
            skillIDList.append(skillInfo)
        else:
            skillIDList += list(skillInfo)
    GameWorld.DebugLog("给NPC技能: job=%s,npcLV=%s, %s" % (job, npcLV, skillIDList))
    for skillID in skillIDList:
        skillManager.LearnSkillByID(skillID)
    return
def GetNPCStrengthenAttrDict(npcID, strengthenLV=0, strengthenPlayerCnt=0, strengthenIpyData=None):
@@ -216,8 +286,9 @@
    
    attrDict = {}
    paramDict = attrStrengthenInfo[NPCAttr_ParamDict] # 过程参数公式字典
    attrStrengthenList = attrStrengthenInfo[NPCAttr_AttrStrengthenList] # 属性成长公式字典
    attrStrengthenDict = attrStrengthenInfo[NPCAttr_AttrStrengthenList] # 属性成长公式字典
    playerCntCoefficient = attrStrengthenInfo[NPCAttr_PlayerCntCoefficient] # 人数系数
    npcIDPlayerCntCoefficient = attrStrengthenInfo[NPCAttr_NPCPlayerCntCoefficient] # 特殊NPC人数系数
    baseMaxHP = npcData.GetHPEx() * ShareDefine.Def_PerPointValue + npcData.GetHP()
    
    if strengthenLV:
@@ -278,9 +349,10 @@
                                                                    paramDict["AtkReplyCoefficient"])) # 怪物攻击回复调整值
        MonterHurt = eval(FormulaControl.GetCompileFormula("NPCParam_MonterHurt", paramDict["MonterHurt"])) # 怪物固定伤害
        LostHPPerSecond = eval(FormulaControl.GetCompileFormula("NPCParam_LostHPPerSecond", paramDict["LostHPPerSecond"])) # 玩家每秒掉血量
        LVStrengthenMark = strengthenIpyData.GetLVStrengthenMark()
        attrStrengthenList = attrStrengthenDict.get(LVStrengthenMark, [])
        for attrKey, strengthenFormat in attrStrengthenList:
            strengthenValue = int(eval(FormulaControl.GetCompileFormula("NPCStrengthen_%s" % attrKey, strengthenFormat)))
            strengthenValue = int(eval(FormulaControl.GetCompileFormula("NPCStrengthen_%s_%s" % (attrKey,LVStrengthenMark), strengthenFormat)))
            #GameWorld.DebugLog("    %s=%s" % (attrKey, strengthenValue))
            locals()[attrKey] = strengthenValue # 创建该属性局部变量作为参数提供给后面属性计算时用
            attrDict[attrKey] = strengthenValue
@@ -292,9 +364,14 @@
    if strengthenPlayerCnt:
        mapID = GameWorld.GetMap().GetMapID()
        dataMapID = FBCommon.GetRecordMapID(mapID)
        formulaKey = "MapCoefficient_%s" % mapID
        playerCntAttrCoefficient = playerCntCoefficient.get(mapID, {})
        if not playerCntAttrCoefficient and dataMapID in playerCntCoefficient:
            playerCntAttrCoefficient = playerCntCoefficient[dataMapID]
            formulaKey = "MapCoefficient_%s" % dataMapID
        if npcID in npcIDPlayerCntCoefficient:
            playerCntAttrCoefficient = npcIDPlayerCntCoefficient[npcID]
            formulaKey = "NPCCoefficient_%s" % npcID
        for attrKey, coefficientDict in playerCntAttrCoefficient.items():
            if attrKey in attrDict:
                attrValue = attrDict[attrKey]
@@ -305,7 +382,15 @@
                if not hasattr(npcData, attrFuncName):
                    continue
                attrValue = getattr(npcData, attrFuncName)()
            coefficient = GameWorld.GetDictValueByRangeKey(coefficientDict, strengthenPlayerCnt, 1)
            # 按字典配置
            if isinstance(coefficientDict, dict):
                coefficient = GameWorld.GetDictValueByRangeKey(coefficientDict, strengthenPlayerCnt, 1)
            # 按公式配置
            elif isinstance(coefficientDict, str):
                formulaKey = "%s_%s" % (formulaKey, attrKey)
                coefficient = eval(FormulaControl.GetCompileFormula(formulaKey, coefficientDict))
            else:
                coefficient = 1
            attrDict[attrKey] = int(attrValue * coefficient)
            
    #GameWorld.DebugLog("计算NPC属性成长: npcID=%s,strengthenLV=%s,strengthenPlayerCnt=%s,baseMaxHP=%s,attrDict=%s" 
@@ -313,7 +398,7 @@
    return attrDict
def GiveKillNPCDropPrize(curPlayer, mapID, npcCountDict, exp_rate=None, mailTypeKey=None, isMail=False, 
                         extraItemList=[], prizeMultiple=1, dropItemMapInfo=[]):
                         extraItemList=[], prizeMultiple=1, dropItemMapInfo=[], curGrade=0):
    '''给玩家击杀NPC掉落奖励
    @param mapID: 击杀的NPC所在地图ID,注意次地图并不一定是玩家当前地图
    @param npcCountDict: 执行单次时所击杀的npc数量字典 {npcID:count, ...}
@@ -353,7 +438,7 @@
        # 掉落有概率因素,需多次执行
        for dCount in xrange(1, totalCount + 1):
            isKillCountDropEquipEx = dCount == 1 # 同一只NPC一次处理中多次击杀的情况,只算一次附加装备处理
            dropInfo = GetNPCDropInfo(curPlayer, mapID, npcID, isKillCountDropEquipEx=isKillCountDropEquipEx)
            dropInfo = GetNPCDropInfo(curPlayer, mapID, npcID, isKillCountDropEquipEx=isKillCountDropEquipEx, curGrade=curGrade)
            if not dropInfo:
                continue
            dropIDList, dropIDBindDict, dropMoneyCnt, moneyValue = dropInfo
@@ -668,7 +753,7 @@
                           % (npcID, killCount, dropIDCountDict, dropIDBindDict, dropMoney), playerID)
    return dropIDCountDict, dropIDBindDict, dropMoney
def GetNPCDropInfo(dropPlayer, mapID, npcID, ownerPlayerList=[], ipyDrop=None, isSingle=True, isKillCountDropEquipEx=True):
def GetNPCDropInfo(dropPlayer, mapID, npcID, ownerPlayerList=[], ipyDrop=None, isSingle=True, isKillCountDropEquipEx=True, curGrade=0):
    '''获取NPC掉落信息, 击杀及扫荡通用,调用该函数获得掉落信息,然后再看掉落地板上还是直接放入背包
        @param dropPlayer: 用于判断调用相关用的玩家示例,该玩家并不一定是击杀者,只是按一定规则设定的掉落判断依据的玩家
                            如队伍,取等级最大的玩家,该玩家并不一定是击杀者
@@ -760,7 +845,7 @@
    indepRateDoCnt = ipyDrop.GetIndepRateDoCnt()
    if indepRateDoCnt:
        indepRateDoCnt = __GetNPCDropDoCountChange(indepRateDoCnt, doCountRate + equipDropDoCountPlus, doCountAdd)
        dropEquipInfoList += __GetNPCIndepRateEquipDrop(ipyDrop, indepRateDoCnt, equipDropRatePlus)
        dropEquipInfoList += __GetNPCIndepRateEquipDrop(ipyDrop, indepRateDoCnt, equipDropRatePlus, curGrade)
        
    #GameWorld.DebugLog("阶,颜色,部位集合key,dropEquipInfoList=%s" % (dropEquipInfoList))
    placeDict = IpyGameDataPY.GetFuncCfg("EquipDropPartSets", 1)
@@ -856,7 +941,7 @@
    fbGradeColorStarRateDict = IpyGameDataPY.GetFuncEvalCfg("FBGradeEquipDropRate", 2) # 评级影响品质星级概率 {npcID:{(颜色,星级):[D级影响概率, ..., S级影响概率], ...}, ...}
    if npcID in fbGradeColorStarRateDict:
        gradeColorStarRateDict = fbGradeColorStarRateDict[npcID]
        curGrade = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FB_Grade)
        curGrade = curGrade if curGrade else GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FB_Grade)
        
    colorDropCntDict = {} # 装备颜色已经掉落数 {颜色:数量, ...}
    colorMaxDropCntDict = ipyDrop.GetIndepRateMaxDropCount() # {颜色:上限数量,...}
@@ -931,7 +1016,7 @@
        fbGradePriItemIDDropDict = IpyGameDataPY.GetFuncEvalCfg("FBGradeEquipDropRate", 3)
        if npcID in fbGradePriItemIDDropDict:
            gradePriItemIDDropDict = fbGradePriItemIDDropDict[npcID]
            curGrade = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FB_Grade)
            curGrade = curGrade if curGrade else GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FB_Grade)
            priDropInfoList = gradePriItemIDDropDict.get(curGrade, [])
            priDropIDList = []
            for priItemID, priItemCount in priDropInfoList:
@@ -1085,6 +1170,8 @@
    # 1. 职业物品ID集合
    job = curPlayer.GetJob()
    JobItemDropSets = IpyGameDataPY.GetFuncCfg("JobItemDropSets", 1) # {物品ID集合key:[职业顺序物品ID列表], ...}
    itemDropSets = IpyGameDataPY.GetFuncCfg("JobItemDropSets", 2) # {物品ID集合key:[随机物品ID列表], ...}
    itemDropRateSets = IpyGameDataPY.GetFuncCfg("JobItemDropSets", 3) # {物品ID集合key:[随机物品ID饼图列表], ...}
    ItemKeyMaxDropCountDict = ipyDrop.GetItemKeyMaxDropCount() # {物品ID集合key:随机次数,...}
    
    #     1.1 只掉本职业的
@@ -1115,20 +1202,14 @@
    #     1.2 随机掉落一个
    ItemKeyDropRateDict = ipyDrop.GetItemKeyDropRate() # {物品ID集合key:概率, ...}, 随机掉一个,优先级低
    if ItemKeyDropRateDict:
        for jobItemKey, dropRate in ItemKeyDropRateDict.items():
            if jobItemKey not in JobItemDropSets:
                continue
            jobItemList = JobItemDropSets[jobItemKey]
            if len(jobItemList) < job:
                GameWorld.ErrLog("职业物品集合key没有配置对应职业ID: npcID=%s,jobItemKey=%s,job=%s" % (npcID, jobItemKey, job))
                continue
        for itemKey, dropRate in ItemKeyDropRateDict.items():
            # 在只掉本职业里的不处理
            if jobItemKey in ItemKeyDropRateJobDict:
            if itemKey in ItemKeyDropRateJobDict:
                continue
            mustDropCount = dropRate / Def_NPCMaxDropRate
            dropRate = dropRate % Def_NPCMaxDropRate # 基础概率
            canDropCount = mustDropCount
            doCnt = ItemKeyMaxDropCountDict.get(jobItemKey, 1) # 默认1个
            doCnt = ItemKeyMaxDropCountDict.get(itemKey, 1) # 默认1个
            doCnt = __GetNPCDropDoCountChange(doCnt, doCountRate, doCountAdd)
            for _ in xrange(doCnt):
                if not GameWorld.CanHappen(dropRate, maxRate=Def_NPCMaxDropRate):
@@ -1136,9 +1217,20 @@
                canDropCount += 1
                
            for _ in xrange(canDropCount):
                randJobItemID = random.choice(jobItemList)
                dropItemIDList.append(randJobItemID)
                #GameWorld.DebugLog("掉落随机职业指定物品ID: jobItemKey=%s,randJobItemID=%s" % (jobItemKey, randJobItemID))
                if itemKey in itemDropRateSets:
                    randItemRateList = itemDropRateSets[itemKey]
                    randItemID = GameWorld.GetResultByRandomList(randItemRateList)
                    #GameWorld.DebugLog("掉落饼图物品ID: itemKey=%s,randItemRateList=%s,randItemID=%s" % (itemKey, randItemRateList, randItemID))
                elif itemKey in itemDropSets:
                    randItemList = itemDropSets[itemKey]
                    randItemID = random.choice(randItemList)
                    #GameWorld.DebugLog("掉落随机物品ID: itemKey=%s,randItemList=%s,randItemID=%s" % (itemKey, randItemList, randItemID))
                else:
                    continue
                if not randItemID:
                    continue
                dropItemIDList.append(randItemID)
                #GameWorld.DebugLog("掉落随机指定物品ID: itemKey=%s,randItemID=%s" % (itemKey, randItemID))
                
    # 2. 指定掉落ID处理, 受全局设定影响
    itemIDDropRateDict = ipyDrop.GetItemIDDropRate() # {物品ID:概率, ...}
@@ -1204,6 +1296,48 @@
        msgInfo = str([itemID, updKilledCnt])
        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "GlobalDropRate", msgInfo, len(msgInfo))
         
    # 4. 指定全服击杀次数必掉,算额外掉落
    globalKillDropDict = IpyGameDataPY.GetFuncEvalCfg("GlobalDropCD", 2) # {NPCID:{击杀次数:[是否本职业, {物品ID:个数, ...}, [[随机物品ID,个数], ...]]}, ...}
    if npcID in globalKillDropDict:
        killCountDropDict = globalKillDropDict[npcID]
        updNPCKilledCount = min(gw.GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_NPCKilledCount % npcID) + 1, ShareDefine.Def_UpperLimit_DWord)
        GameWorld.Log("更新全服击杀次数:npcID=%s, %s" % (npcID, updNPCKilledCount))
        # 通知GameServer记录
        msgInfo = str([npcID, updNPCKilledCount])
        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "GlobalKillCount", msgInfo, len(msgInfo))
        if updNPCKilledCount in killCountDropDict:
            isJobLimit, itemIDCountDict, randItemIDCountList = killCountDropDict[updNPCKilledCount]
            for itemID, itemCount in itemIDCountDict.items():
                if isJobLimit:
                    itemData = GameWorld.GetGameData().GetItemByTypeID(itemID)
                    if not itemData:
                        continue
                    itemJob = itemData.GetJobLimit() / 100
                    if itemJob and itemJob != curPlayer.GetJob():
                        # 非本职业可用,不掉落
                        GameWorld.DebugLog("全服击杀次数必掉,非本职业可用,不掉落! itemID=%s" % itemID)
                        continue
                dropItemIDList += [itemID] * itemCount
                GameWorld.Log("全服击杀次数必掉物品: itemID=%s,itemCount=%s" % (itemID, itemCount))
            if randItemIDCountList:
                if isJobLimit:
                    randJobItemList = []
                    for rItemID, rItemCount in randItemIDCountList:
                        itemData = GameWorld.GetGameData().GetItemByTypeID(rItemID)
                        if not itemData:
                            continue
                        itemJob = itemData.GetJobLimit() / 100
                        if itemJob and itemJob != curPlayer.GetJob():
                            # 非本职业可用,不掉落
                            GameWorld.DebugLog("全服击杀次数必掉随机,非本职业可用,不掉落! rItemID=%s" % rItemID)
                            continue
                        randJobItemList.append([rItemID, rItemCount])
                    randItemID, randItemCount = random.choice(randJobItemList)
                else:
                    randItemID, randItemCount = random.choice(randItemIDCountList)
                dropItemIDList += [randItemID] * randItemCount
                GameWorld.Log("全服击杀次数必掉随机物品: randItemID=%s,randItemCount=%s" % (randItemID, randItemCount))
    return dropItemIDList
def __GetEquipIDList(npcID, classLV, color, placeList, star, itemJobList, tagPlace=None):
@@ -1271,7 +1405,7 @@
    #GameWorld.DebugLog("饼图装备掉落结果: doCnt=%s, %s" % (doCnt, dropEquipInfoList))
    return dropEquipInfoList
def __GetNPCIndepRateEquipDrop(ipyDrop, doCnt, equipDropPlus):
def __GetNPCIndepRateEquipDrop(ipyDrop, doCnt, equipDropPlus, curGrade=0):
    ## 获取NPC独立掉率装备掉落信息
    npcID = ipyDrop.GetNPCID()
    indepRateDict = ipyDrop.GetIndepRateDrop() # {(阶,颜色,部位集合key):概率,...}
@@ -1282,7 +1416,7 @@
    fbGradeColorRateDict = IpyGameDataPY.GetFuncEvalCfg("FBGradeEquipDropRate", 1) #{npcID:{颜色:[D级影响概率, ..., S级影响概率], ...}, ...}
    if npcID in fbGradeColorRateDict:
        gradeColorRateDict = fbGradeColorRateDict[npcID]
        curGrade = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FB_Grade)
        curGrade = curGrade if curGrade else GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FB_Grade)
        
    #colorDropCntDict = {} # 装备颜色已经掉落数 {颜色:数量, ...}
    dropEquipInfoList = []
@@ -1922,9 +2056,8 @@
        # 设置npc死亡及自身处理
        SetDeadEx(summonNPC)
        
    if curNPC.GetGameObjType() == IPY_GameWorld.gotNPC and \
        curNPC.GetGameNPCObjType() == IPY_GameWorld.gnotSummon:
        FBLogic.DoFB_SummonNPCDead(curNPC)
    if curNPC.GetGameObjType() == IPY_GameWorld.gotNPC:
        FBLogic.DoFB_NPCDead(curNPC)
    
    summonPlayerID = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_SummonMapNPCPlayerID)
    if summonPlayerID > 0:
@@ -1935,36 +2068,101 @@
        # 通知GameServer boss状态 封魔坛在副本里单独处理
        ipyData = IpyGameDataPY.GetIpyGameDataNotLog('BOSSInfo', npcid)
        if ipyData and ipyData.GetMapID() != ChConfig.Def_FBMapID_SealDemon:
            mapID = GameWorld.GetMap().GetMapID()
            lineID = GameWorld.GetGameWorld().GetLineID()
            msgList = [npcid, 0, mapID, lineID]
            GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, 'GameWorldBossState',
                                                                '%s' % (msgList), len(str(msgList)))
            bosskey = ShareDefine.Def_Notify_WorldKey_GameWorldBossReborn % npcid
            GameWorld.GetGameWorld().SetGameWorldDict(bosskey, 0)
            GameServe_GameWorldBossState(npcid, 0)
            #GameWorld.GetGameWorld().SetGameWorldDict(ChConfig.Map_NPC_WorldBossDeadTick % npcid, GameWorld.GetGameWorld().GetTick())
            #因为存在boss分流,所以用gameFB字典,但是存活状态还是用GameWorld字典
            GameWorld.GetGameFB().SetGameFBDict(ChConfig.Map_NPC_WorldBossDeadTick % npcid, GameWorld.GetGameWorld().GetTick())
            if GetDropOwnerType(curNPC) == ChConfig.DropOwnerType_Family:
                FamilyRobBoss.ClearFamilyOwnerBossHurt(curNPC)
        ChNPC.OnNPCSetDead(curNPC)
        
    
    # 清除队伍成员伤血列表
    AttackCommon.ClearTeamPlayerHurtValue(curNPC)
    # 清除自定义伤血列表
    BossHurtMng.ClearHurtValueList(curNPC)
    #BossHurtMng.ClearHurtValueList(curNPC)
    if curNPC.GetType() == ChConfig.ntRobot:
        lineID = GameWorld.GetGameWorld().GetLineID()
        lineRobotJobDict = PyGameData.g_fbRobotJobDict.get(lineID, {})
        lineRobotJobDict.pop(curNPC.GetID(), 0)
        PyGameData.g_fbRobotJobDict[lineID] = lineRobotJobDict
    # C++设置npc死亡
    curNPC.SetDead(curNPC.GetDictByKey(ChConfig.Def_NPCDead_Reason),
                   curNPC.GetDictByKey(ChConfig.Def_NPCDead_KillerType),
                   curNPC.GetDictByKey(ChConfig.Def_NPCDead_KillerID))
    return
def GameServer_KillGameWorldBoss(bossID, killerName, hurtValue, isNotify=True):
    isAddKillCnt = 0 # 页游逻辑,手游暂写死不增
    mapID = GameWorld.GetMap().GetMapID()
    killMsg = str([bossID, killerName, hurtValue, isAddKillCnt, isNotify, mapID])
def GameServer_KillGameWorldBoss(bossID, killPlayerName, hurtValue, isNotify=True, killerIDList=[]):
    dataMapID = GameWorld.GetGameWorld().GetMapID()
    realMapID = GameWorld.GetGameWorld().GetRealMapID()
    copyMapID = GameWorld.GetGameWorld().GetCopyMapID()
    killMsg = str([bossID, killPlayerName, hurtValue, isNotify, realMapID, dataMapID, copyMapID, killerIDList])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, 'KillGameWorldBoss', killMsg, len(killMsg))
    GameWorld.DebugLog("Boss被击杀: bossID=%s,dataMapID=%s,realMapID=%s,copyMapID=%s,killerIDList=%s" % (bossID, dataMapID, realMapID, copyMapID, killerIDList))
    return
def GameServe_GameWorldBossState(bossID, isAlive):
    dataMapID = GameWorld.GetGameWorld().GetMapID()
    realMapID = GameWorld.GetGameWorld().GetRealMapID()
    copyMapID = GameWorld.GetGameWorld().GetCopyMapID()
    stateMsg = str([bossID, isAlive, dataMapID, realMapID, copyMapID])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, 'GameWorldBossState', '%s' % stateMsg, len(stateMsg))
    GameWorld.DebugLog("Boss状态变更: bossID=%s,isAlive=%s,dataMapID=%s,realMapID=%s,copyMapID=%s"
                       % (bossID, isAlive, dataMapID, realMapID, copyMapID))
    if not isAlive:
        if dataMapID in ChConfig.Def_CrossZoneTableName:
            tableName = ChConfig.Def_CrossZoneTableName[dataMapID]
            realMapID = GameWorld.GetGameWorld().GetRealMapID()
            copyMapID = GameWorld.GetGameWorld().GetCopyMapID()
            zoneIpyData = IpyGameDataPY.GetIpyGameData(tableName, realMapID, dataMapID, copyMapID)
            if not zoneIpyData:
                return
            zoneID = zoneIpyData.GetZoneID()
            GameWorld.GetGameWorld().SetGameWorldDict(ShareDefine.Def_Notify_WorldKey_GameWorldBossRebornCross % (zoneID, bossID), 0)
        else:
            GameWorld.GetGameWorld().SetGameWorldDict(ShareDefine.Def_Notify_WorldKey_GameWorldBossReborn % bossID, 0)
    return
def OnPlayerKillBoss(curPlayer, npcID, mapID, isCrossServer):
    killBossCntLimitDict = IpyGameDataPY.GetFuncCfg('KillBossCntLimit', 1)
    limitIndex = GameWorld.GetDictValueByKey(killBossCntLimitDict, npcID)
    if limitIndex != None:
        #今日杀怪次数+1
        key = ChConfig.Def_PDict_Boss_KillCnt % limitIndex
        newCnt = curPlayer.NomalDictGetProperty(key, 0) + 1
        PlayerControl.NomalDictSetProperty(curPlayer, key, newCnt)
        BossHurtMng.NotifyAttackBossCnt(curPlayer, limitIndex)
        GameWorld.DebugLog("今日杀怪次数  playerID=%s, newCnt=%s" % (curPlayer.GetPlayerID(), newCnt))
        dataDict = {"objID":npcID, "bossID":npcID, "touchCnt":newCnt,
                    "AccID":curPlayer.GetAccID(), "PlayerID":curPlayer.GetPlayerID()}
        DataRecordPack.SendEventPack("AddKillBossCnt", dataDict, curPlayer)
    if isCrossServer:
        return
    if limitIndex == ShareDefine.Def_Boss_Func_World:
        # 世界BOSS击杀成就
        PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_KillWorldBoss, 1)
        # 每日活动
        PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_WorldBOSS)
        PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_WorldBOSS, 1)
        PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_WorldBoss, 1)
        PlayerWeekParty.AddWeekPartyActionCnt(curPlayer, ChConfig.Def_WPAct_WorldBOSS, 1)
    if mapID == ChConfig.Def_FBMapID_BossHome:
        #BOSS之家
        # BOSS之家BOSS击杀成就
        PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_KillBossHomeBoss, 1)
        # 每日活动
        PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_BOSSHome)
        PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_BOSSHome, 1)
        PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_BossHome, 1)
        PlayerWeekParty.AddWeekPartyActionCnt(curPlayer, ChConfig.Def_WPAct_BOSSHome, 1)
    return
#################################################
## NPC控制定义
#
@@ -1984,6 +2182,10 @@
        self.__Killer = None # 击杀者, 由各种规则得出, 一般也是物品归属的代表, 用于广播、记录等确保与归属一致
        self.__AllKillerDict = {} # 所有击杀的玩家ID对应字典, 非队伍, 一般也是归属的拥有者
        self.__FeelPlayerList = [] # 所有摸怪玩家列表,处理任务及某些逻辑用
        self.__ownerPlayerList = [] # 归属者列表
        self.__OwnerHurtType = 0
        self.__OwnerHurtID = 0
        return
    #---------------------------------------------------------------------
    ## 移动到某一个点的附近点
@@ -2311,7 +2513,7 @@
    def GetIsBossView(self):
        # 主动视野情况,GetIsBoss 0 1 4 为普通NPC视野(有视野范围配置,但去除视野刷新),其他为BOSS类视野有刷新
        curNPC = self.__Instance
        if not ChConfig.IsGameBoss(curNPC) and not GetFaction(curNPC):
        if not ChConfig.IsGameBoss(curNPC) and not GetFaction(curNPC) and curNPC.GetType() != ChConfig.ntRobot:
            return False
        
        return True
@@ -2829,6 +3031,7 @@
    
    def __GetTeamHurtNPCPlayerIDList(self, refreshPoint, teamID, tick):
        
        curNPC = self.__Instance
        teamHurtPlayerIDList = []
        # 如果没有在线队员在有效范围内,则进一步判断离线队员是否有伤血保护中的
        playerMgr = GameWorld.GetPlayerManager()
@@ -2849,7 +3052,8 @@
                        continue
                    
                #if curTeamPlayer.GetHP() > 0 and self.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint):
                if self.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint):
                if self.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint) \
                    and AttackCommon.CheckKillNPCByCnt(curTeamPlayer, curNPC, False):
                    #GameWorld.DebugLog("有队员在boss范围内,保留队伍伤血!teamID=%s,playerID=%s" % (teamID, curTeamPlayer.GetPlayerID()))
                    teamHurtPlayerIDList.append(playerID)
            else:
@@ -3038,10 +3242,13 @@
        curNPC = self.__Instance
        #清除状态
        self.__ClearNPCAllState(False)
        #初始化召唤兽
        self.__InitNPCSummon()
        #重置技能CD
        self.__NormalNPCInItCD()
        #只在重生或者满血的状态下才重置以下内容
        if isReborn or GameObj.GetHP(curNPC) >= GameObj.GetMaxHP(curNPC):
            #初始化召唤兽
            self.__InitNPCSummon()
            #重置技能CD
            self.__NormalNPCInItCD()
        #重刷属性
        self.RefreshNPCState(isReborn=isReborn)
        #通知血量, 复活的情况不通知血量,由NPC出现包通知
@@ -3229,7 +3436,12 @@
        #范围校验
        posMapX = posMap.GetPosX()
        posMapY = posMap.GetPosY()
        posMapArea = posMap.GetArea()
        if curNPC.GetType() == IPY_GameWorld.ntFunctionNPC: #功能NPC
            posMapArea = 0
        else:
            posMapArea = posMap.GetArea()
        #获取范围内一点可以移动的点
        posX, poxY = GameMap.GetNearbyPosByDis(posMapX, posMapY, posMapArea)
        
@@ -3353,12 +3565,10 @@
            # 通知GameServer boss刷新成功
            ipyData = IpyGameDataPY.GetIpyGameDataNotLog('BOSSInfo', curNPCID)
            if ipyData:
                mapID = GameWorld.GetMap().GetMapID()
                lineID = GameWorld.GetGameWorld().GetLineID()
                msgList = [curNPCID, 1, mapID, lineID]
                GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, 'GameWorldBossState',
                                                            '%s' % (msgList), len(str(msgList)))
                GameServe_GameWorldBossState(curNPCID, 1)
                if GetDropOwnerType(curNPC) == ChConfig.DropOwnerType_Family:
                    FamilyRobBoss.FamilyOwnerBossOnReborn(curNPC)
        # 检查是否有光环, 在重生时处理,不然可能导致有些无战斗逻辑的怪物无法套上光环buff
        skillManager = curNPC.GetSkillManager()
        for index in xrange(skillManager.GetSkillCount()):
@@ -3575,7 +3785,7 @@
    #  @remarks 刷新NPC属性
    def RefreshNPCAttrState(self, canSyncClient=True, isReborn=False):
        curNPC = self.__Instance
        curNPCMaxHP_Before = GameObj.GetMaxHP(curNPC)
        #curNPCMaxHP_Before = GameObj.GetMaxHP(curNPC)
        #清空NPC战斗属性
        curNPC.ClearBattleEffect()
        #--------------------------------------------
@@ -3612,6 +3822,28 @@
            
        return
    
    def SetHelpBattleRobotRebornAttr(self, fightPower):
        '''助战机器人只设置血量属性
                        血量算法,(助战玩家=助战机器人):每个副本配置伤害*(助战玩家战力/副本规定战力)*系数值  系数值暂定为50
        '''
        curNPC = self.__Instance
        mapID = FBCommon.GetRecordMapID(GameWorld.GetMap().GetMapID())
        funcLineID = FBCommon.GetFBPropertyMark()
        ipyData = IpyGameDataPY.GetIpyGameData("FBHelpBattle", mapID, funcLineID)
        if not ipyData:
            return
        SetSuppressFightPower(curNPC, fightPower)
        fbFightPower = ipyData.GetFightPowerMin()
        baseHurt = ipyData.GetRobotBaseHurt()
        hpCoefficient = ipyData.GetRobotHPCoefficient()
        maxHP = int(eval(IpyGameDataPY.GetFuncCompileCfg("HelpBattleRobot", 2)))
        GameWorld.DebugLog("设置助战机器人属性: objID=%s,fightPower=%s,maxHP=%s" % (curNPC.GetID(), fightPower, maxHP))
        GameObj.SetMaxHP(curNPC, maxHP)
        GameObj.SetHP(curNPC, maxHP)
        curNPC.Notify_HPEx()
        curNPC.Notify_MaxHPEx()
        return
    
    # NPC移动速度特殊处理,只处理百分比不能处理固定值 
    # 因为 ChConfig.TYPE_Calc_AttrSpeed 非服务端移动速度,偷懒处理法
@@ -3622,11 +3854,13 @@
        if not speedPer:
            if curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_SpeedPer):
                curNPC.SetDict(ChConfig.Def_NPC_Dict_SpeedPer, 0)
            return
        speed = int(curNPC.GetSpeed() * (ShareDefine.Def_MaxRateValue) / max(100.0, float(ShareDefine.Def_MaxRateValue + speedPer)))
        curNPC.SetSpeed(speed)
        curNPC.SetDict(ChConfig.Def_NPC_Dict_SpeedPer, speedPer)
        else:
            speed = int(curNPC.GetSpeed() * (ShareDefine.Def_MaxRateValue) / max(100.0, float(ShareDefine.Def_MaxRateValue + speedPer)))
            curNPC.SetSpeed(speed)
            curNPC.SetDict(ChConfig.Def_NPC_Dict_SpeedPer, speedPer)
        if GameWorld.GetMap().GetMapID() == ChConfig.Def_FBMapID_GatherSoul:
            #目前只在聚魂副本里通知
            NPCSpeedChangeNotify(curNPC, curNPC.GetSpeed())
        return
    
    
@@ -3636,7 +3870,6 @@
    #  @remarks 刷新NPC行为属性
    def RefreshNPCActionState(self):
        curNPC = self.__Instance
        OperControlManager.ClearObjActionState(curNPC)
        
        #根据BUFF 加上状态
@@ -3736,7 +3969,7 @@
        #=====================================================================================================
        
        #boss伤血排行榜击杀逻辑
        BossHurtMng.BossOnKilled(curNPC)
        #BossHurtMng.BossOnKilled(curNPC)
        
        #掉落需要用到摸怪,所以在处理掉落奖励之前设置
        self.__SetFeelNPCPlayerList()
@@ -3752,8 +3985,11 @@
        # 记录boss击杀信息的NPC
        bossIpyData = IpyGameDataPY.GetIpyGameDataListNotLog('BOSSInfo', npcID)
        if bossIpyData:
            if GetDropOwnerType(curNPC) == ChConfig.DropOwnerType_Family:
                killerName = FamilyRobBoss.FamilyOwnerBossOnKilled(curNPC, self.__OwnerHurtID)
            #KillerJob = 0 if not self.__Killer else self.__Killer.GetJob()
            GameServer_KillGameWorldBoss(curNPC.GetNPCID(), killerName, 0)
            killerIDList = [player.GetPlayerID() for player in self.__ownerPlayerList]
            GameServer_KillGameWorldBoss(curNPC.GetNPCID(), killerName, 0, True, killerIDList)
        #===========================================================================================
        # # 暗金boss
        # if curNPC.GetIsBoss() == ChConfig.Def_NPCType_Boss_Dark:
@@ -3838,7 +4074,7 @@
                return moneyID
        return moneyItemList[-1][1]
    
    def __NPCSpecialDropItem(self, ownerPlayerList, ipyDrop):
    def __NPCSpecialDropItem(self, dropPlayer, ownerPlayerList, ipyDrop):
        '''特殊掉落 (私有特殊掉落 + 击杀次数特殊掉落), 支持摸怪
        @return: None
        @return: [[ownerPlayer, itemID, isBind, isDropInItemPack], ...]
@@ -3846,6 +4082,12 @@
        curNPC = self.__Instance
        npcID = curNPC.GetNPCID()
        specDropItemList = []
        playerLV = dropPlayer.GetLV()
        maxDropLV = ipyDrop.GetMaxDropLV()
        if maxDropLV and playerLV > maxDropLV:
            GameWorld.DebugLog("超过最大可掉落等级,不掉落物品,特殊掉落!npcID=%s,playerLV(%s) > maxDropLV(%s)" % (npcID, playerLV, maxDropLV))
            return specDropItemList
        
        # 私有掉落
        fbGradePriItemIDDropDict = IpyGameDataPY.GetFuncEvalCfg("FBGradeEquipDropRate", 3)
@@ -3944,10 +4186,16 @@
        npcID = curNPC.GetNPCID()
        mapID = GameWorld.GetMap().GetMapID()
        mapID = FBCommon.GetRecordMapID(mapID)
        isGameBoss = ChConfig.IsGameBoss(curNPC)
        if isGameBoss:
            GameWorld.Log("NPC开始掉落: npcID=%s,dropPlayerID=%s" % (npcID, dropPlayer.GetPlayerID()), dropPlayer.GetPlayerID())
        if mapID == ChConfig.Def_FBMapID_MunekadoTrial:
            return
        ipyDrop = GetNPCDropIpyData(npcID)
        if not ipyDrop:
            if isGameBoss:
                curWorldLV = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
                GameWorld.ErrLog("取不到NPC掉落信息!npcID=%s,curWorldLV=%s" % (npcID, curWorldLV))
            return
        
        #if mapID == ChConfig.Def_FBMapID_MunekadoTrial:
@@ -3964,13 +4212,17 @@
            dropIDList += [moneyID] * dropMoneyCnt
            
        specItemSign = "SpecItem"
        playerSpecDropList = self.__NPCSpecialDropItem(ownerPlayerList, ipyDrop) # 特殊掉落 [[ownerPlayer, itemID, isBind, isDropInItemPack], ...]  私有特殊掉落 + 击杀次数特殊掉落
        playerSpecDropList = self.__NPCSpecialDropItem(dropPlayer, ownerPlayerList, ipyDrop) # 特殊掉落 [[ownerPlayer, itemID, isBind, isDropInItemPack], ...]  私有特殊掉落 + 击杀次数特殊掉落
        dropIDList += [specItemSign] * len(playerSpecDropList)
        
        if len(dropIDList) > 5:
            #打乱物品顺序
            random.shuffle(playerSpecDropList)
            random.shuffle(dropIDList)
        if not dropIDList and isGameBoss:
            GameWorld.ErrLog("Boss没有掉落: dropPlayerLV=%s,ipyWorldLV=%s,maxDropLV=%s"
                             % (dropPlayer.GetLV(), ipyDrop.GetMaxWorldLV(), ipyDrop.GetMaxDropLV()), dropPlayer.GetPlayerID())
            
        gameMap = GameWorld.GetMap()
        dropPosX, dropPosY = curNPC.GetPosX(), curNPC.GetPosY() # 以NPC为中心点开始掉落
@@ -4001,9 +4253,16 @@
                ownerType, ownerID = hurtType, hurtID
                itemCnt = moneyValue if itemID == moneyID else 1
                isBind = dropIDBindDict.get(itemID, 1)
            curItem = self.__CreateDropItem(curNPC, itemID, itemCnt, isBind)
            if not curItem:
                continue
            if mapID == ChConfig.Def_FBMapID_GatherSoul:#聚魂副本特殊处理
                GameLogic_GatherSoul.KillGatherSoulNPCDropAward(itemID, itemCnt, isBind)
                dropItemDataStr = ChItem.GetMapDropItemDataStr(curItem)
                self.SendVirtualItemDrop(ownerPlayer, itemID, resultX, resultY, dropItemDataStr)
                curItem.Clear()
                continue
            
            if isDropInItemPack:
@@ -4013,17 +4272,22 @@
                if ItemControler.DoLogic_PutItemInPack(ownerPlayer, curItem, True, True,
                                                       event=["NPCDrop", False, {"npcID":npcID}]):
                    #通知客户端
                    vItemDrop = ChPyNetSendPack.tagMCVirtualItemDrop()
                    vItemDrop.ItemTypeID = itemID
                    vItemDrop.PosX = resultX
                    vItemDrop.PosY = resultY
                    vItemDrop.UserData = dropItemDataStr
                    vItemDrop.UserDataLen = len(vItemDrop.UserData)
                    NetPackCommon.SendFakePack(ownerPlayer, vItemDrop)
                    self.SendVirtualItemDrop(ownerPlayer, itemID, resultX, resultY, dropItemDataStr)
            else:
                self.__MapCreateItem(curItem, resultX, resultY, ownerType, ownerID)
        return
    
    def SendVirtualItemDrop(self, player, itemID, posX, posY, userDataStr):
        #通知客户端
        vItemDrop = ChPyNetSendPack.tagMCVirtualItemDrop()
        vItemDrop.ItemTypeID = itemID
        vItemDrop.PosX = posX
        vItemDrop.PosY = posY
        vItemDrop.UserData = userDataStr
        vItemDrop.UserDataLen = len(vItemDrop.UserData)
        NetPackCommon.SendFakePack(player, vItemDrop)
        return
    #---------------------------------------------------------------------
    ## NPC被杀死逻辑处理
    #  @param self 类实例
@@ -4196,8 +4460,12 @@
        self.__LastHurtPlayer = self.__FindLastTimeHurtObjEx()
        self.__MaxHurtPlayer = self.__FindBossMaxHurtObj() # py自定义伤血所得到的Boss最大伤血玩家
        
        self.__AllKillerDict, curTeam, hurtType, hurtID = self.__FindNPCKillerInfo()
        isGameBoss = ChConfig.IsGameBoss(curNPC)
        self.__AllKillerDict, curTeam, hurtType, hurtID = self.__FindNPCKillerInfo(isGameBoss)
        self.__OwnerHurtType, self.__OwnerHurtID = hurtType, hurtID
        if isGameBoss:
            GameWorld.Log("__GiveObjPrize npcID=%s,hurtType=%s,hurtID=%s" % (npcID, hurtType, hurtID))
        #最后一击处理
        self.__DoLastTimeHurtLogic()
        
@@ -4219,7 +4487,8 @@
                    dropPlayer = curPlayer
                self.__KilledByPlayerSetPrize(curPlayer)
                ownerPlayerList.append(curPlayer)
            self.__ownerPlayerList = ownerPlayerList
            #调用物品掉落
            self.__NPCDropItem(dropPlayer, hurtType, hurtID, ownerPlayerList)
@@ -4227,9 +4496,19 @@
        elif curTeam != None:
            self.__KilledByTeamSetPrize(curTeam, hurtType, hurtID)
        
        else:
            GameWorld.ErrLog("NPC归属异常:npcID=%s,hurtType=%s,hurtID=%s" % (npcID, hurtType, hurtID))
        #被仙盟杀死
        elif hurtType == ChConfig.Def_NPCHurtTypeFamily:
            self.__KilledByFamilySetPrize(hurtType, hurtID)
            
        elif isGameBoss:
            GameWorld.ErrLog("NPC归属异常:npcID=%s,hurtType=%s,hurtID=%s" % (npcID, hurtType, hurtID))
        if isGameBoss:
            dataDict = {"objID":curNPC.GetID(), "bossID":npcID, "mapID":GameWorld.GetMap().GetMapID(),
                        "lineID":GameWorld.GetGameWorld().GetLineID(), "teamID":curTeam.GetTeamID() if curTeam else 0,
                            "killerID":self.__AllKillerDict.keys(), "hurtType":hurtType,"hurtID":hurtID}
            DataRecordPack.SendEventPack("KillBossRecord", dataDict)
        if OnNPCDie:
            OnNPCDie(curNPC, hurtType, hurtID)
        
@@ -4295,7 +4574,7 @@
    ## NPC死亡, 分享经验逻辑
    #  @param self 类实例
    #  @return 返回击杀玩家信息元组, (玩家列表实例,队伍实例,归属类型,归属ID)
    def __FindNPCKillerInfo(self):
    def __FindNPCKillerInfo(self, isGameBoss):
        curNPC = self.__Instance
        npcID = curNPC.GetNPCID()
        objID = curNPC.GetID()
@@ -4314,36 +4593,39 @@
        
        #isLog = self.__GetIsLog()
        dropOwnerType = GetDropOwnerType(curNPC)
        #GameWorld.DebugLog("NPC击杀者信息...npcID=%s,dropOwnerType=%s" % (npcID, dropOwnerType))
        if isGameBoss:
            GameWorld.Log("NPC被击杀, key=%s,dropOwnerType=%s" % (key, dropOwnerType))
        # 最大伤血 - 伤血可能被重置
        if dropOwnerType == ChConfig.DropOwnerType_MaxHurt:
            npcHurtList = curNPC.GetPlayerHurtList()
            npcHurtList.Sort()
            #if isLog:
            #    GameWorld.DebugLog("NPC被击杀,npcID=%s,dropOwnerType=%s,hurtCount=%s" % (npcID, dropOwnerType, npcHurtList.GetHurtCount()))
            if isGameBoss:
                GameWorld.Log("hurtCount=%s" % (npcHurtList.GetHurtCount()))
            for i in xrange(npcHurtList.GetHurtCount()):
                #获得最大伤血对象
                maxHurtObj = npcHurtList.GetHurtAt(i)
                #if isLog:
                #    GameWorld.DebugLog("    i=%s,hurtValueType=%s,valueID=%s" % (i, maxHurtObj.GetValueType(), maxHurtObj.GetValueID()))
                curPlayer, curTeam = self.__GetTagByHurtObj(maxHurtObj)
                if isGameBoss:
                    GameWorld.Log("hurtIndex=%s,hurtValueType=%s,valueID=%s" % (i, maxHurtObj.GetValueType(), maxHurtObj.GetValueID()))
                curPlayer, curTeam = self.__GetTagByHurtObj(maxHurtObj, isLog=isGameBoss)
                #当前伤血对象超出指定范围或已经死亡
                if curPlayer == None and curTeam == None:
                    #if isLog:
                    #    GameWorld.DebugLog("        当前伤血对象超出指定范围或已经死亡")
                    if isGameBoss:
                        GameWorld.Log("    当前伤血对象超出指定范围或已经死亡")
                    continue
                
                if curPlayer:
                    playerID = curPlayer.GetPlayerID()
                    if playerID not in killerDict:
                        killerDict[playerID] = curPlayer
                    GameWorld.Log("    归属最大伤血玩家: npcID=%s,dropOwnerType=%s,playerID=%s" % (npcID, dropOwnerType, playerID))
                    if isGameBoss:
                        GameWorld.Log("    归属最大伤血玩家: npcID=%s,dropOwnerType=%s,playerID=%s" % (npcID, dropOwnerType, playerID))
                    return killerDict, None, ChConfig.Def_NPCHurtTypePlayer, playerID
                
                if curTeam:
                    killTeam = curTeam
                    GameWorld.Log("    归属最大伤血队伍: npcID=%s,dropOwnerType=%s,teamID=%s" % (npcID, dropOwnerType, curTeam.GetTeamID()))
                    if isGameBoss:
                        GameWorld.Log("    归属最大伤血队伍: npcID=%s,dropOwnerType=%s,teamID=%s" % (npcID, dropOwnerType, curTeam.GetTeamID()))
                    return killerDict, curTeam, ChConfig.Def_NPCHurtTypeTeam, curTeam.GetTeamID()
        # 最大伤血玩家 - 伤血不会被重置
        elif dropOwnerType == ChConfig.DropOwnerType_MaxHurtPlayer:
@@ -4373,8 +4655,8 @@
        if self.__LastHurtPlayer:
            lastHurtPlayerID = self.__LastHurtPlayer.GetPlayerID()
            teamID = self.__LastHurtPlayer.GetTeamID()
            #if isLog:
            #    GameWorld.DebugLog("    归属最后一击,npcID=%s,lastHurtPlayerID=%s,teamID=%s" % (npcID, lastHurtPlayerID, teamID))
            if isGameBoss:
                GameWorld.Log("    归属最后一击,npcID=%s,lastHurtPlayerID=%s,teamID=%s" % (npcID, lastHurtPlayerID, teamID))
            if teamID:
                killTeam = GameWorld.GetTeamManager().FindTeam(teamID)
            if not killTeam and lastHurtPlayerID not in killerDict:
@@ -4382,9 +4664,8 @@
                
        if dropOwnerType == ChConfig.DropOwnerType_All:
            hurtType = ChConfig.Def_NPCHurtTypeAll
            #if isLog:
            #    GameWorld.DebugLog("    无归属...npcID=%s" % npcID)
            #GameWorld.DebugLog("    无归属...")
            if isGameBoss:
                GameWorld.Log("    无归属...npcID=%s" % npcID)
            
        elif dropOwnerType == ChConfig.DropOwnerType_Faction:
            #阵营归属
@@ -4392,23 +4673,29 @@
            if protectFaction > 0:
                hurtType = ChConfig.Def_NPCHurtTypeFaction
                hurtID = protectFaction
                #GameWorld.DebugLog("    阵营归属...factionID=%s" % protectFaction)
                if isGameBoss:
                    GameWorld.Log("    阵营归属...factionID=%s" % protectFaction)
                
        if hurtType == 0:
            #归属队伍
            if killTeam:
                hurtType = ChConfig.Def_NPCHurtTypeTeam
                hurtID = killTeam.GetTeamID()
                #if isLog:
                #    GameWorld.DebugLog("    归属默认队伍, npcID=%s,teamID=%s" % (npcID, hurtID))
                #GameWorld.DebugLog("    归属默认队伍, teamID=%s" % hurtID)
                if isGameBoss:
                    GameWorld.Log("    归属默认队伍, npcID=%s,teamID=%s" % (npcID, hurtID))
            #伤血归属玩家
            elif killerDict:
                hurtType = ChConfig.Def_NPCHurtTypePlayer
                hurtID = killerDict.keys()[0]
                #if isLog:
                #    GameWorld.DebugLog("    归属默认玩家, npcID=%s,playerID=%s" % (npcID, hurtID))
                #GameWorld.DebugLog("    归属默认玩家, playerID=%s" % hurtID)
                if isGameBoss:
                    GameWorld.Log("    归属默认玩家, npcID=%s,playerID=%s" % (npcID, hurtID))
            elif GameWorld.GetMap().GetMapID() == ChConfig.Def_FBMapID_GatherSoul:
                player = FBCommon.GetCurSingleFBPlayer()
                if player:
                    hurtID = player.GetPlayerID()
                    killerDict[hurtID] = player
                    hurtType = ChConfig.Def_NPCHurtTypePlayer
                    #GameWorld.Log("    聚魂副本归属默认玩家, npcID=%s,playerID=%s" % (npcID, hurtID))
                
        return killerDict, killTeam, hurtType, hurtID
    
@@ -4479,9 +4766,10 @@
    #  @param maxHurtObj 最大伤血对象
    #  @return 返回值, 伤血对象
    #  @remarks 获得伤血对象,支持抢怪
    def __GetTagByHurtObj(self, maxHurtObj, isCheckRefreshArea=False):
    def __GetTagByHurtObj(self, maxHurtObj, isCheckRefreshArea=False, isLog=False):
        #获得死亡的NPC
        curNPC = self.__Instance
        npcID = curNPC.GetNPCID()
        # 伤害的obj类型元组(玩家, 队伍)
        hurtObjTuple = (None, None)
        if maxHurtObj == None:
@@ -4496,18 +4784,28 @@
            curPlayer = GameWorld.GetObj(maxHurtObj.GetValueID(), IPY_GameWorld.gotPlayer)
            
            if curPlayer == None:
                if isLog:
                    GameWorld.Log("找不到该目标伤血玩家: npcID=%s,playerID=%s" % (npcID, maxHurtObj.GetValueID()))
                return hurtObjTuple
            
            #支持抢怪,个人杀死,但自己死亡,不算
            if curPlayer.GetHP() <= 0 or curPlayer.GetPlayerAction() == IPY_GameWorld.paDie:
                if isLog:
                    GameWorld.Log("该目标伤血玩家已死亡: npcID=%s,playerID=%s" % (npcID, maxHurtObj.GetValueID()))
                return hurtObjTuple
            
            if isCheckRefreshArea:
                if not self.GetIsInRefreshPoint(curPlayer.GetPosX(), curPlayer.GetPosY(), refreshPoint):
                    if isLog:
                        GameWorld.Log("该目标伤血玩家不在NPC区域内: npcID=%s,playerID=%s,pos(%s,%s)"
                                      % (npcID, maxHurtObj.GetValueID(), curPlayer.GetPosX(), curPlayer.GetPosY()))
                    return hurtObjTuple
            #如果玩家已经超出指定距离,不加经验
            elif GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(),
                                     curPlayer.GetPosX(), curPlayer.GetPosY()) > ChConfig.Def_Team_GetExpScreenDist:
                if isLog:
                    GameWorld.Log("该目标伤血玩家超出指定距离: npcID=%s,playerID=%s,npcPos(%s,%s),playerPos(%s,%s)"
                                  % (npcID, maxHurtObj.GetValueID(), curNPC.GetPosX(), curNPC.GetPosY(), curPlayer.GetPosX(), curPlayer.GetPosY()))
                return hurtObjTuple
            
            #正常返回
@@ -4518,23 +4816,39 @@
            #获得当前队伍
            teamID = maxHurtObj.GetValueID()
            curTeam = GameWorld.GetTeamManager().FindTeam(teamID)
            if isLog:
                GameWorld.Log("目标伤血队伍: npcID=%s,teamID=%s" % (npcID, teamID))
            if curTeam == None:
                if isLog:
                    GameWorld.Log("找不到目标队伍, teamID=%s" % (teamID))
                return hurtObjTuple
            
            if isLog:
                GameWorld.Log("队伍成员数: GetMemberCount=%s" % (curTeam.GetMemberCount()))
            #遍历队伍,半径为一屏半的距离内的所有队伍/团队成员,可以获得经验
            for i in xrange(curTeam.GetMemberCount()):
                curTeamPlayer = curTeam.GetMember(i)
                if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:
                    if isLog:
                        GameWorld.Log("    i=%s, 无该队员!" % (i))
                    continue
                
                if curTeamPlayer.GetHP() <= 0 or curTeamPlayer.GetPlayerAction() == IPY_GameWorld.paDie:
                    if isLog:
                        GameWorld.Log("    i=%s, 队员已死亡!memPlayerID=%s" % (i, curTeamPlayer.GetPlayerID()))
                    continue
                
                if isCheckRefreshArea:
                    if not self.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint):
                        if isLog:
                            GameWorld.Log("    i=%s, 队员不在NPC区域内!memPlayerID=%s,pos(%s,%s)"
                                          % (i, curTeamPlayer.GetPlayerID(), curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY()))
                        continue
                elif GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(), curTeamPlayer.GetPosX(),
                                       curTeamPlayer.GetPosY()) > ChConfig.Def_Team_GetExpScreenDist:
                    if isLog:
                        GameWorld.Log("    i=%s, 队员超出指定距离!memPlayerID=%s,npcPos(%s,%s),playerPos(%s,%s)"
                                      % (i, curTeamPlayer.GetPlayerID(), curNPC.GetPosX(), curNPC.GetPosY(), curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY()))
                    continue
                
                hurtObjTuple = (None, curTeam)
@@ -4616,12 +4930,67 @@
            
            self.__DoNormalTeamExp(curPlayer)
            self.__KillNPCFuncEx(curPlayer, curNPC, maxHurtID, True)
        self.__ownerPlayerList = ownerPlayerList
            
        #调用物品掉落
        self.__NPCDropItem(dropPlayer, hurtType, hurtID, ownerPlayerList)
        #GameWorld.Log("队伍杀死怪物奖励,逻辑成功结束")
        return
    def __KilledByFamilySetPrize(self, hurtType, hurtID):
        ## 仙盟杀死NPC奖励逻辑
        curNPC = self.__Instance
        maxLV = 0
        dropPlayer = None
        ownerPlayerList = []
        refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
        copyPlayerMgr = GameWorld.GetMapCopyPlayerManager()
        for index in xrange(copyPlayerMgr.GetPlayerCount()):
            player = copyPlayerMgr.GetPlayerByIndex(index)
            if not player:
                continue
            if player.GetFamilyID() != hurtID or not self.GetIsInRefreshPoint(player.GetPosX(), player.GetPosY(), refreshPoint):
                continue
            curPlayerLV = player.GetLV()
            if maxLV < curPlayerLV:
                maxLV = curPlayerLV
                dropPlayer = player
            ownerPlayerList.append(player)
        self.__ownerPlayerList = ownerPlayerList
        if not ownerPlayerList:
            GameWorld.Log("奖励归属仙盟,但是不存在可获得该奖励的成员!npcID=%s,hurtType=%s,hurtID=%s"
                          % (curNPC.GetNPCID(), hurtType, hurtID))
        # 因为仙盟归属boss归属伤血第一的仙盟,仙盟伤血有保护,可能存在伤血第一仙盟在boss死亡的时候都不在
        # 此时掉落计算玩家算最后一击玩家,归属还是算伤血第一仙盟的
        if not dropPlayer:
            dropPlayer = self.__LastHurtPlayer
        if not dropPlayer:
            GameWorld.ErrLog("奖励归属仙盟,找不到掉落玩家!npcID=%s,hurtType=%s,hurtID=%s"
                             % (curNPC.GetNPCID(), hurtType, hurtID))
            return
        # 赶时间,先简单处理直接取最大等级的,之后可按实际情况来
        if not self.__LastHurtPlayer:
            self.__LastHurtPlayer = dropPlayer
        if not self.__MaxHurtPlayer:
            self.__MaxHurtPlayer = dropPlayer
        if not self.__Killer:
            self.__Killer = dropPlayer
        maxHurtID = dropPlayer.GetPlayerID()
        for curPlayer in ownerPlayerList:
            self.__KillNPCFuncEx(curPlayer, curNPC, maxHurtID, False)
        #调用物品掉落
        self.__NPCDropItem(dropPlayer, hurtType, hurtID, ownerPlayerList)
        return
    ## 队伍或自己击杀NPC扩展功能
    #  @param curPlayer
    #  @return None
@@ -4630,45 +4999,32 @@
        npcID = curNPC.GetNPCID()
        defObjType = curNPC.GetGameObjType() 
        mapFBType = GameWorld.GetMap().GetMapFBType()
        mapID = GameWorld.GetMap().GetMapID()
        playerID = curPlayer.GetPlayerID()
        mapID = FBCommon.GetRecordMapID(GameWorld.GetMap().GetMapID())
        #playerID = curPlayer.GetPlayerID()
        
        # 如果是NPC
        if defObjType == IPY_GameWorld.gotNPC:
        if defObjType != IPY_GameWorld.gotNPC:
            return
        # 跨服服务器处理
        if GameWorld.IsCrossServer():
            #掉落归属
            if mapFBType != IPY_GameWorld.fbtNull:
                FBLogic.DoFB_DropOwner(curPlayer , curNPC)
            if ChConfig.IsGameBoss(curNPC):
                OnPlayerKillBoss(curPlayer, npcID, mapID, True)
            return
        #掉落归属
        if mapFBType != IPY_GameWorld.fbtNull:
            FBLogic.DoFB_DropOwner(curPlayer , curNPC)
        else:
            if curNPC.GetLV()>=curPlayer.GetLV() - IpyGameDataPY.GetFuncCfg('DailyQuestKillMonster'):
                PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_KillNPC)
            
            killBossCntLimitDict = IpyGameDataPY.GetFuncCfg('KillBossCntLimit', 1)
            limitIndex = GameWorld.GetDictValueByKey(killBossCntLimitDict, npcID)
            if limitIndex != None:
                #今日杀怪次数+1
                key = ChConfig.Def_PDict_Boss_KillCnt % limitIndex
                newCnt = curPlayer.NomalDictGetProperty(key, 0) + 1
                PlayerControl.NomalDictSetProperty(curPlayer, key, newCnt)
                BossHurtMng.NotifyAttackBossCnt(curPlayer, limitIndex)
                GameWorld.DebugLog("今日杀怪次数  playerID=%s, newCnt=%s" % (curPlayer.GetPlayerID(), newCnt))
                dataDict = {"objID":curNPC.GetID(), "bossID":npcID, "touchCnt":newCnt,
                            "AccID":curPlayer.GetAccID(), "PlayerID":curPlayer.GetPlayerID()}
                DataRecordPack.SendEventPack("AddKillBossCnt", dataDict, curPlayer)
                if limitIndex == 0:
                    # 世界BOSS击杀成就
                    PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_KillWorldBoss, 1)
                    # 每日活动
                    PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_WorldBOSS)
                    PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_WorldBOSS, 1)
                    PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_WorldBoss, 1)
                elif limitIndex == 1: #BOSS之家
                    # BOSS之家BOSS击杀成就
                    PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_KillBossHomeBoss, 1)
                    # 每日活动
                    PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_BOSSHome)
                    PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_BOSSHome, 1)
                    PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_BossHome, 1)
            #击杀特定NPC成就
            PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_KillSpecificNPC, 1, [npcID])
        if ChConfig.IsGameBoss(curNPC):
            OnPlayerKillBoss(curPlayer, npcID, mapID, False)
        return
        
    #---------------------------------------------------------------------
@@ -4690,11 +5046,11 @@
        #不是普通NPC    
        elif npcObjType != IPY_GameWorld.gnotNormal:
            return
        npcID = curNPC.GetNPCID()
        #GameWorld.DebugLog("__MissionOnKillNPC isFeel=%s" % (isFeel), curPlayer.GetPlayerID())
        killBossCntLimitDict = IpyGameDataPY.GetFuncCfg('KillBossCntLimit', 1)
        limitIndex = GameWorld.GetDictValueByKey(killBossCntLimitDict, curNPC.GetNPCID())
        isWorldBoos = limitIndex == 0
        limitIndex = GameWorld.GetDictValueByKey(killBossCntLimitDict, npcID)
        isWorldBoos = limitIndex == ShareDefine.Def_Boss_Func_World
        if isFeel:
            #击杀NPC触发摸怪任务事件
            EventShell.EventRespons_OnKillByFeel(curPlayer, curNPC)
@@ -4705,13 +5061,14 @@
            EventShell.EventRespons_OnKillById(curPlayer, curNPC)
            if isWorldBoos:
                EventShell.EventRespons_KillWorldBoss(curPlayer)
        #击杀特定NPC成就
        PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_KillSpecificNPC, 1, [npcID])
        return
        
    def __GetIsLog(self):
        ## 测试查错日志,临时用
        ## 相关bug: 仙界秘境无经验、boss无掉落
        return False
        return ChConfig.IsGameBoss(self.__Instance)
        #return GameWorld.GetMap().GetMapID() == ChConfig.Def_FBMapID_BZZD or ChConfig.IsGameBoss(self.__Instance)
    #---------------------------------------------------------------------
@@ -4875,7 +5232,7 @@
    return max(value / pow(10, nlen), 1)
Def_CollNPCCfg_Len = 9
Def_CollNPCCfg_Len = 10
(
Def_CollNPCCfg_CanTogether, # 是否允许同时采集
Def_CollNPCCfg_SysMsgMark, # 不可同时采集提示
@@ -4886,6 +5243,7 @@
Def_CollNPCCfg_ZhenQi, # 获得的真气/魔魂
Def_CollNPCCfg_GiveItemModeID, # 获得的物品信息模板编号
Def_CollNPCCfg_NotCostItemNotify, # 消耗品不足提示
Def_CollNPCCfg_LimitSysMsgMark, #采集上限提示
) = range(Def_CollNPCCfg_Len)
@@ -4943,24 +5301,13 @@
    
    # 采集次数判断
    limitMaxTime, todayCollTime = 0, 0
    npcFuncType = curNPC.GetFunctionType()
    npcFuncCollectCntLimitDict = ReadChConfig.GetEvalChConfig('CollectNPCFuncTimeLimit')
    if npcFuncType in npcFuncCollectCntLimitDict:
        vipLV = curPlayer.GetVIPLv()
        limitMaxTime = eval(npcFuncCollectCntLimitDict[npcFuncType])
        todayBuyTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcBuyTime % npcFuncType)
        todayCollTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcCollTime % npcFuncType)
        limitMaxTime += todayBuyTime
        GameWorld.DebugLog("    vipLV=%s,maxTime=%s,todayTime=%s,todayBuyTime=%s" % (vipLV, limitMaxTime, todayCollTime, todayBuyTime))
    npcIDCollectCntLimitDict = ReadChConfig.GetEvalChConfig("CollectNPCIDTimeLimit")
    if npcID in npcIDCollectCntLimitDict:
        limitMaxTime = npcIDCollectCntLimitDict[npcID][0]
        todayCollTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcIDCollTime % npcID)
        GameWorld.DebugLog("    maxTime=%s,todayTime=%s" % (limitMaxTime, todayCollTime))
    collLimitInfo = GetCollTimeLimitInfo(curPlayer, npcID)
    if collLimitInfo:
        limitMaxTime, todayCollTime = collLimitInfo
        
    if limitMaxTime > 0 and todayCollTime >= limitMaxTime:
        PlayerControl.NotifyCode(curPlayer, "GeRen_liubo_807125")
        PlayerControl.NotifyCode(curPlayer, collectNPCInfo[Def_CollNPCCfg_LimitSysMsgMark], [limitMaxTime])
        return True
    
    # 采集消耗
@@ -5000,11 +5347,24 @@
    PlayerControl.Sync_PrepareBegin(curPlayer, prepareTime, IPY_GameWorld.pstMissionCollecting, \
                                    prepareID=curNPC.GetID())
    FBLogic.OnBeginCollect(curPlayer, curNPC)
    ##添加这个NPC的伤血列表,用于判断可否同时采集,改为字典判断
    AttackCommon.AddHurtValue(curNPC, curPlayer.GetPlayerID(), ChConfig.Def_NPCHurtTypePlayer, 1)
    return
def GetCollTimeLimitInfo(curPlayer, npcID):
    ## 获取采集次数限制信息
    npcIDCollectCntLimitDict = ReadChConfig.GetEvalChConfig("CollectNPCIDTimeLimit")
    for npcIDTuple in npcIDCollectCntLimitDict.keys():
        if npcID not in npcIDTuple:
            continue
        limitMaxTime = npcIDCollectCntLimitDict[npcIDTuple][0]
        todayCollTime = 0
        for collNPCID in npcIDTuple:
            todayCollTime += curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcIDCollTime % collNPCID)
        GameWorld.DebugLog("    limitMaxTime=%s,todayCollTime=%s" % (limitMaxTime, todayCollTime))
        return limitMaxTime, todayCollTime
    return
## 设置玩家采集该NPC
#  @param curPlayer:玩家实例
@@ -5089,11 +5449,11 @@
    openCnt = clientData.OpenCnt
    isAutoBuy = clientData.IsAutoBuy
    isOnlyGold = clientData.IsOnlyGold
    npcIDCollectCntLimitDict = ReadChConfig.GetEvalChConfig("CollectNPCIDTimeLimit")
    if npcID not in npcIDCollectCntLimitDict:
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    collLimitInfo = GetCollTimeLimitInfo(curPlayer, npcID)
    if not collLimitInfo:
        GameWorld.DebugLog("该NPC不是自定义采集NPC箱子, 不可开启! npcID=%s" % npcID)
        return
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    GameWorld.DebugLog("OnOpenCollNPCBox...npcID=%s,openCnt=%s,isAutoBuy=%s" % (npcID, openCnt, isAutoBuy))
    DoGetCollectionNPCAwardLogic(curPlayer, npcID, isAutoBuy, openCnt, mapID, isOnlyGold)
    return
@@ -5123,22 +5483,14 @@
    curNPC = GameWorld.GetGameData().FindNPCDataByID(npcID)
    if not curNPC:
        return True
    # 根据NPC功能号
    npcFuncType = curNPC.GetFunctionType()
    npcFuncCollectCntLimitDict = ReadChConfig.GetEvalChConfig('CollectNPCFuncTimeLimit')
    limitMaxTime = 0 # 0表示不限制次数
    todayCollTime = 0
    if npcFuncType in npcFuncCollectCntLimitDict:
        vipLV = curPlayer.GetVIPLv()
        todayCollTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcCollTime % npcFuncType)
        limitMaxTime = eval(npcFuncCollectCntLimitDict[npcFuncType])
    
    # 根据NPCID
    npcIDCollectCntLimitDict = ReadChConfig.GetEvalChConfig("CollectNPCIDTimeLimit")
    if npcID in npcIDCollectCntLimitDict:
        limitMaxTime = npcIDCollectCntLimitDict[npcID][0]
        todayCollTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcIDCollTime % npcID)
    collLimitInfo = GetCollTimeLimitInfo(curPlayer, npcID)
    if collLimitInfo:
        limitMaxTime, todayCollTime = collLimitInfo
        
    if limitMaxTime > 0:
        canCollectCnt = max(0, limitMaxTime - todayCollTime)
@@ -5240,19 +5592,13 @@
                               % (unCostCnt, itemGold, reduceGold), curPlayer.GetPlayerID())
        
    # 增加当日采集次数
    if limitMaxTime > 0:
    if collLimitInfo:
        updCollTime = todayCollTime + successCnt
        npcFuncType = curNPC.GetFunctionType()
        npcFuncCollectCntLimitDict = ReadChConfig.GetEvalChConfig('CollectNPCFuncTimeLimit')
        if npcFuncType in npcFuncCollectCntLimitDict:
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcCollTime % npcFuncType, updCollTime)
            SyncCollNPCTime(curPlayer, [npcFuncType])
        if npcID in npcIDCollectCntLimitDict:
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcIDCollTime % npcID, updCollTime)
            SyncCollNPCTime(curPlayer, npcIDList=[npcID])
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcIDCollTime % npcID, updCollTime)
        SyncCollNPCTime(curPlayer, npcIDList=[npcID])
        GameWorld.DebugLog("        增加当日采集次数: todayCollTime=%s,updCollTime=%s" % (todayCollTime, updCollTime))
    #采集成就
    PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_Collect, successCnt, [npcID])
    SyncCollectionItemInfo(curPlayer, addExp, addMoney, addZhenQi, giveItemInfoList, npcID)
    #DataRecordPack.DR_CollectNPCOK(curPlayer, npcID, addMoney, addExp, addZhenQi, giveItemInfoList)
    return True
@@ -5311,38 +5657,38 @@
    GameWorld.DebugLog("    最终可得到物品giveItemInfoList=%s" % giveItemInfoList)
    
    syncItemInfoList = [] # 同步的采集到的物品信息列表
    for itemType, itemID, itemCnt, isBind in giveItemInfoList:
    for itemID, itemCnt, isBind in giveItemInfoList:
        if not ItemCommon.CheckPackHasSpace(curPlayer, IPY_GameWorld.rptItem):
            break
        
        isBind = setBind or isBind
        if itemType == 0:
            getItemObj = ItemControler.GetOutPutItemObj(itemID)
        elif itemType == 1:
            itemDictData = ItemControler.GetAppointItemDictData(itemID, isBind)
            getItemObj = ItemControler.GetItemByData(itemDictData)
        elif itemType == 2:
            quality = ItemCommon.GetRandEquipQualityByTable(itemID, "CollectEquipRandQuality")
            if quality == 0:
                isBroadcast = False
                getItemObj = ItemCommon.RandNormalEquip(curPlayer, itemID, isBind, "CollectNormalEquip")
            else:
                getItemObj, isBroadcast = ItemCommon.RandGreateEquip(curPlayer, itemID, isBind, "CollectGreateEquip", quality)
            if getItemObj == None:
                continue
            itemID = getItemObj.GetItemTypeID()
        getItemObj = ItemControler.GetOutPutItemObj(itemID)
#        elif itemType == 1:
#            itemDictData = ItemControler.GetAppointItemDictData(itemID, isBind)
#            getItemObj = ItemControler.GetItemByData(itemDictData)
#        elif itemType == 2:
#            quality = ItemCommon.GetRandEquipQualityByTable(itemID, "CollectEquipRandQuality")
#            if quality == 0:
#                isBroadcast = False
#                getItemObj = ItemCommon.RandNormalEquip(curPlayer, itemID, isBind, "CollectNormalEquip")
#            else:
#                getItemObj, isBroadcast = ItemCommon.RandGreateEquip(curPlayer, itemID, isBind, "CollectGreateEquip", quality)
#            if getItemObj == None:
#                continue
#
#            itemID = getItemObj.GetItemTypeID()
        userData = getItemObj.GetUserData()
        getItemObj.SetCount(itemCnt)
        getItemObj.SetIsBind(isBind)
        ItemCommon.NotifyItemDropByKill(curPlayer, getItemObj, npcID)
            
        SendGameServerGoodItemRecord(mapID, npcID, curPlayer.GetPlayerName(), curPlayer.GetPlayerID(), itemID)
        #SendGameServerGoodItemRecord(mapID, npcID, curPlayer.GetPlayerName(), curPlayer.GetPlayerID(), itemID)
        #可以放入背包
        if not ItemControler.DoLogic_PutItemInPack(curPlayer, getItemObj, True, True,
                                                   event=["CollectNPC", False, {"npcID":npcID}]):
            break
        syncItemInfoList.append([itemType, itemID, itemCnt, isBind, userData])
        syncItemInfoList.append([itemID, itemCnt, isBind, userData])
    return syncItemInfoList
## 采集结果同步
@@ -5429,24 +5775,11 @@
#  @param funcType:功能号,默认为0,即同步配表中全部
#  @return None
def SyncCollNPCTime(curPlayer, funcTypeList=[], npcIDList=[]):
    if not funcTypeList:
        npcFuncCollectCntLimitDict = ReadChConfig.GetEvalChConfig('CollectNPCFuncTimeLimit')
        funcTypeList = npcFuncCollectCntLimitDict.keys()
    if not npcIDList:
        collectNPCIDTimeLimit = ReadChConfig.GetEvalChConfig('CollectNPCIDTimeLimit')
        npcIDList = collectNPCIDTimeLimit.keys()
#    if funcTypeList:
#        collection = ChPyNetSendPack.tagMCFuncNPCCollectionCnt()
#        for fType in funcTypeList:
#            todayCollTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcCollTime % fType)
#            collection.Clear()
#            collection.FuncType = fType
#            collection.CollectionCnt = todayCollTime
#            collection.BuyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcBuyTime % fType)
#            NetPackCommon.SendFakePack(curPlayer, collection)
        for npcIDTuple in collectNPCIDTimeLimit.keys():
            npcIDList += list(npcIDTuple)
    if npcIDList:
        npcIDCollInfo = ChPyNetSendPack.tagMCNPCIDCollectionCntInfo()
        npcIDCollInfo.Clear()
@@ -5466,18 +5799,13 @@
#  @param curPlayer:玩家实例
#  @return None
def CollNPCTimeOnDay(curPlayer, funcTypeList=[]):
    npcFuncCollectCntLimitDict = ReadChConfig.GetEvalChConfig('CollectNPCFuncTimeLimit')
    funcTypeList = npcFuncCollectCntLimitDict.keys() if not funcTypeList else funcTypeList
    for funcType in funcTypeList:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcCollTime % funcType, 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcBuyTime % funcType, 0)
    collectNPCIDTimeLimit = ReadChConfig.GetEvalChConfig('CollectNPCIDTimeLimit')
    for npcID, collCntInfo in collectNPCIDTimeLimit.items():
    for npcIDTuple, collCntInfo in collectNPCIDTimeLimit.items():
        isResetOnDay = collCntInfo[1]
        if not isResetOnDay:
            continue
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcIDCollTime % npcID, 0)
        for npcID in npcIDTuple:
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcIDCollTime % npcID, 0)
    
    SyncCollNPCTime(curPlayer)
    return
@@ -5565,28 +5893,43 @@
#  @param queryNPCIDList:查询的NPCID列表
#  @param tick
#  @return {NPCID:cnt}
def GetNPCCntInfo(queryNPCIDList, tick):
def GetNPCCntInfo(queryNPCIDList, tick, copyMapID=None):
    npcCntDict = {}
    if not queryNPCIDList:
        return npcCntDict
    #if not queryNPCIDList:
    #    return npcCntDict
    
    gameNPCManager = GameWorld.GetNPCManager()
    GameWorld.DebugLog("GetNPCCntInfo...queryNPCIDList=%s" % (str(queryNPCIDList)))
    for index in xrange(gameNPCManager.GetNPCCount()):
        curNPC = gameNPCManager.GetNPCByIndex(index)
        curID = curNPC.GetID()
        if curID == 0:
            continue
        curNPCID = curNPC.GetNPCID()
        if curNPCID not in queryNPCIDList:
            continue
        if curNPC.GetCurAction() == IPY_GameWorld.laNPCDie or not curNPC.IsAlive():
            continue
        npcCntDict[curNPCID] = npcCntDict.get(curNPCID, 0) + 1
    if isinstance(copyMapID, int):
        for index in xrange(gameNPCManager.GetNPCCountByGWIndex(copyMapID)):
            curNPC = gameNPCManager.GetNPCByIndexByGWIndex(copyMapID, index)
            curID = curNPC.GetID()
            if curID == 0:
                continue
            curNPCID = curNPC.GetNPCID()
            if queryNPCIDList and curNPCID not in queryNPCIDList:
                continue
            if curNPC.GetCurAction() == IPY_GameWorld.laNPCDie or not curNPC.IsAlive():
                continue
            npcCntDict[curNPCID] = npcCntDict.get(curNPCID, 0) + 1
    else:
        for index in xrange(gameNPCManager.GetNPCCount()):
            curNPC = gameNPCManager.GetNPCByIndex(index)
            curID = curNPC.GetID()
            if curID == 0:
                continue
            curNPCID = curNPC.GetNPCID()
            if queryNPCIDList and curNPCID not in queryNPCIDList:
                continue
            if curNPC.GetCurAction() == IPY_GameWorld.laNPCDie or not curNPC.IsAlive():
                continue
            npcCntDict[curNPCID] = npcCntDict.get(curNPCID, 0) + 1
                
    GameWorld.DebugLog("    npcCntDict=%s" % (str(npcCntDict)))
    return npcCntDict
@@ -5613,7 +5956,7 @@
    NetPackCommon.SendFakePack(curPlayer, npcInfoPack)
    return
def SendGameServerGoodItemRecord(mapID, npcID, playerName, playerID, itemID, equipInfo=[]):
def SendGameServerGoodItemRecord(mapID, npcID, playerName, playerID, itemID, equipInfo=[], serverGroupID=0):
    # @param equipInfo: [equipPlace, itemClassLV, itemColor, itemQuality, itemUserData]
#    GameWorld.DebugLog("检查物品是否发送GameServer: mapID=%s, npcID=%s, playerName=%s, itemID=%s" 
#                       % (mapID, npcID, playerName, itemID))
@@ -5644,13 +5987,9 @@
    if not needRecord:
        return
    
    dropEquipMsg = str([playerID, playerName, mapID, npcID, itemID, itemUserData, weightValue])
    dropEquipMsg = str([playerID, playerName, mapID, npcID, itemID, itemUserData, weightValue, serverGroupID])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, 'BossDropGoodItem', dropEquipMsg, len(dropEquipMsg))
    GameWorld.DebugLog("发送GameServer记录拾取掉落好物品: %s" % dropEquipMsg, playerID)
    msgList = [playerName, playerID, mapID, npcID, itemID, itemUserData]
    PlayerControl.WorldNotify(0, 'DropRecord' , msgList)
    return
#// A5 52 购买功能NPC采集次数 #tagCMBuyCollectionCnt
@@ -5782,3 +6121,13 @@
    GameWorld.DebugLog("通知GameServer地图Boss分流信息: mapID=%s,lineID=%s,shuntPlayerDict=%s" % (mapID, lineID, shuntPlayerDict), lineID)
    return
def NPCSpeedChangeNotify(curNPC, speed):
    ##通知NPC速度
    sendPack = ChNetSendPack.tagObjInfoRefresh()
    sendPack.Clear()
    sendPack.ObjID = curNPC.GetID()
    sendPack.ObjType = curNPC.GetGameObjType()
    sendPack.RefreshType = IPY_GameWorld.CDBPlayerRefresh_Speed
    sendPack.Value = speed
    curNPC.NotifyAll(sendPack.GetBuffer(), sendPack.GetLength())
    return