ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossFamilyGCZ.py
@@ -28,6 +28,7 @@
import NetPackCommon
import PyDataManager
import PlayerControl
import GameXiangong
import PyGameData
import ChConfig
import ChPlayer
@@ -37,14 +38,14 @@
import time
## 1-公示期;99-领奖期;轮次状态=轮次*10+轮次阶段;轮次阶段:1-分组战备;2-战斗;3-休战结算
## 1-公示期;99-领奖期;轮次状态=轮次*10+轮次阶段;轮次阶段:1-分组战备;2-战斗;3-休战
FamilyGCZState_Publicity = 1 # 公示期
FamilyGCZState_Award = 99 # 结束领奖期
# 轮次状态信息
FamilyGCZRoundState_Group = 1 # 分组+战备
FamilyGCZRoundState_Fight = 2 # 战斗
FamilyGCZRoundState_Over = 3 # 休战结算
FamilyGCZRoundState_Over = 3 # 休战,该阶段可有可无,由策划配置决定,程序结算逻辑不依赖与此状态
# 战场类型
BatTypeList = (
@@ -52,6 +53,20 @@
BatType_Middle, # 中级 2
BatType_High, # 高级 3
) = range(1, 1 + 3)
BatType_Min = BatType_Junior
BatType_Max = BatType_High
# 晋降级类型
(
UpdownState_None, # 未处理 0
UpdownState_Down, # 降级 1
UpdownState_Keep, # 保级 2
UpdownState_Up, # 晋级 3
) = range(4)
# 每组固定仙盟数
Def_PerGroupFamilyCnt = 12
# 攻击类型
(
@@ -76,7 +91,8 @@
AwwardType_Guess, # 竞猜
AwwardType_PlayerHurt, # 个人伤害排行
AwwardType_FamilyScore, # 仙盟积分排行
) = range(3)
AwwardType_Like, # 点赞
) = range(4)
class FamilyGCZFighter(object):
@@ -170,7 +186,7 @@
        self.fighterIDList.pop(0)
        if not self.fighterIDList:
            return 0
        nextGuardID = self.fighterIDList
        nextGuardID = self.fighterIDList[0]
        if self.IsGuardNPC(nextGuardID):
            self.guardNPC.hp = self.guardNPC.hpMax
            GameWorld.DebugLog("    切换下一个NPC守卫: %s, %s/%s" % (nextGuardID, self.guardNPC.hp, self.guardNPC.hpMax))
@@ -233,9 +249,8 @@
        return True
    
    def SetPlayerInCity(self, playerID, serverGroupID):
        if playerID in self.inCityPlayerIDList:
            return
        self.inCityPlayerIDList.append(playerID)
        if playerID not in self.inCityPlayerIDList:
            self.inCityPlayerIDList.append(playerID)
        Sync_FamilyGCZBatCityInfo(self.zoneID, self.batType, self.groupNum, self.cityID, toPlayerServerDict={playerID:serverGroupID})
        return
    
@@ -276,7 +291,8 @@
        self.groupNum = 0
        self.campLV = 1 # 大本营等级
        self.campExp = 0
        self.score = 0 # 活动总积分
        self.roundInfoDict = {} # 仙盟轮次信息汇总 {轮次:{k:v, ...}, ...} k-batType,groupNum,rank,score,updownState
        self.sortWeight = 0 # 榜单比较权值,用于平分或伤害相同时的比较权值
        
        self.familyName = ""
        self.lv = 0
@@ -315,6 +331,13 @@
            Sync_FamilyGCZCampInfo(self.zoneID, self.familyID)
        return isOK
    
    def GetTotalScore(self):
        ## 获取仙盟当前总积分,所有轮次积分的总和
        totalScore = 0
        for info in self.roundInfoDict.values():
            totalScore += info.get("score", 0)
        return totalScore
    def AddCampExp(self, playerID, addCampExp, isNotify=True):
        ## 大本营加经验
        # @return: 是否升级
@@ -329,26 +352,29 @@
        updCampLV = campLV
        
        # 满级了也不限制捐献,让玩家可以继续消耗多余的道具
        campIpyData = IpyGameDataPY.GetIpyGameData("CrossActFamilyGCZCampLV", campLV)
        if campIpyData and campIpyData.GetLVUPNeedExp():
            lvupNeedExp = campIpyData.GetLVUPNeedExp()
            if updCampExp >= lvupNeedExp and IpyGameDataPY.GetIpyGameDataNotLog("CrossActFamilyGCZCampLV", campLV + 1):
                updCampExp -= lvupNeedExp
                updCampLV = campLV + 1
        campIpyData = IpyGameDataPY.GetIpyGameData("CrossActFamilyGCZCampLV", updCampLV)
        lvupNeedExp = campIpyData.GetLVUPNeedExp() if campIpyData else 0
        nextIpyData = IpyGameDataPY.GetIpyGameDataNotLog("CrossActFamilyGCZCampLV", updCampLV + 1)
        GameWorld.DebugLog("增加大本营经验: familyID=%s,playerID=%s,addCampExp=%s,campLV-Exp=%s-%s,updLV-Exp=%s-%s,lvupNeedExp=%s"
                           % (self.familyID, playerID, addCampExp, campLV, campExp, updCampLV, updCampExp, lvupNeedExp))
        while lvupNeedExp > 0 and updCampExp >= lvupNeedExp and nextIpyData:
            updCampLV += 1
            updCampExp -= lvupNeedExp
            campIpyData = IpyGameDataPY.GetIpyGameData("CrossActFamilyGCZCampLV", updCampLV)
            lvupNeedExp = campIpyData.GetLVUPNeedExp() if campIpyData else 0
            nextIpyData = IpyGameDataPY.GetIpyGameDataNotLog("CrossActFamilyGCZCampLV", updCampLV + 1)
            GameWorld.DebugLog("    大本营升级: updCampLV=%s,updCampExp=%s,lvupNeedExp=%s" % (updCampLV, updCampExp, lvupNeedExp))
        self.campLV = updCampLV
        self.campExp = updCampExp
        self.memAddCampExpInfo[playerID] = self.memAddCampExpInfo.get(playerID, 0) + addCampExp
        GameWorld.DebugLog("增加大本营经验: familyID=%s,playerID=%s,addCampExp=%s,campLV-Exp=%s-%s,updLV-Exp=%s-%s,playerAddExpTotal=%s"
                           % (self.familyID, playerID, addCampExp, campLV, campExp, updCampLV, updCampExp, self.memAddCampExpInfo[playerID]))
        
        # 升级了
        defMemIDList = [] # 加经验默认不通知防守成员属性
        isLVUP = False
        if campLV != updCampLV:
            isLVUP = True
            GameWorld.DebugLog("大本营升级了! familyID=%s,campLV=%s to %s" % (self.familyID, campLV, updCampLV))
            GameWorld.DebugLog("大本营等级变更! familyID=%s,campLV=%s to %s" % (self.familyID, campLV, updCampLV))
            if self.RefreshFamilyAttr(False):
                defMemIDList = None # 升级后成功刷成员属性后,额外附带所有防守成员信息,战斗阶段属性是锁定的
                
@@ -470,9 +496,8 @@
        return newCity
    
    def SetPlayerInScene(self, playerID, serverGroupID):
        if playerID in self.inBatScenePlayerIDList:
            return
        self.inBatScenePlayerIDList.append(playerID)
        if playerID not in self.inBatScenePlayerIDList:
            self.inBatScenePlayerIDList.append(playerID)
        Sync_FamilyGCZBatSceneInfo(self.zoneID, self.batType, self.groupNum, toPlayerServerDict={playerID:serverGroupID})
        return
    
@@ -491,6 +516,8 @@
        self.joinFamilyDict = {} # 参与仙盟对象信息 {familyID:FamilyGCZFamily, ...}
        self.joinMemberDict = {} # 参与玩家对象信息 {playerID:FamilyGCZMember, ...}
        self.roundGroupDict = {} # 当前轮次分组信息 {batType:{groupNum:FamilyGCZBatGroup, ...}, ...}
        self.roundNum = 0 # roundGroupDict 数据对应的轮次
        self.roundOver = 0 # roundNum轮次是否已结算了
        self.familyGuessDict = {} # 仙盟竞猜热度信息 {familyID:value, ...}
        self.playerGuessDict = {} # 玩家竞猜记录 {playerID:[familyID, ...], ...}
        
@@ -500,8 +527,10 @@
        self.inBatCityPlayerIDDict = {} # 在战斗城池中的玩家ID  {(batType, groupNum, cityID):[playerID, ...], ...}
        return
    
    def OnRoundReset(self):
    def OnRoundReset(self, roundNum):
        ## 分区轮次重置
        self.roundNum = roundNum
        self.roundOver = 0
        self.roundGroupDict = {}
        self.inBatScenePlayerIDDict = {}
        self.inBatCityPlayerIDDict = {}
@@ -722,6 +751,8 @@
        zone.lockFamilyIDList = zoneDataDict.get("lockFamilyIDList", [])
        zone.familyGuessDict = zoneDataDict.get("familyGuessDict", {})
        zone.playerGuessDict = zoneDataDict.get("playerGuessDict", {})
        zone.roundNum = zoneDataDict.get("roundNum", 0)
        zone.roundOver = zoneDataDict.get("roundOver", 0)
        GameWorld.Log("加载分区记录! zoneID=%s,joinFamilyCnt=%s,lockCnt=%s,%s" % (zoneID, zone.joinFamilyCnt, len(zone.lockFamilyIDList), zone.lockFamilyIDList))
        
        familyRecList = gameRecMgr.GetGameRecDataList(ShareDefine.Def_GameRecType_FamilyGCZJoinFamily, zoneID)
@@ -731,7 +762,6 @@
            groupNum = familyRecData.GetValue3()
            campLV = familyRecData.GetValue4()
            campExp = familyRecData.GetValue5()
            score = familyRecData.GetValue6()
            familyDataDict = familyRecData.GetUserDataDict()
            
            joinFamily = zone.AddZoneJoinFamily(familyID)
@@ -739,7 +769,6 @@
            joinFamily.groupNum = groupNum
            joinFamily.campLV = campLV
            joinFamily.campExp = campExp
            joinFamily.score = score
            joinFamily.familyName = familyDataDict.get("familyName", "")
            joinFamily.leaderID = familyDataDict.get("leaderID", 0)
            joinFamily.leaderName = familyDataDict.get("leaderName", "")
@@ -749,9 +778,11 @@
            joinFamily.fightPowerTotal = familyDataDict.get("fightPowerTotal", 0)
            joinFamily.joinMemberIDList = familyDataDict.get("joinMemberIDList", [])
            joinFamily.memAddCampExpInfo = familyDataDict.get("memAddCampExpInfo", {})
            joinFamily.roundInfoDict = familyDataDict.get("roundInfoDict", {})
            joinFamily.sortWeight = familyDataDict.get("sortWeight", 0)
            
            GameWorld.Log("    加载分区仙盟 zoneID=%s,%s,familyID=%s,campLV=%s-%s,score=%s,,memAddCampExpInfo=%s"
                          % (zoneID, fNum, familyID, joinFamily.campLV, joinFamily.campExp, joinFamily.score, joinFamily.memAddCampExpInfo))
            GameWorld.Log("    加载分区仙盟 zoneID=%s,%s,familyID=%s,campLV=%s-%s,roundInfoDict=%s"
                          % (zoneID, fNum, familyID, joinFamily.campLV, joinFamily.campExp, joinFamily.roundInfoDict))
            
        memberRecList = gameRecMgr.GetGameRecDataList(ShareDefine.Def_GameRecType_FamilyGCZJoinMember, zoneID)
        for pNum, memberRecData in enumerate(memberRecList, 1):
@@ -842,7 +873,8 @@
        zoneRecData = gameRecMgr.AddGameRecData(ShareDefine.Def_GameRecType_FamilyGCZMgr, zoneID)
        zoneRecData.SetValue1(zone.joinFamilyCnt)
        lockFamilyIDList = zone.GetZoneLockFamilyIDList()
        zoneData = {"lockFamilyIDList":lockFamilyIDList, "familyGuessDict":zone.familyGuessDict, "playerGuessDict":zone.playerGuessDict}
        zoneData = {"lockFamilyIDList":lockFamilyIDList, "familyGuessDict":zone.familyGuessDict, "playerGuessDict":zone.playerGuessDict,
                    "roundNum":zone.roundNum, "roundOver":zone.roundOver}
        zoneRecData.SetUserData(zoneData)
        
        GameWorld.Log("----- zoneID=%s,joinFamilyCnt=%s,lockFamilyIDList=%s,%s" % (zoneID, zone.joinFamilyCnt, len(lockFamilyIDList), lockFamilyIDList))
@@ -881,14 +913,14 @@
            familyRecData.SetValue3(joinFamily.groupNum)
            familyRecData.SetValue4(joinFamily.campLV)
            familyRecData.SetValue5(joinFamily.campExp)
            familyRecData.SetValue6(joinFamily.score)
            
            familyData = {"familyName":joinFamily.familyName, "leaderID":joinFamily.leaderID, "leaderName":joinFamily.leaderName,
                          "lv":joinFamily.lv, "emblemID":joinFamily.emblemID, "serverID":joinFamily.serverID, "fightPowerTotal":joinFamily.fightPowerTotal,
                          "joinMemberIDList":joinFamily.joinMemberIDList, "memAddCampExpInfo":joinFamily.memAddCampExpInfo
                          "joinMemberIDList":joinFamily.joinMemberIDList, "memAddCampExpInfo":joinFamily.memAddCampExpInfo,
                          "roundInfoDict":joinFamily.roundInfoDict, "sortWeight":joinFamily.sortWeight
                          }
            familyRecData.SetUserData(familyData)
            GameWorld.Log("    保存分区仙盟 zoneID=%s,%s,familyID=%s,campLV=%s-%s,score=%s," % (zoneID, fNum, familyID, joinFamily.campLV, joinFamily.campExp, joinFamily.score))
            GameWorld.Log("    保存分区仙盟 zoneID=%s,%s,familyID=%s,campLV=%s-%s,roundInfoDict=%s" % (zoneID, fNum, familyID, joinFamily.campLV, joinFamily.campExp, joinFamily.roundInfoDict))
            
            for pNum, playerID in enumerate(joinFamily.joinMemberIDList, 1):
                joinMember = zone.GetZoneJoinMember(playerID)
@@ -983,9 +1015,9 @@
            zone = gczMgr.GetActZone(zoneID)
            zone.OnRoundFightStart()
            
        # 结算
        # 结算,注:程序的结算流程不依赖与此状态,如果策划有配置,则直接在该阶段结算,如果没有配置则在分组时、活动领奖时之前校验上一轮结算
        elif roundState == FamilyGCZRoundState_Over:
            DoRoundOver(curRound, zoneID)
            DoRoundOver(zoneID)
            
    else:
        pass
@@ -1137,71 +1169,55 @@
    ## 执行轮次分组
    
    GameWorld.Log("========== 仙盟攻城战轮次分组: zoneID=%s,curRound=%s,joinFamilyCnt=%s" % (zoneID, curRound, joinFamilyCnt))
    # 参与仙盟数对应分组及晋级规则 {参与仙盟数:{轮次:{战场类型:[组数, 前x名晋级, x名开始降级], ...}, ...}, ...}
    DoRoundOver(zoneID)
    # 参与仙盟数对应分组及晋级规则 {参与仙盟数:{轮次:{战场类型:[组数, 前x名晋级, 后x名降级], ...}, ...}, ...}
    FamilyGCZGroupSet = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGroupSet", 1, {})
    if joinFamilyCnt not in FamilyGCZGroupSet:
    if str(joinFamilyCnt) not in FamilyGCZGroupSet:
        return
    roundSetDict = FamilyGCZGroupSet[joinFamilyCnt]
    if curRound not in roundSetDict:
    roundSetDict = FamilyGCZGroupSet[str(joinFamilyCnt)]
    if str(curRound) not in roundSetDict:
        return
    batTypeDict = roundSetDict[curRound]
    batTypeDict = roundSetDict[str(curRound)]
    
    groupValue1 = zoneID
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    gczMgr = GetFamilyGCZMgr()
    zone = gczMgr.GetActZone(zoneID)
    # 先处理上一轮晋级、保级、降级,需在重置前处理
    nextBatTypeFamilyInfo = {} # 下一轮战场仙盟ID分配信息 {batType:[familyID, ...], ....}
    preRound = curRound - 1
    preBatTypeDict = roundSetDict.get(preRound, {})
    for batType in BatTypeList: # 按战场类型顺序遍历处理
        if batType not in zone.roundGroupDict:
            continue
        groupDict = zone.roundGroupDict[batType]
        groupNumList = groupDict.keys()
        groupNumList.sort()
        preRoundSet = preBatTypeDict.get(batType, [0, 0])
        upRank = preRoundSet[1] # 前x晋级
        downRank = preRoundSet[2] if len(preRoundSet) > 2 else 0 # 大于等于x名降级,为0时不降级
        GameWorld.Log("上轮战场类型分组: preRound=%s,batType=%s,upRank=%s,downRank=%s,%s" % (preRound, batType, upRank, downRank, groupNumList))
        for groupNum in groupNumList:
            groupValue2 = GetRoundHurtGroupID(batType, groupNum)
            billboardObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, groupValue1, groupValue2)
            billboardObj.SortData()
            upFamilyIDList, keepFamilyIDList, downFamilyIDList = [], [], []
            for bIndex in range(billboardObj.GetCount()):
                billData = billboardObj.At(bIndex)
                familyID = billData.ID
                rank = bIndex + 1
                if rank <= upRank:
                    nextBatType = min(BatType_High, batType + 1)
                    upFamilyIDList.append(familyID)
                elif downRank and rank >= downRank:
                    nextBatType = max(BatType_Junior, batType - 1)
                    downFamilyIDList.append(familyID)
                # 除了升降级的就是保级
                else:
                    nextBatType = batType
                    keepFamilyIDList.append(familyID)
                if nextBatType not in nextBatTypeFamilyInfo:
                    nextBatTypeFamilyInfo[nextBatType] = []
                familyIDList = nextBatTypeFamilyInfo[nextBatType]
                if familyID not in familyIDList:
                    familyIDList.append(familyID)
            GameWorld.Log("    升保降级zoneID=%s,batType=%s,groupNum=%s,up:%s,keep:%s,down:%s,%s"
                          % (zoneID, batType, groupNum, upFamilyIDList, keepFamilyIDList, downFamilyIDList, nextBatTypeFamilyInfo))
    #汇总上一轮升降级明细
    nextBatTypeFamilyInfo = {} # 根据上一轮结算的升降级状态汇总出的本轮仙盟ID战场归组
    if curRound > 1:
        preRound = curRound - 1
        lockFamilyIDList = zone.GetZoneLockFamilyIDList()
        for familyID in lockFamilyIDList:
            joinFamily = zone.GetZoneJoinFamily(familyID)
            if not joinFamily:
                continue
            roundInfo = joinFamily.roundInfoDict.get(preRound, {})
            batType = roundInfo.get("batType", BatType_Min) # 默认最低级
            updownState = roundInfo.get("updownState", UpdownState_Keep) # 默认保级
            if updownState == UpdownState_Up:
                nextBatType = min(batType + 1, BatType_Max)
            elif updownState == UpdownState_Down:
                nextBatType = max(batType - 1, BatType_Min)
            else:
                nextBatType = batType
            if nextBatType not in nextBatTypeFamilyInfo:
                nextBatTypeFamilyInfo[nextBatType] = []
            batFamilyIDList = nextBatTypeFamilyInfo[nextBatType]
            if familyID not in batFamilyIDList:
                batFamilyIDList.append(familyID)
        GameWorld.Log("上一轮升降级结算汇总: %s" % nextBatTypeFamilyInfo)
    #每轮重新分组时,重置该分区下的轮次伤害相关榜单
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    billboardMgr.ClearBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, groupValue1)
    zone.OnRoundReset()
    zone.OnRoundReset(curRound)
    
    for batType in BatTypeList: # 按战场类型顺序遍历处理
        if batType not in batTypeDict:
        if str(batType) not in batTypeDict:
            # 本轮不需要此战场类型分组
            continue
        groupCnt = batTypeDict[batType][0] # 本轮该战场类型分组
        groupCnt = batTypeDict[str(batType)][0] # 本轮该战场类型分组
        if not groupCnt:
            GameWorld.Log("本轮战场不需要分组: zoneID=%s,curRound=%s,batType=%s" % (zoneID, curRound, batType))
            continue
@@ -1232,16 +1248,16 @@
            if not joinFamily:
                continue
            groupFamilySortList.append(joinFamily)
            groupFamilyDict[familyID] = [joinFamily.fightPowerTotal, joinFamily.score]
            groupFamilyDict[familyID] = [joinFamily.fightPowerTotal, joinFamily.GetTotalScore()]
            
        # 先按总战力倒序,可扩展积分等
        groupFamilySortList.sort(key=lambda f: (f.fightPowerTotal, f.score), reverse=True)
        groupFamilySortList.sort(key=lambda f: (f.fightPowerTotal, f.GetTotalScore()), reverse=True)
        groupFamilyIDSortList = [f.familyID for f in groupFamilySortList]
        GameWorld.Log("开始分组按排序后的顺序循环插入每组! 仙盟数:%s, %s, %s" % (len(groupFamilyIDSortList), groupFamilyIDSortList, groupFamilyDict))
        groupFamilyIDCnt = len(groupFamilyIDSortList)
        GameWorld.Log("开始分组按排序后的顺序循环插入每组! 仙盟数:%s, %s, %s" % (groupFamilyIDCnt, groupFamilyIDSortList, groupFamilyDict))
        
        groupNum = 0
        for familyID in groupFamilyIDSortList:
        for index, familyID in enumerate(groupFamilyIDSortList):
            groupNum += 1
            if groupNum > groupCnt:
                groupNum = 1
@@ -1253,10 +1269,12 @@
            if not joinFamily:
                GameWorld.ErrLog("仙盟分配到战斗分组失败! zoneID=%s,batType=%s,groupNum=%s,familyID=%s" % (zoneID, batType, groupNum, familyID))
                continue
            joinFamily.sortWeight = batType * 10000 + (100 - groupNum) * 100 + (groupFamilyIDCnt - index)
            UndFamilyRoundHurtBillboard(joinFamily)
            GameWorld.Log("    仙盟ID分组: zoneID=%s,curRound=%s,batType=%s,groupNum=%s,familyID=%s,%s" % (zoneID, curRound, batType, groupNum, familyID, groupFamilyDict[familyID]))
            
    Sync_FamilyGCZBatGroupInfo(zoneID)
    RefreshFamilyGCZBillboard(zoneID, True) # 分组完毕后,有新轮次数据了,强制刷新一次榜单,更新新一轮的积分
    return
def UndFamilyRoundHurtBillboard(joinFamily):
@@ -1272,27 +1290,147 @@
    value5 = joinFamily.serverID
    groupValue1 = joinFamily.zoneID
    groupValue2 = GetRoundHurtGroupID(joinFamily.batType, joinFamily.groupNum)
    roundTotalHurt = sum(joinFamily.cityWall.fighterHurtDict.values())
    cmpValue = roundTotalHurt / ChConfig.Def_PerPointValue
    cmpValue2 = roundTotalHurt % ChConfig.Def_PerPointValue
    memHurtTotal = sum(joinFamily.cityWall.fighterHurtDict.values())
    cityRemainHPPer = 0
    if joinFamily.cityWall.hpMax:
        cityRemainHPPer = joinFamily.cityWall.hp / float(joinFamily.cityWall.hpMax)
    familyRoundHurt = eval(IpyGameDataPY.GetFuncCompileCfg("FamilyGCZCity", 3))
    GameWorld.DebugLog("计算仙盟轮次伤害榜值: familyID=%s,familyRoundHurt=%s,memHurtTotal=%s,cityRemainHPPer=%s, %s/%s"
                       % (dataID, familyRoundHurt, memHurtTotal, cityRemainHPPer, joinFamily.cityWall.hp, joinFamily.cityWall.hpMax))
    cmpValue = familyRoundHurt / ChConfig.Def_PerPointValue
    cmpValue2 = familyRoundHurt % ChConfig.Def_PerPointValue
    cmpValue3 = joinFamily.sortWeight
    CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, groupValue1, dataID, name1, name2, type2, value1, value2,
                                     cmpValue, cmpValue2, groupValue2=groupValue2, id2=id2, autoSort=True, value3=value3, value5=value5)
                                     cmpValue, cmpValue2, cmpValue3, groupValue2=groupValue2, id2=id2, autoSort=True, value3=value3, value5=value5)
    return
def DoRoundOver(curRound, zoneID):
    ## 执行轮次结算
def OnMinuteProcess(curMinute):
    ## 每分钟处理
    if not GameWorld.IsCrossServer():
        return
    gczMgr = GetFamilyGCZMgr()
    zoneIDList = gczMgr.zoneDict.keys()
    if not zoneIDList:
        return
    for zoneID in zoneIDList:
        if not zoneID:
            continue
        RefreshFamilyGCZBillboard(zoneID)
    return
def RefreshFamilyGCZBillboard(zoneID, force=False):
    ## 刷新并排序当前活动相关榜单
    # 非强制刷新的,仅战斗阶段定时刷新
    if not force:
        actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
        if not actInfo:
            return
        state = actInfo.get(ShareDefine.ActKey_State)
        if not state:
            return
        curRound, roundState = GetRoundState(state)
        # 仅轮次战斗阶段需要定时刷新
        if not curRound or roundState != FamilyGCZRoundState_Fight:
            return
    gczMgr = GetFamilyGCZMgr()
    zone = gczMgr.GetActZone(zoneID)
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    batTypeScoreInfo = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGroupSet", 2, {})
    GameWorld.Log("========== 仙盟攻城战轮次结算: zoneID=%s,curRound=%s" % (zoneID, curRound))
    if zone.roundOver:
        # 轮次数据已经结算过了,不需要再处理
        return
    roundNum = zone.roundNum
    if not roundNum:
        return
    GameWorld.DebugLog("仙盟攻城战榜单排序! zoneID=%s,roundNum=%s,force=%s" % (zoneID, roundNum, force))
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    playerHurtBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZPlayerHurt, zoneID)
    playerHurtBillObj.SortData()
    batTypeScoreInfo = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGroupSet", 2, {}) # 不同战场类型仙盟排名对应积分 {"战场类型":{"名次":积分, ...}, ...}
    for batType, groupDict in zone.roundGroupDict.items():
        rankScoreInfo = batTypeScoreInfo.get(str(batType), {})
        if not rankScoreInfo:
            GameWorld.ErrLog("仙盟攻城战战场类型没有配置名次积分! batType=%s" % batType)
        rankScoreDict = {int(k):v for k, v in rankScoreInfo.items()}
        groupNumList = groupDict.keys()
        GameWorld.Log("zoneID=%s,batType=%s,groupCnt=%s,rankScoreDict=%s" % (zoneID, batType, len(groupNumList), rankScoreDict))
        for groupNum in groupNumList:
            batGroup = zone.GetBatGroup(batType, groupNum)
            if not batGroup:
                continue
            groupValue2 = GetRoundHurtGroupID(batType, groupNum)
            roundBillboardObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, zoneID, groupValue2)
            roundBillboardObj.SortData()
            for bIndex in range(roundBillboardObj.GetCount()):
                billData = roundBillboardObj.At(bIndex)
                familyID = billData.ID
                rank = bIndex + 1
                score = GameWorld.GetOrderValueByDict(rankScoreDict, rank, False, 0)
                joinFamily = zone.GetZoneJoinFamily(familyID)
                if not joinFamily:
                    continue
                roundInfo = joinFamily.roundInfoDict.get(roundNum, {})
                roundInfo.update({"batType":batType, "groupNum":groupNum, "rank":rank, "score":score})
                joinFamily.roundInfoDict[roundNum] = roundInfo
    lockFamilyIDList = zone.GetZoneLockFamilyIDList()
    for familyID in lockFamilyIDList:
        joinFamily = zone.GetZoneJoinFamily(familyID)
        if not joinFamily:
            continue
        dataID = joinFamily.familyID
        name1 = joinFamily.familyName
        id2 = joinFamily.leaderID
        name2 = joinFamily.leaderName
        type2, value1, value2 = 0, 0, 0
        value3 = joinFamily.emblemID
        value5 = joinFamily.serverID
        cmpValue = joinFamily.GetTotalScore()
        cmpValue2 = joinFamily.sortWeight
        CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID, dataID, name1, name2, type2, value1, value2,
                                         cmpValue, cmpValue2=cmpValue2, id2=id2, autoSort=False, value3=value3, value5=value5, userData=joinFamily.roundInfoDict)
    familyScoreBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
    familyScoreBillObj.SortData()
    return
def DoRoundOver(zoneID):
    ## 执行轮次结算,这里可以不用管是第几轮,对于结算来说只对当前的轮次数据做结算
    gczMgr = GetFamilyGCZMgr()
    zone = gczMgr.GetActZone(zoneID)
    if zone.roundOver:
        # 已经结算过了,不重复结算
        return
    roundNum = zone.roundNum
    if not roundNum:
        # 还没有轮次不处理
        return
    RefreshFamilyGCZBillboard(zoneID, True) # 强制刷新一次榜单
    joinFamilyCnt = zone.joinFamilyCnt
    # 参与仙盟数对应分组及晋级规则 {参与仙盟数:{轮次:{战场类型:[组数, 前x名晋级, 后x名降级], ...}, ...}, ...}
    FamilyGCZGroupSet = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGroupSet", 1, {})
    if str(joinFamilyCnt) not in FamilyGCZGroupSet:
        return
    roundSetDict = FamilyGCZGroupSet[str(joinFamilyCnt)]
    if str(roundNum) not in roundSetDict:
        return
    batTypeDict = roundSetDict[str(roundNum)]
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    GameWorld.Log("========== 仙盟攻城战轮次结算: zoneID=%s,roundNum=%s" % (zoneID, roundNum))
    for batType, groupDict in zone.roundGroupDict.items():
        if str(batType) not in batTypeDict:
            continue
        batTypeSet = batTypeDict[str(batType)]
        upRank = batTypeSet[1] # 前x晋级
        downRank = batTypeSet[2] if len(batTypeSet) > 2 else 0 # 后x名降级,为0时不降级
        groupNumList = groupDict.keys()
        GameWorld.Log("zoneID=%s,batType=%s,upRank=%s,downRank=%s,groupNumList=%s" % (zoneID, batType, upRank, downRank, groupNumList))
        for groupNum in groupNumList:
            batGroup = zone.GetBatGroup(batType, groupNum)
            if not batGroup:
@@ -1300,41 +1438,32 @@
            
            groupValue2 = GetRoundHurtGroupID(batType, groupNum)
            billboardObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, zoneID, groupValue2)
            billboardObj.SortData()
            GameWorld.Log("zoneID=%s,batType=%s,groupNum=%s,roundHurtFamilyCnt=%s" % (zoneID, batType, groupNum, billboardObj.GetCount()))
            GameWorld.Log("zoneID=%s,batType=%s,groupNum=%s,groupFamilyCnt=%s" % (zoneID, batType, groupNum, billboardObj.GetCount()))
            for bIndex in range(billboardObj.GetCount()):
                billData = billboardObj.At(bIndex)
                familyID = billData.ID
                hurtValue = billData.CmpValue
                hurtValueEx = billData.CmpValue2
                hurtValueTotal = hurtValue * ChConfig.Def_PerPointValue + hurtValueEx
                cmpValue = billData.CmpValue
                cmpValue2 = billData.CmpValue2
                hurtValueTotal = cmpValue * ChConfig.Def_PerPointValue + cmpValue2
                rank = bIndex + 1
                addScore = GameWorld.GetOrderValueByDict(rankScoreDict, rank, False, 0)
                updownState = UpdownState_Keep # 本轮结算晋级降级状态:0-未处理,1-降级;2-保级;3-晋级,即本轮的晋降级状态决定下一轮的战场类型
                if rank <= upRank and batType < BatType_Max:
                    updownState = UpdownState_Up
                elif downRank and rank > (Def_PerGroupFamilyCnt - downRank) and batType > BatType_Min:
                    updownState = UpdownState_Down
                joinFamily = zone.GetZoneJoinFamily(familyID)
                if not joinFamily:
                    continue
                joinFamily.score += addScore
                GameWorld.Log("    zoneID=%s,batType=%s,groupNum=%s,rank=%s,familyID=%s,addScore=%s,updScore=%s,hurtValueTotal=%s"
                              % (zoneID, batType, groupNum, rank, familyID, addScore, joinFamily.score, hurtValueTotal))
                roundInfo = joinFamily.roundInfoDict.get(roundNum, {})
                roundInfo.update({"updownState":updownState})
                joinFamily.roundInfoDict[roundNum] = roundInfo
                
                dataID = joinFamily.familyID
                name1 = joinFamily.familyName
                id2 = joinFamily.leaderID
                name2 = joinFamily.leaderName
                type2, value1, value2 = 0, 0, 0
                value3 = joinFamily.emblemID
                value5 = joinFamily.serverID
                cmpValue = joinFamily.score
                CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID, dataID, name1, name2, type2, value1, value2,
                                                 cmpValue, id2=id2, autoSort=False, value3=value3, value5=value5)
                GameWorld.Log("    升保降级zoneID=%s,batType=%s,groupNum=%s,rank=%s,familyID=%s,updownState=%s,hurtValueTotal=%s"
                              % (zoneID, batType, groupNum, rank, familyID, updownState, hurtValueTotal))
                Sync_FamilyGCZCampInfo(zoneID, familyID)
                
    # 都加完积分后统一排序
    familyScoreBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
    familyScoreBillObj.SortData()
    playerHurtBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZPlayerHurt, zoneID)
    playerHurtBillObj.SortData()
    zone.roundOver = 1 # 标记已结算
    return
def DoGenerateGuess(zoneID, state):
@@ -1348,7 +1477,8 @@
    guessRankMax = IpyGameDataPY.GetFuncCfg("FamilyGCZGuess", 2)
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    scoreBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
    for index in range(guessRankMax):
    guessCnt = min(scoreBillObj.GetCount(), guessRankMax)
    for index in range(guessCnt):
        billData = scoreBillObj.At(index)
        familyID = billData.ID
        if familyID not in zone.familyGuessDict:
@@ -1360,6 +1490,8 @@
def DoStartAward(zoneID):
    ## 开始领奖
    DoRoundOver(zoneID)
    
    # 下发竞猜统计结果
    Sync_FamilyGCZGuessInfo(zoneID)
@@ -1375,16 +1507,6 @@
    msgInfo["ActMsgType"] = actMsgType
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_FamilyGCZ, msgInfo, serverGroupIDList)
    return
#def GetMaxRound(joinFamilyCnt):
#    ## 获取最大轮次
#    FamilyGCZGroupSet = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGroupSet", 1, {})
#    if joinFamilyCnt not in FamilyGCZGroupSet:
#        return 0
#    roundSetDict = FamilyGCZGroupSet[joinFamilyCnt]
#    if not roundSetDict:
#        return 0
#    return max(roundSetDict)
def ClientServerMsg_FamilyGCZ(serverGroupID, msgData):
    ActMsgType = msgData["ActMsgType"]
@@ -1465,14 +1587,14 @@
    gczMgr = GetFamilyGCZMgr()
    joinMember = gczMgr.GetJoinMember(playerID)
    if not joinMember:
        GameWorld.ErrLog("非仙盟攻城战参赛成员!", playerID)
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "非仙盟攻城战参赛成员:%s" % playerID)
        return
    zoneID = joinMember.zoneID
    familyID = joinMember.familyID
    zone = gczMgr.GetActZone(zoneID)
    joinFamily = zone.GetZoneJoinFamily(familyID)
    if not joinFamily:
        GameWorld.ErrLog("非仙盟攻城战参赛仙盟! familyID=%s" % familyID, playerID)
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "非仙盟攻城战参赛仙盟:%s" % familyID)
        return
    batType, groupNum = joinMember.batType, joinMember.groupNum
    batGroup = zone.GetBatGroup(batType, groupNum)
@@ -1489,7 +1611,7 @@
    curRound, roundState = GetRoundState(state)
    
    value0 = msgList[0]
    if value0 in ["a", "h", "k"]:
    if value0 in ["a", "h", "k", "s"]:
        if not curRound or (curRound and roundState != FamilyGCZRoundState_Fight):
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "非战斗阶段!state=%s" % (state))
            return
@@ -1515,7 +1637,7 @@
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "目标城池不存在:%s" % (tagCityID))
            return
        atkOK = __DoGMAtkCity(playerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, atkCntTotal, 0, atkMemberID)
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "atkOK:%s,remainHP:%s" % (atkOK, tagCity.hp))
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "atkOK:%s,remainHP:%s" % (atkOK != None, tagCity.hp))
        
    #攻击城池: FamilyGCZ a 城池ID 剩余血量 [攻击方人数 仙盟ID]
    elif value0 == "h":
@@ -1543,8 +1665,68 @@
            return
        hurtValueTotal = tagCity.hp - remainHP
        atkOK = __DoGMAtkCity(playerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, 0, hurtValueTotal, atkMemberID)
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "atkOK:%s,remainHP:%s" % (atkOK, tagCity.hp))
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "atkOK:%s,remainHP:%s" % (atkOK != None, tagCity.hp))
    #技能攻击: FamilyGCZ s 技能类型 [城池ID 攻击方ID]
    elif value0 == "s":
        atkType = msgList[1] if len(msgList) > 1 else 0
        tagCityID = msgList[2] if len(msgList) > 2 else 0
        atkMemberID = msgList[3] if len(msgList) > 3 else playerID
        hurtMulti = msgList[4] if len(msgList) > 4 else 2
        hurtFamilyCnt = msgList[5] if len(msgList) > 5 else 10
        atkJoinMember = zone.GetZoneJoinMember(atkMemberID)
        if not atkJoinMember:
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "攻击方不存在:%s" % atkMemberID)
            return
        atkFamilyID = atkJoinMember.familyID
        atkJoinFamily = zone.GetZoneJoinFamily(atkFamilyID)
        curCityID = atkJoinFamily.cityWall.cityID
        batFightPower = atkJoinMember.fightPower
        hurtValueTotal = batFightPower * hurtMulti # 总伤害
        canAtkedCityList = []
        # 技能单体
        if atkType == AtkType_SkillSingle:
            tagCity = batGroup.GetCityWall(tagCityID)
            if not tagCity:
                GameWorld.DebugAnswerCross(playerID, serverGroupID, "目标城池不存在:%s" % (tagCityID))
                return
            if atkFamilyID == tagCity.familyID:
                GameWorld.DebugAnswerCross(playerID, serverGroupID, "不能攻击自己仙盟")
                return
            canAtkedCityList.append(tagCity)
        # 技能群体
        elif atkType == AtkType_SkillArea:
            for tagCityID in batGroup.cityWallDict.keys(): # 遍历同组城池
                if tagCityID == curCityID:
                    #不打自己
                    continue
                tagCity = batGroup.GetCityWall(tagCityID)
                if not tagCity or tagCity.IsBroken():
                    # 已被摧毁的
                    continue
                canAtkedCityList.append(tagCity)
            if len(canAtkedCityList) > hurtFamilyCnt:
                random.shuffle(canAtkedCityList)
                canAtkedCityList = canAtkedCityList[:hurtFamilyCnt]
        else:
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "非技能攻击类型(%s)" % atkType)
            return
        oneHurtTotal = hurtValueTotal / len(canAtkedCityList) # 所有可攻击的目标仙盟平摊伤害
        hurtDict = {}
        killCntTotal = 0
        for tagCity in canAtkedCityList:
            tagCityID = tagCity.cityID
            atkOK = __DoGMAtkCity(playerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, 0, oneHurtTotal, atkMemberID, atkType)
            if atkOK != None:
                hurtDict[tagCityID] = oneHurtTotal
                killCntTotal += atkOK
        Sync_FamilyGCZBatSceneInfo(zoneID, batType, groupNum, atkMemberID, atkType, hurtDict, killCntTotal)
    #击毁城池: FamilyGCZ k [城池ID 攻击方仙盟ID]    
    elif value0 == "k":
        tagCityID = msgList[1] if len(msgList) > 1 else 0
@@ -1574,7 +1756,7 @@
        tagCityID = tagCity.cityID
        hurtValueTotal = tagCity.hp
        atkOK = __DoGMAtkCity(playerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, 0, hurtValueTotal)
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "killOK:%s,目标:%s,攻击方:%s" % (atkOK, tagCityID, curCityID))
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "killOK:%s,目标:%s,攻击方:%s" % (atkOK != None, tagCityID, curCityID))
        
    #输出城池: FamilyGCZ p [战场类型 分组编号]
    elif value0 == "p":
@@ -1635,7 +1817,7 @@
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "    HP: %s/%s %s" % (city.hp, city.hpMax, broken))
    return
def __DoGMAtkCity(gmPlayerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, atkCntTotal=0, hurtValueTotal=0, atkMemberID=0):
def __DoGMAtkCity(gmPlayerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, atkCntTotal=0, hurtValueTotal=0, atkMemberID=0, atkType=AtkType_Normal):
    '''GM攻击城池
    @param curCityID: 攻击方城池ID
    @param tagCityID: 防守方城池ID
@@ -1743,7 +1925,11 @@
                           % (remainHurtTotal, killCnt, curCity.atkReportDict, tagCity.defReportDict))
        
    GameWorld.DebugLog("cityHP=%s/%s,atkedGuardIDList=%s" % (tagCity.hp, tagCity.hpMax, atkedGuardIDList))
    if tagFamilyID:
        tagJoinFamily = zone.GetZoneJoinFamily(tagFamilyID)
        if tagJoinFamily:
            UndFamilyRoundHurtBillboard(tagJoinFamily) # 榜单伤害受城池血量影响,所以被攻击方也要更新榜单
    for memID, memHurt in memHurtDict.items():
        GameWorld.DebugLog("攻击方成员输出: memID=%s,%s" % (memID, memHurt))
        joinMember = zone.GetZoneJoinMember(memID)
@@ -1763,13 +1949,17 @@
            
    if not curCity.IsBroken():
        UndFamilyRoundHurtBillboard(curJoinFamily)
    else:
        GameWorld.DebugLog("    攻击方仙盟城池已被摧毁,不再累加本轮仙盟伤害榜值! familyID=%s" % curFamilyID)
        
    if not CheckXiuluoCity(zoneID, curRound, batType, groupNum, gmPlayerID):
        Sync_FamilyGCZBatSceneInfo(zoneID, batType, groupNum)
        if atkType == AtkType_Normal: # 技能攻击外层统一通知
            Sync_FamilyGCZBatSceneInfo(zoneID, batType, groupNum)
    Sync_FamilyGCZBatCityInfo(zoneID, batType, groupNum, tagCityID)
    if tagFamilyID:
        Sync_FamilyGCZCampInfo(zoneID, tagFamilyID, defMemIDList=atkedGuardIDList)
    return True
    return killCnt
def __clientServer_Atk(serverGroupID, msgData):
    ## 攻击
@@ -1940,6 +2130,9 @@
        hurtDict[tagCityID] = oneHurtTotal # 飘血按伤害
        # 通知被攻击盟成员大本营城池信息
        if tagCity.familyID:
            tagJoinFamily = zone.GetZoneJoinFamily(tagCity.familyID)
            if tagJoinFamily:
                UndFamilyRoundHurtBillboard(tagJoinFamily) # 榜单伤害受城池血量影响,所以被攻击方也要更新榜单
            Sync_FamilyGCZCampInfo(zoneID, tagCity.familyID, defMemIDList=atkedGuardIDList)
        Sync_FamilyGCZBatCityInfo(zoneID, batType, groupNum, tagCityID, AtkRet_OK, playerID, oneHurtTotal, killCnt)
        
@@ -2134,8 +2327,10 @@
        scenePlayer = ChPyNetSendPack.tagGCFamilyGCZBatScenePlayer()
        scenePlayer.PlayerID = playerID
        scenePlayer.Name = joinMember.playerName
        scenePlayer.Job = joinMember.job
        scenePlayer.Face = joinMember.face
        scenePlayer.FacePic = joinMember.facePic
        scenePlayer.FamilyID = joinMember.familyID
        clientPack.PlayerInfoList.append(scenePlayer)
    clientPack.PlayerCnt = len(clientPack.PlayerInfoList)
    
@@ -2171,15 +2366,23 @@
    if not onlinePlayerIDDict:
        return
    
    guradID = cityWall.GetCurGuardID()
    if cityWall.IsGuardNPC(guradID):
        curGuard = cityWall.guardNPC
    else:
        curGuard = zone.GetZoneJoinMember(guradID)
    guardHP, guradHPMax = 0, 0
    if curGuard:
        guardHP, guradHPMax = curGuard.hp, curGuard.hpMax
    clientPack = ChPyNetSendPack.tagGCFamilyGCZBatCityInfo()
    clientPack.CityID = cityID
    clientPack.CityLV = cityWall.cityLV
    clientPack.FamilyID = cityWall.familyID
    clientPack.GuardID = cityWall.GetCurGuardID()
    clientPack.HP = cityWall.hp % ChConfig.Def_PerPointValue
    clientPack.HPEx = cityWall.hp / ChConfig.Def_PerPointValue
    clientPack.HPMax = cityWall.hpMax % ChConfig.Def_PerPointValue
    clientPack.HPMaxEx = cityWall.hpMax / ChConfig.Def_PerPointValue
    clientPack.GuardID = guradID
    clientPack.HP = guardHP % ChConfig.Def_PerPointValue
    clientPack.HPEx = guardHP / ChConfig.Def_PerPointValue
    clientPack.HPMax = guradHPMax % ChConfig.Def_PerPointValue
    clientPack.HPMaxEx = guradHPMax / ChConfig.Def_PerPointValue
    clientPack.AtkPlayerID = atkPlayerID
    clientPack.AtkRet = atkRet
    clientPack.KillCnt = killCnt
@@ -2355,7 +2558,17 @@
    # 竞猜
    if awardType == AwwardType_Guess:
        awardTemplateID = ipyDataInfo.get("GuessTemplateID", 0)
        __clientServer_GetAward_Guess(zoneID, playerID, serverGroupID, actID, state, awardType, awardTemplateID)
        playerAwardID, awardItemList = GetGuessAwardInfo(zoneID, state, awardTemplateID, playerID)
        if not awardItemList:
            GameWorld.ErrLog("仙盟攻城战玩家没有竞猜奖励! zoneID=%s" % (zoneID), playerID)
            return
        __clientServer_GetAward_Ret(zoneID, playerID, serverGroupID, actID, awardType, playerAwardID, awardItemList)
        return
    # 点赞奖励
    if awardType == AwwardType_Like:
        awardItemList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGuess", 4)
        __clientServer_GetAward_Ret(zoneID, playerID, serverGroupID, actID, awardType, 0, awardItemList)
        return
    
    gczMgr = GetFamilyGCZMgr()
@@ -2406,30 +2619,25 @@
    if not playerAwardItemList:
        return
    
    GameWorld.Log("仙盟攻城战玩家请求领取奖励! zoneID=%s,awardType=%s,playerAwardItemList=%s"
                  % (zoneID, awardType, playerAwardItemList), playerID)
    __clientServer_GetAward_Ret(zoneID, playerID, serverGroupID, actID, awardType, billRank, playerAwardItemList)
    
    joinMember.awardState = joinMember.awardState | pow(2, awardType) # 标记已推送
    msgInfo = {"zoneID":zoneID, "playerID":playerID, "actID":actID, "awardType":awardType, "awardValue":billRank,
               "awardItemList":playerAwardItemList, "fmLV":fmLV}
    Send_CrossServerMsg_FamilyGCZ("FamilyGCZAward", msgInfo, [serverGroupID])
    serverIDList = actInfo.get(ShareDefine.ActKey_ServerIDRangeList, [])
    GameXiangong.AddXiangongPlayerByItem(playerAwardItemList, playerID, serverIDList, billRank)
    return
def __clientServer_GetAward_Guess(zoneID, playerID, serverGroupID, actID, state, awardType, guessTemplateID):
    ## 竞猜领奖
    playerAwardID, awardItemList = GetGuessAwardInfo(zoneID, state, guessTemplateID, playerID)
    if not awardItemList:
        GameWorld.ErrLog("仙盟攻城战玩家没有竞猜奖励! zoneID=%s" % (zoneID), playerID)
        return
    GameWorld.Log("仙盟攻城战玩家请求领取竞猜奖励! zoneID=%s,playerAwardID=%s,awardItemList=%s"
                  % (zoneID, playerAwardID, awardItemList), playerID)
def __clientServer_GetAward_Ret(zoneID, playerID, serverGroupID, actID, awardType, awardValue, awardItemList):
    ## 同步子服领奖结果
    gczMgr = GetFamilyGCZMgr()
    zone = gczMgr.GetActZone(zoneID)
    joinMember = zone.GetZoneJoinMember(playerID)
    fmLV = 0
    if joinMember:
        fmLV = joinMember.fmLV
        joinMember.awardState = joinMember.awardState | pow(2, awardType) # 标记已推送
    msgInfo = {"zoneID":zoneID, "playerID":playerID, "actID":actID, "awardType":awardType, "awardValue":playerAwardID, "awardItemList":awardItemList}
    GameWorld.Log("仙盟攻城战玩家请求领取奖励! zoneID=%s,awardType=%s,awardValue=%s,awardItemList=%s"
                  % (zoneID, awardType, awardValue, awardItemList), playerID)
    msgInfo = {"zoneID":zoneID, "playerID":playerID, "actID":actID, "awardType":awardType, "awardValue":awardValue,
               "awardItemList":awardItemList, "fmLV":fmLV}
    Send_CrossServerMsg_FamilyGCZ("FamilyGCZAward", msgInfo, [serverGroupID])
    return
@@ -2486,6 +2694,9 @@
                paramList = [billRank]
                PlayerCompensation.SendMailByKey("FamilyGCZAwardHurt", [playerID], playerAwardItemList, paramList, crossMail=True)
                
                serverIDList = ipyData.GetServerIDRangeList()
                GameXiangong.AddXiangongPlayerByItem(playerAwardItemList, playerID, serverIDList, billRank)
        # 仙盟积分名次奖励
        if not joinMember.awardState&pow(2, AwwardType_FamilyScore):
            joinMember.awardState = joinMember.awardState | pow(2, AwwardType_FamilyScore)
@@ -2498,6 +2709,9 @@
            if playerAwardItemList:
                paramList = [billRank]
                PlayerCompensation.SendMailByKey("FamilyGCZAwardScore", [playerID], playerAwardItemList, paramList, crossMail=True)
                serverIDList = ipyData.GetServerIDRangeList()
                GameXiangong.AddXiangongPlayerByItem(playerAwardItemList, playerID, serverIDList, billRank)
                
        # 竞猜奖励
        if not joinMember.awardState&pow(2, AwwardType_Guess):
@@ -2595,9 +2809,11 @@
    guessCountMax = IpyGameDataPY.GetFuncCfg("FamilyGCZGuess", 3)
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    scoreBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
    for index in range(guessCountMax):
    for index in range(scoreBillObj.GetCount()):
        billData = scoreBillObj.At(index)
        finalFamilyIDList.append(billData.ID)
        if len(finalFamilyIDList) >= guessCountMax:
            break
        
    # 统计竞猜结果
    rightRankAwardIDDict = GameWorld.GetActGuessRightRankAwardIDDict(guessTemplateID)
@@ -2712,7 +2928,7 @@
        return
    
    clientPack = ChPyNetSendPack.tagGCFamilyGCZBatGroupInfo()
    clientPack.RoundNum = curRound
    clientPack.RoundNum = zone.roundNum
    clientPack.BatList = []
    for batType, groupNumDict in zone.roundGroupDict.items():
        batInfo = ChPyNetSendPack.tagGCFamilyGCZBat()
@@ -2753,7 +2969,6 @@
    clientPack = ChPyNetSendPack.tagGCFamilyGCZCampInfo()
    clientPack.Clear()
    clientPack.FamilyID = familyID
    clientPack.Score = joinFamily.score
    clientPack.CampLV = joinFamily.campLV
    clientPack.CampExp = joinFamily.campExp
    clientPack.CityLV = joinFamily.cityWall.cityLV
@@ -2763,6 +2978,17 @@
    clientPack.HPMaxEx = joinFamily.cityWall.hpMax / ChConfig.Def_PerPointValue
    clientPack.HP = joinFamily.cityWall.hp % ChConfig.Def_PerPointValue
    clientPack.HPEx = joinFamily.cityWall.hp / ChConfig.Def_PerPointValue
    clientPack.RoundInfoList = []
    for roundNum, info in joinFamily.roundInfoDict.items():
        roundObj = ChPyNetSendPack.tagGCFamilyGCZCampRound()
        roundObj.RoundNum = roundNum
        roundObj.BatType = info.get("batType", 0)
        roundObj.GroupNum = info.get("groupNum", 0)
        roundObj.Rank = info.get("rank", 0)
        roundObj.Score = info.get("score", 0)
        roundObj.UpdownState = info.get("updownState", 0)
        clientPack.RoundInfoList.append(roundObj)
    clientPack.RoundCnt = len(clientPack.RoundInfoList)
    clientPack.DefMemList = []
    for playerID in joinFamily.joinMemberIDList:                
        if defMemIDList != None and playerID not in defMemIDList:
@@ -2775,6 +3001,7 @@
        defMem.PlayerID = playerID
        defMem.Name = joinMember.playerName
        defMem.FamilyLV = joinMember.fmLV
        defMem.Job = joinMember.job
        defMem.Face = joinMember.face
        defMem.FacePic = joinMember.facePic
        defMem.FightPower = joinMember.fightPower % ChConfig.Def_PerPointValue
@@ -2894,7 +3121,7 @@
    for playerID, pActInfo in playerActInfo.items():
        if not PlayerControl.GetDBPlayerAccIDByID(playerID):
            # 非本服玩家
            return
            continue
        curPlayer = playerManager.FindPlayerByID(playerID)
        if curPlayer:
            MapServer_QueryPlayerResult(curPlayer, "FamilyGCZPlayerActInfo", pActInfo)