hxp
2023-04-25 ae8371301b81bfae8de95d2ecbe52a50df8c7f06
9811 【BT9】【后端】逐鹿万界
13个文件已修改
2个文件已添加
1332 ■■■■■ 已修改文件
PySysDB/PySysDBG.h 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PySysDB/PySysDBPY.h 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossActionControl.py 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossFamilyFlagwar.py 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerEventCounter.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossFamilyFlagwar.py 969 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AICommon.py 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PySysDB/PySysDBG.h
@@ -925,6 +925,16 @@
    BYTE        _CopyMapID;    //虚拟线路ID
};
//跨服分区地图逐鹿万界
struct tagCrossFamilyFlagwarZoneMap
{
    BYTE        ZoneID;    //分区ID
    DWORD        _MapID;    //场景地图ID
    DWORD        _DataMapID;    //数据地图ID
    BYTE        _CopyMapID;    //虚拟线路ID
};
//周狂欢活动时间表
struct tagActWeekParty
PySysDB/PySysDBPY.h
@@ -2250,6 +2250,18 @@
    WORD        PosY;    //坐标Y
};
//跨服分区地图逐鹿万界
struct tagCrossFamilyFlagwarZoneMap
{
    BYTE        ZoneID;    //分区ID
    DWORD        _MapID;    //场景地图ID
    DWORD        _DataMapID;    //数据地图ID
    BYTE        _CopyMapID;    //虚拟线路ID
    WORD        PosX;    //坐标X
    WORD        PosY;    //坐标Y
};
//聚魂表
struct tagGatherSoul
ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
@@ -707,6 +707,8 @@
Def_FBMapID_CrossGrasslandXian = 32050
#跨服战场
Def_FBMapID_CrossBattlefield = 32060
#跨服仙盟夺旗战/逐鹿万界
Def_FBMapID_CrossFamilyFlagwar = 32090
#情缘副本
Def_FBMapID_Love = 31300
@@ -728,7 +730,14 @@
#跨服分区对应地图配置表名 - 仅适用于固定地图及虚拟分线的跨服玩法
Def_CrossZoneMapTableName = {Def_FBMapID_CrossPenglai:"CrossPenglaiZoneMap",
                             Def_FBMapID_CrossDemonLand:"CrossDemonLandZoneMap",
                             Def_FBMapID_CrossFamilyFlagwar:"CrossFamilyFlagwarZoneMap",
                             }
#跨服日常活动对应需要直接开启副本线路地图的,仅适用于固定分区的日常,不包含动态分配线路及有特定分配规则的地图
Def_CrossDailyMap = {
                     ShareDefine.CrossDailyActionID_FamilyWarFlag:Def_FBMapID_CrossFamilyFlagwar,
                     }
#需要动态分配线路的跨服地图
Def_CrossDynamicLineMap = [Def_FBMapID_CrossDemonKing, Def_FBMapID_CrossGrasslandLing, Def_FBMapID_CrossGrasslandXian]
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossActionControl.py
@@ -24,6 +24,7 @@
import CrossActCTGBillboard
import CrossRealmMsg
import PyGameData
import PlayerFB
import ChConfig
import datetime
@@ -838,6 +839,10 @@
            continue
        
        if state:
            if dailyActionID in ChConfig.Def_CrossDailyMap:
                # 开启对应日常地图分区线路
                __openCrossDailyMap(ChConfig.Def_CrossDailyMap[dailyActionID])
            if dailyActionID == ShareDefine.CrossDailyActionID_YaomoBoss:
                GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_ActionBossRebornSign % dailyActionID, int(time.time()))
                
@@ -855,6 +860,26 @@
        
    return
def __openCrossDailyMap(mapID):
    if mapID not in ChConfig.Def_CrossZoneMapTableName:
        return
    zoneTypeName = ChConfig.Def_CrossZoneMapTableName[mapID]
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    if zoneTypeName and hasattr(ipyDataMgr, "Get%sCount" % zoneTypeName):
        realMapInfo = {}
        for index in range(getattr(ipyDataMgr, "Get%sCount" % zoneTypeName)()):
            ipyData = getattr(ipyDataMgr, "Get%sByIndex" % zoneTypeName)(index)
            zoneID = ipyData.GetZoneID()
            realMapID = ipyData.GetMapID()
            copyMapID = ipyData.GetCopyMapID()
            if realMapID not in realMapInfo:
                realMapInfo[realMapID] = []
            copyPropertyList = realMapInfo[realMapID]
            copyPropertyList.append([copyMapID, zoneID])
        for realMapID, copyPropertyList in realMapInfo.items():
            PlayerFB.SendMapOpenFBEx(realMapID, copyPropertyList)
    return
def SendMapServerCrossDailyActionState():
    # 地图启动成功时通知本日进行中的日常活动状态
    
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossFamilyFlagwar.py
New file
@@ -0,0 +1,207 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package CrossFamilyFlagwar
#
# @todo:跨服仙盟夺旗战/逐鹿万界
# @author hxp
# @date 2023-04-25
# @version 1.0
#
# 详细描述: 跨服仙盟夺旗战
#
#-------------------------------------------------------------------------------
#"""Version = 2023-04-25 15:30"""
#-------------------------------------------------------------------------------
import GameWorld
import CrossRealmMsg
import IpyGameDataPY
import CrossBillboard
import PlayerCompensation
import CrossRealmPlayer
import PlayerControl
import PyDataManager
import ShareDefine
import ChConfig
def DoOnWeekEx():
    if not GameWorld.IsCrossServer():
        return
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    groupList = billboardMgr.GetBillboardGroupList(ShareDefine.Def_CBT_FamilyFlagwarWeek)
    for billboardType, groupValue1, groupValue2 in groupList:
        billboardObj = billboardMgr.GetCrossBillboard(billboardType, groupValue1, groupValue2)
        zoneID = groupValue1
        zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByZoneID(ChConfig.Def_FBMapID_CrossFamilyFlagwar, zoneID)
        serverGroupIDList = zoneIpyData.GetServerGroupIDList() if zoneIpyData else []
        syncFamilyList = []
        for index in range(billboardObj.GetCount()):
            billboardData = billboardObj.At(index)
            familyID = billboardData.ID
            familyRank = index + 1
            syncFamilyList.append([familyRank, familyID])
        billboardObj.ClearData()
        # 通知子服
        sendMsg = {"zoneID":zoneID, "syncFamilyList":syncFamilyList, "isWeek":True}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_FamilyFlagwarOver, sendMsg, serverGroupIDList)
    return
def MapServer_CrossFamilyFlagwarOver(msgList):
    ## 地图结算
    zoneID, battleFamilyList = msgList
    GameWorld.Log("跨服仙盟夺旗战/逐鹿万界地图同步结果: zoneID=%s" % zoneID, zoneID)
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    groupValue1 = zoneID
    billboardObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyFlagwar, groupValue1)
    billboardObj.ClearData() # 单场榜每次更新前重置
    billboardObjWeek = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyFlagwarWeek, groupValue1)
    syncFamilyList = []
    championFamilyName = ""
    for familyInfo in battleFamilyList:
        familyRank, familyID, familyName, familyScore, battlePlayerList = familyInfo
        if familyRank == 1:
            championFamilyName = familyName
        billboardDataWeek = billboardObjWeek.FindByID(familyID)
        weekScore = billboardDataWeek.CmpValue if billboardDataWeek else 0
        weekScore += familyScore
        dataID, name1, name2 = familyID, familyName, ""
        type2, value1, value2 = 0, 0, 0
        # 周总榜
        cmpValue = weekScore
        CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_FamilyFlagwarWeek, groupValue1, dataID, name1, name2, type2, value1, value2, cmpValue)
        # 单场榜
        cmpValue = familyScore
        CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_FamilyFlagwar, groupValue1, dataID, name1, name2, type2, value1, value2, cmpValue)
        syncFamilyList.append([familyRank, familyID, familyScore, battlePlayerList])
    billboardObj.SortData()
    billboardObjWeek.SortData()
    zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByZoneID(ChConfig.Def_FBMapID_CrossFamilyFlagwar, zoneID)
    serverGroupIDList = zoneIpyData.GetServerGroupIDList() if zoneIpyData else []
    # 通知子服
    sendMsg = {"zoneID":zoneID, "syncFamilyList":syncFamilyList}
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_FamilyFlagwarOver, sendMsg, serverGroupIDList)
    # 结算广播
    if championFamilyName:
        PlayerControl.WorldNotifyCross(serverGroupIDList, 0, "CrossFamilyFlagwarFlagOver", [championFamilyName])
    return
def CrossServerMsg_FamilyFlagwarOver(msgData):
    ## 子服收到 结算结果同步
    if "isWeek" in msgData:
        __doFamilyFlagwarOverWeek(msgData)
        return
    #zoneID = msgData["zoneID"]
    syncFamilyList = msgData["syncFamilyList"]
    playerScoreAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarAward", 1, {}) # 单场个人累计积分对应奖励物品列表 {"积分":[[物品ID,个数,是否拍品], ...], ...}
    playerScoreIntAwardDict = {int(k):v for k, v in playerScoreAwardDict.items()}
    playerHurtAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarAward", 2, {}) # 单场个人累计伤害对应奖励物品列表 {"伤害":[[物品ID,个数,是否拍品], ...], ...}
    playerHurtIntAwardDict = {int(k):v for k, v in playerHurtAwardDict.items()}
    # 单场仙盟积分排名奖励列表 {"名次":[[[参与奖物品ID,个数,是否拍品], ...], [仙盟奖物品ID,个数,是否拍品], ...]], ...}
    # 未参与的玩家只有仙盟奖励,没有参与奖
    familyRankAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarAward", 3, {})
    familyRankIntAwardDict = {int(k):v for k, v in familyRankAwardDict.items()}
    for familyInfo in syncFamilyList:
        familyRank, familyID, familyScore, battlePlayerList = familyInfo
        family = GameWorld.GetFamilyManager().FindFamily(familyID)
        if not family:
            ## 找不到的不处理,可能不是本服的仙盟
            continue
        # 所有成员
        memPlayerIDList = []
        for i in range(family.GetCount()):
            member = family.GetAt(i)
            memPlayerIDList.append(member.GetPlayerID())
        joinPlayerIDList = []
        for playerInfo in battlePlayerList:
            playerID, score, hurtTotal = playerInfo
            joinPlayerIDList.append(playerID)
            # 个人累计积分奖励
            for scoreNeed, itemList in playerScoreIntAwardDict.items():
                if score < scoreNeed:
                    continue
                PlayerCompensation.SendMailByKey("CrossFamilyFlagwarPlayerScore", [playerID], itemList, [scoreNeed])
            # 个人累计伤害奖励
            for hurtNeed, itemList in playerHurtIntAwardDict.items():
                if hurtTotal < hurtNeed:
                    continue
                PlayerCompensation.SendMailByKey("CrossFamilyFlagwarPlayerHurt", [playerID], itemList, [hurtNeed])
        if familyScore:
            pass
        rankAwardList = GameWorld.GetOrderValueByDict(familyRankIntAwardDict, familyRank, False)
        if rankAwardList and len(rankAwardList) == 2:
            # 参与奖励
            addItemList = rankAwardList[0]
            paramList = [familyRank]
            PlayerCompensation.SendMailByKey("CrossFamilyFlagwarRankJoin", joinPlayerIDList, addItemList, paramList)
            # 全员奖励
            addItemList = rankAwardList[1]
            paramList = [familyRank]
            PlayerCompensation.SendMailByKey("CrossFamilyFlagwarRank", memPlayerIDList, addItemList, paramList)
        else:
            GameWorld.ErrLog("逐鹿万界找不到仙盟对应名次奖励! familyRank=%s,familyID=%s" % (familyRank, familyID))
    return
def __doFamilyFlagwarOverWeek(msgData):
    syncFamilyList = msgData["syncFamilyList"]
    weekRankAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarAward", 4, {}) # 每周仙盟累计积分排名奖励列表 {"名次":[[物品ID,个数,是否拍品], ...], ...}
    weekRankIntAwardDict = {int(k):v for k, v in weekRankAwardDict.items()}
    for familyInfo in syncFamilyList:
        familyRank, familyID = familyInfo
        family = GameWorld.GetFamilyManager().FindFamily(familyID)
        if not family:
            ## 找不到的不处理,可能不是本服的仙盟
            continue
        # 所有成员
        memPlayerIDList = []
        for i in range(family.GetCount()):
            member = family.GetAt(i)
            memPlayerIDList.append(member.GetPlayerID())
        awardItemList = GameWorld.GetOrderValueByDict(weekRankIntAwardDict, familyRank, False)
        if awardItemList:
            paramList = [familyRank]
            PlayerCompensation.SendMailByKey("CrossFamilyFlagwarRankWeek", memPlayerIDList, awardItemList, paramList)
    return
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
@@ -25,6 +25,7 @@
import PlayerCompensation
import CrossActionControl
import CrossActAllRecharge
import CrossFamilyFlagwar
import CrossChampionship
import CrossBattlefield
import CrossBillboard
@@ -351,6 +352,9 @@
        elif msgType == ShareDefine.CrossServerMsg_BattlefieldOver:
            CrossBattlefield.CrossServerMsg_BattlefieldOver(msgData)
            
        elif msgType == ShareDefine.CrossServerMsg_FamilyFlagwarOver:
            CrossFamilyFlagwar.CrossServerMsg_FamilyFlagwarOver(msgData)
        elif msgType == ShareDefine.CrossServerMsg_ChampionshipState:
            CrossChampionship.CrossServerMsg_ChampionshipState(msgData)
            
ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py
@@ -750,6 +750,13 @@
                        ("BYTE", "CopyMapID", 1),
                        ),
                "CrossFamilyFlagwarZoneMap":(
                        ("BYTE", "ZoneID", 0),
                        ("DWORD", "MapID", 1),
                        ("DWORD", "DataMapID", 1),
                        ("BYTE", "CopyMapID", 1),
                        ),
                "ActWeekParty":(
                        ("DWORD", "CfgID", 1),
                        ("char", "ActMark", 0),
@@ -2447,6 +2454,21 @@
    def GetDataMapID(self): return self.DataMapID # 数据地图ID
    def GetCopyMapID(self): return self.CopyMapID # 虚拟线路ID
# 跨服分区地图逐鹿万界
class IPY_CrossFamilyFlagwarZoneMap():
    def __init__(self):
        self.ZoneID = 0
        self.MapID = 0
        self.DataMapID = 0
        self.CopyMapID = 0
        return
    def GetZoneID(self): return self.ZoneID # 分区ID
    def GetMapID(self): return self.MapID # 场景地图ID
    def GetDataMapID(self): return self.DataMapID # 数据地图ID
    def GetCopyMapID(self): return self.CopyMapID # 虚拟线路ID
# 周狂欢活动时间表
class IPY_ActWeekParty():
    
@@ -3007,6 +3029,8 @@
        self.ipyCrossPenglaiZoneMapLen = len(self.ipyCrossPenglaiZoneMapCache)
        self.ipyCrossDemonLandZoneMapCache = self.__LoadFileData("CrossDemonLandZoneMap", IPY_CrossDemonLandZoneMap)
        self.ipyCrossDemonLandZoneMapLen = len(self.ipyCrossDemonLandZoneMapCache)
        self.ipyCrossFamilyFlagwarZoneMapCache = self.__LoadFileData("CrossFamilyFlagwarZoneMap", IPY_CrossFamilyFlagwarZoneMap)
        self.ipyCrossFamilyFlagwarZoneMapLen = len(self.ipyCrossFamilyFlagwarZoneMapCache)
        self.ipyActWeekPartyCache = self.__LoadFileData("ActWeekParty", IPY_ActWeekParty)
        self.ipyActWeekPartyLen = len(self.ipyActWeekPartyCache)
        self.ipyActLoginAwardCache = self.__LoadFileData("ActLoginAward", IPY_ActLoginAward)
@@ -3347,6 +3371,8 @@
    def GetCrossPenglaiZoneMapByIndex(self, index): return self.ipyCrossPenglaiZoneMapCache[index]
    def GetCrossDemonLandZoneMapCount(self): return self.ipyCrossDemonLandZoneMapLen
    def GetCrossDemonLandZoneMapByIndex(self, index): return self.ipyCrossDemonLandZoneMapCache[index]
    def GetCrossFamilyFlagwarZoneMapCount(self): return self.ipyCrossFamilyFlagwarZoneMapLen
    def GetCrossFamilyFlagwarZoneMapByIndex(self, index): return self.ipyCrossFamilyFlagwarZoneMapCache[index]
    def GetActWeekPartyCount(self): return self.ipyActWeekPartyLen
    def GetActWeekPartyByIndex(self, index): return self.ipyActWeekPartyCache[index]
    def GetActLoginAwardCount(self): return self.ipyActLoginAwardLen
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerEventCounter.py
@@ -33,6 +33,7 @@
import PlayerDuJie
import PlayerCharm
import CrossBattlefield
import CrossFamilyFlagwar
import CrossChampionship
import CrossYaomoBoss
#---------------------------------------------------------------------
@@ -141,6 +142,8 @@
    GameWorldArena.OnWeekEx()
    # 跨服战场
    CrossBattlefield.DoOnWeekEx()
    # 逐鹿万界
    CrossFamilyFlagwar.DoOnWeekEx()
    
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
@@ -74,6 +74,7 @@
import CrossRealmPK
import CrossChampionship
import CrossBattlefield
import CrossFamilyFlagwar
import CrossActAllRecharge
import ChPyNetSendPack
import NetPackCommon
@@ -671,6 +672,11 @@
        CrossBattlefield.MapServer_CrossBattlefieldOver(eval(resultName))
        return
    
    # 跨服仙盟夺旗战/逐鹿万界 结算
    if callName =="CrossFamilyFlagwarOver":
        CrossFamilyFlagwar.MapServer_CrossFamilyFlagwarOver(eval(resultName))
        return
    # 跨服妖魔boss伤害结算
    if callName =="CrossYaomoBossHurtInfo":
        CrossYaomoBoss.MapServer_CrossYaomoBossHurtInfo(eval(resultName))
ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
@@ -812,7 +812,9 @@
Def_CBT_BattlefieldWScore, # 跨服战场每周积分榜  153
Def_CBT_BattlefieldWScoreLastWeek, # 跨服战场上周积分榜  154
Def_CBT_YaomoBossHurt, # 跨服妖魔boss最新一次伤血排名  155
) = range(150, 155 + 1)
Def_CBT_FamilyFlagwar, # 逐鹿万界 - 单场榜  156
Def_CBT_FamilyFlagwarWeek, # 逐鹿万界 - 周总榜  157
) = range(150, 157 + 1)
#职业对应战力排行榜类型
JobFightPowerBillboardDict = {
@@ -1486,6 +1488,7 @@
CrossServerMsg_ActAllRechargeInfo = "ActAllRechargeInfo"# 跨服全民充值信息
CrossServerMsg_CrossDailyActionState = "CrossDailyActionState" # 跨服日常任务状态信息
CrossServerMsg_CrossYaomoBossHurtInfo = "CrossYaomoBossHurtInfo" # 跨服妖魔boss玩家伤害信息
CrossServerMsg_FamilyFlagwarOver = "FamilyFlagwarOver"  # 逐鹿万界结算信息
# 子服发送跨服信息定义
ClientServerMsg_ServerInitOK = "ServerInitOK"           # 子服启动成功
@@ -1776,7 +1779,8 @@
# 跨服每日活动编号定义, 从150开始
CrossDailyActionIDList = (
CrossDailyActionID_YaomoBoss, # 妖魔boss 150
) = range(150, 150 + 1)
CrossDailyActionID_FamilyWarFlag, # 跨服仙盟夺旗战/逐鹿万界 151
) = range(150, 150 + 2)
# 成就类型定义
SuccessTypeList = (
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1853,6 +1853,8 @@
Def_FBMapID_CrossGrasslandXian = 32050
#跨服战场
Def_FBMapID_CrossBattlefield = 32060
#跨服仙盟夺旗战/逐鹿万界
Def_FBMapID_CrossFamilyFlagwar = 32090
#竞技场战斗
Def_FBMapID_ArenaBattle = 31290
#情缘副本
@@ -1864,7 +1866,7 @@
#注册上传跨服服务器数据后直接进入跨服服务器的地图
RegisterEnter_CrossServerMapIDList = [Def_FBMapID_CrossPenglai, Def_FBMapID_CrossDemonLand, Def_FBMapID_CrossDemonKing, 
                                      Def_FBMapID_CrossGrasslandLing, Def_FBMapID_CrossGrasslandXian, Def_FBMapID_CrossBattlefield,
                                      Def_FBMapID_CrossChampionship,
                                      Def_FBMapID_CrossChampionship, Def_FBMapID_CrossFamilyFlagwar,
                                      ]
#跨服地图
Def_CrossMapIDList = RegisterEnter_CrossServerMapIDList + [Def_FBMapID_CrossRealmPK]
@@ -1878,10 +1880,12 @@
                         Def_FBMapID_CrossGrasslandXian:"CrossZonePK",
                         Def_FBMapID_CrossBattlefield:"CrossZonePK",
                         Def_FBMapID_CrossChampionship:"CrossZonePK",
                         Def_FBMapID_CrossFamilyFlagwar:"CrossZoneComm",
                         }
#跨服分区对应地图配置表名 - 仅适用于固定地图及虚拟分线的跨服玩法
Def_CrossZoneMapTableName = {Def_FBMapID_CrossPenglai:"CrossPenglaiZoneMap",
                             Def_FBMapID_CrossDemonLand:"CrossDemonLandZoneMap",
                             Def_FBMapID_CrossFamilyFlagwar:"CrossFamilyFlagwarZoneMap",
                             }
#需要动态分配线路的跨服地图
Def_CrossDynamicLineMap = [Def_FBMapID_CrossDemonKing, Def_FBMapID_CrossGrasslandLing, Def_FBMapID_CrossGrasslandXian, Def_FBMapID_CrossBattlefield, Def_FBMapID_CrossChampionship]
@@ -1988,6 +1992,7 @@
                'FairyTreasure':[Def_FBMapID_FairyTreasure],#缥缈宝藏
                'Love':[Def_FBMapID_Love],#情缘副本
                'CrossBattlefield':[Def_FBMapID_CrossBattlefield], #跨服战场
                'CrossFamilyFlagwar':[Def_FBMapID_CrossFamilyFlagwar], #跨服仙盟夺旗战/逐鹿万界
                }
#特殊副本ID, 由系统分配, 进入时候不验证IsMapCopyFull
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossFamilyFlagwar.py
New file
@@ -0,0 +1,969 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package GameWorldLogic.FBProcess.GameLogic_CrossFamilyFlagwar
#
# @todo:跨服仙盟夺旗战/逐鹿万界
# @author hxp
# @date 2023-04-25
# @version 1.0
#
# 详细描述: 跨服仙盟夺旗战
#
#-------------------------------------------------------------------------------
#"""Version = 2023-04-25 15:30"""
#-------------------------------------------------------------------------------
import ChConfig
import FBCommon
import GameWorld
import ShareDefine
import IPY_GameWorld
import IpyGameDataPY
import GameWorldProcess
import DataRecordPack
import PlayerControl
import SkillCommon
import BuffSkill
import AICommon
import GameObj
import GameMap
import operator
import time
#当前副本地图的状态
(
FB_Step_Open, # 地图开启
FB_Step_Prepare, # 地图准备
FB_Step_Fighting, # 战斗中
FB_Step_LeaveTime, # 自由时间
FB_Step_Over, # 副本关闭
) = range(5)
(
Time_Prepare, # 副本准备时间 0
Time_Fight, # 副本战斗时间 1
Time_Leave, # 副本离开时间 2
) = range(3)
FightRefreshInterval = 5000 # 战斗阶段刷新处理间隔,毫秒
GameFBData_BattleMgr = "BattleMgr"
## 战场管理类
class BattleMgr():
    def __init__(self):
        self.zoneID = 0
        self.battlePlayerDict = {} # 参与战斗的玩家 {playerID:BattlePlayer, ...}
        self.battleFamilyDict = {} # 参与战斗的仙盟 {familyID:BattleFamily, ...}
        self.battleFamilySortList = [] # 参与战斗的仙盟积分排名列表 [BattleFamily, ...]
        self.allotRebornPointInfo = {} # 已经分配的复活点,一个复活点可能多个仙盟共用 {index:[familyID, ...], ...}
        self.playerFlagDict = {} # 玩家获得战旗 {playerID:flagNPC, ...}
        self.worldHelpDict = {} # 未通知的世界变更信息
        return
    def sortBattleFamilyScore(self):
        self.battleFamilySortList = self.battleFamilyDict.values()
        self.battleFamilySortList.sort(key=operator.attrgetter("score", "scoreSortTime"), reverse=True)
        return
    def getBattleFamily(self, familyID):
        if familyID in self.battleFamilyDict:
            batFamily = self.battleFamilyDict[familyID]
        else:
            batFamily = BattleFamily(familyID)
            self.battleFamilyDict[familyID] = batFamily
        return batFamily
    def getBattlePlayer(self, playerID):
        if playerID in self.battlePlayerDict:
            batPlayer = self.battlePlayerDict[playerID]
        else:
            batPlayer = BattlePlayer(playerID)
            self.battlePlayerDict[playerID] = batPlayer
        return batPlayer
    def addBattleFamily(self, familyID, name):
        batFamily = self.getBattleFamily(familyID)
        batFamily.name = name
        self.__allotRebornPoint(familyID)
        return batFamily
    def __allotRebornPoint(self, familyID):
        batFamily = self.getBattleFamily(familyID)
        for familyIDList in self.allotRebornPointInfo.values():
            if familyID in familyIDList:
                return
        allotIndex = -1
        allotAlreadyIndexs = self.allotRebornPointInfo.keys()
        rebornPointList = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwar", 2)
        if len(rebornPointList) > len(allotAlreadyIndexs):
            # 还有空余点未分配,直接分配空余点
            for index, _ in enumerate(rebornPointList):
                if index not in allotAlreadyIndexs:
                    allotIndex = index
                    break
        if allotIndex == -1:
            # 没有空余点了,与其他仙盟共用,分配一个最少仙盟的复活点
            leastIDCount = 99999
            for index, familyIDList in self.allotRebornPointInfo.items():
                if len(familyIDList) < leastIDCount and index < len(rebornPointList):
                    leastIDCount = len(familyIDList)
                    allotIndex = index
        rebornPoint = rebornPointList[allotIndex]
        if len(rebornPoint) != 3:
            GameWorld.ErrLog("复活点配置异常! index=%s, %s" % (allotIndex, rebornPoint))
            return
        if allotIndex not in self.allotRebornPointInfo:
            self.allotRebornPointInfo[allotIndex] = []
        familyIDList = self.allotRebornPointInfo[allotIndex]
        familyIDList.append(familyID)
        batFamily.rebornPoint = rebornPoint
        GameWorld.Log("分配仙盟复活点: familyID=%s,allotIndex=%s,rebornPoint=%s,allotRebornPointInfo=%s"
                      % (familyID, allotIndex, rebornPoint, self.allotRebornPointInfo), self.zoneID)
        return True
    def setPlayerFlag(self, playerID, flagNPC):
        self.playerFlagDict[playerID] = flagNPC
        npcID = flagNPC.GetNPCID()
        batPlayer = self.getBattlePlayer(playerID)
        flagOwner = self.worldHelpDict.get("flagOwner", {})
        flagOwner[npcID] = [playerID, batPlayer.name]
        self.worldHelpDict["flagOwner"] = flagOwner
        return
    def popPlayerFlag(self, playerID):
        self.playerFlagDict.pop(playerID, None)
        flagOwner = self.worldHelpDict.get("flagOwner", {})
        for npcID, playerInfo in flagOwner.items():
            if playerID == playerInfo[0]:
                flagOwner.pop(npcID, None)
                break
        return
    def getWorldHelpInfo(self, isAll, familyID):
        helpInfo = {}
        if not isAll:
            helpInfo = self.worldHelpDict
        else:
            flagOwner = {}
            for playerID, flagNPC in self.playerFlagDict.items():
                npcID = flagNPC.GetNPCID()
                batPlayer = self.getBattlePlayer(playerID)
                flagOwner[npcID] = [playerID, batPlayer.name]
            helpInfo = {"flagOwner":flagOwner}
        # 取自身仙盟及前后名次仙盟信息
        familyIndex = -1
        familyList = []
        for index, batFamily in enumerate(self.battleFamilySortList):
            if familyID == batFamily.familyID:
                familyIndex = index
                break
        # 第一名默认取前3名
        if familyIndex == 0:
            familyList = self.battleFamilySortList[:3]
        # 第一名默认取前3名
        elif familyIndex == len(self.battleFamilySortList) - 1:
            familyList = self.battleFamilySortList[-3:]
        elif familyIndex > 0:
            familyList = self.battleFamilySortList[familyIndex - 1:familyIndex + 2]
        for i, batFamily in enumerate(familyList):
            familyList[i] = [batFamily.familyID, batFamily.score, batFamily.name]
        helpInfo["familyList"] = familyList
        return {"world":helpInfo}
## 战场仙盟类
class BattleFamily():
    def __init__(self, familyID):
        self.familyID = familyID
        self.name = ""
        self.score = 0 # 积分
        self.scoreSortTime = 0 # 积分变更排序time值,用于同积分时,先到排名靠前
        self.rebornPoint = [130, 300, 10] # 复活点坐标 [x,y,r],这里为默认值,防止没有分配到报错等
        self.battlePlayerIDList = [] # 参与战斗的仙盟玩家ID列表 [playerID, ...]
        self.battlePlayerSortList = [] # 参与战斗的仙盟玩家积分排名列表 [BattlePlayer, ...]
        self.homePlayerIDList = [] # 在复活点里的玩家
        self.familyHelpDict = {} # 未通知的仙盟变更信息
        return
    def addJoinPlayer(self, playerID):
        if playerID not in self.battlePlayerIDList:
            self.battlePlayerIDList.append(playerID)
        self.familyHelpDict["playerCount"] = len(self.battlePlayerIDList)
        return
    def setPlayerToRebornPoint(self, curPlayer):
        if not self.rebornPoint:
            return
        randPosX, randPosY, maxDist = self.rebornPoint
        posPoint = GameMap.GetEmptyPlaceInAreaEx(randPosX, randPosY, 3, maxDist)
        curPlayer.ResetPos(posPoint.GetPosX(), posPoint.GetPosY())
        return
    def sortBattlePlayerScore(self):
        if len(self.battlePlayerIDList) != len(self.battlePlayerSortList):
            self.battlePlayerSortList = []
            mgr = GetBattleMgr()
            for playerID in self.battlePlayerIDList:
                batPlayer = mgr.getBattlePlayer(playerID)
                self.battlePlayerSortList.append(batPlayer)
        self.battlePlayerSortList.sort(key=operator.attrgetter("score", "scoreSortTime"), reverse=True)
        return
    def addFamilyScore(self, addValue):
        ## 增加积分
        if addValue <= 0:
            return
        self.score = max(0, self.score + addValue)
        self.familyHelpDict["score"] = self.score
        calcTime = 3471264000 #GameWorld.ChangeTimeStrToNum("2080-01-01 00:00:00")
        self.scoreSortTime = max(0, calcTime - int(time.time()))
        #GameWorld.DebugLog("    增加仙盟积分: familyID=%s,addValue=%s,updScore=%s" % (self.familyID, addValue, self.score))
        return
    def getFamilyHelpInfo(self, isAll):
        helpInfo = {}
        if not isAll:
            helpInfo = self.familyHelpDict
        else:
            helpInfo = {"score":self.score, "playerCount":len(self.battlePlayerIDList)}
        return {"family":helpInfo}
## 战场玩家类
class BattlePlayer():
    def __init__(self, playerID):
        self.playerID = playerID
        self.name = ""
        self.familyID = 0
        self.familyName = ""
        self.accID = ""
        self.job = 1
        self.realmLV = 0
        self.fightPower = 0
        self.restoreHPTick = 0 # 营地回血tick
        self.healthRebornCount = 0 # 原地健康复活次数
        self.continueKillCount = 0 # 连杀次数
        self.score = 0 # 积分
        self.scoreSortTime = 0 # 积分变更排序time值,用于同积分时,先到排名靠前
        self.hurtTotal = 0 # 累计伤害
        self.onlineCalcTick = 0 # 在线计算tick
        self.outsideFlagTick = 0 # 超出战旗范围计算tick
        self.outsideFlagNotifySecond = 0 # 通知超出范围秒
        self.playerHelpDict = {} # 未通知的玩家变更信息
        return
    def doPlayerEnter(self, curPlayer, tick):
        self.familyID = curPlayer.GetFamilyID()
        self.familyName = curPlayer.GetFamilyName()
        self.job = curPlayer.GetJob()
        self.accID = curPlayer.GetAccID()
        self.name = curPlayer.GetPlayerName()
        self.realmLV = curPlayer.GetOfficialRank()
        self.fightPower = PlayerControl.GetFightPower(curPlayer)
        self.onlineCalcTick = tick
        return
    def addPlayerScore(self, addValue):
        if addValue <= 0:
            return
        self.score = max(0, self.score + addValue)
        calcTime = 3471264000 #GameWorld.ChangeTimeStrToNum("2080-01-01 00:00:00")
        self.scoreSortTime = max(0, calcTime - int(time.time()))
        #GameWorld.DebugLog("    增加玩家积分: playerID=%s,addValue=%s,updScore=%s" % (self.playerID, addValue, self.score))
        self.playerHelpDict.update({"score":self.score})
        return
    def addHealthRebornCount(self):
        self.healthRebornCount += 1
        self.playerHelpDict["healthRebornCount"] = self.healthRebornCount
        return
    def getPlayerHelpInfo(self, isAll):
        helpInfo = {}
        if not isAll:
            helpInfo = self.playerHelpDict
        else:
            helpInfo = {"score":self.score, "healthRebornCount":self.healthRebornCount}
        return {"player":helpInfo}
def GetBattleMgr():
    mgr = FBCommon.GetGameFBData(GameFBData_BattleMgr)
    if not mgr:
        mgr = BattleMgr()
        FBCommon.SetGameFBData(GameFBData_BattleMgr, mgr)
    return mgr
def GetBFStepTime(): return IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwar", 1) # 阶段时间
def OnOpenFB(tick):
    FBCommon.SetGameFBData(GameFBData_BattleMgr, None)
    mgr = GetBattleMgr()
    mgr.zoneID = GameWorld.GetGameWorld().GetPropertyID()
    FBCommon.SetFBStep(FB_Step_Prepare, tick)
    return
def OnCloseFB(tick):
    GameWorld.GetGameWorld().SetPropertyID(0)
    FBCommon.SetGameFBData(GameFBData_BattleMgr, None)
    #FBCommon.ClearFBNPC()
    return
def OnEnterFBEvent(curPlayer, mapID, lineID, tick):
    if GameWorld.IsCrossServer():
        return True
    if not curPlayer.GetFamilyID():
        GameWorld.DebugLog("无仙盟无法进入: mapID=%s" % mapID)
        return False
    if not GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.CrossDailyActionID_FamilyWarFlag):
        GameWorld.DebugLog("非活动中,无法进入: CrossDailyActionID=%s" % ShareDefine.CrossDailyActionID_FamilyWarFlag)
        return False
    return True
def OnChangeMapAsk(ask, tick):
    return IPY_GameWorld.cmeAccept
##副本玩家进入点, 玩家分散在半径3格范围
def OnGetFBEnterPos(curPlayer, mapID, lineId, ipyEnterPosInfo, tick):
    return ipyEnterPosInfo
def DoEnterFB(curPlayer, tick):
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    playerID = curPlayer.GetPlayerID()
    familyID = curPlayer.GetFamilyID()
    familyName = curPlayer.GetFamilyName()
    mgr = GetBattleMgr()
    if fbStep not in [FB_Step_Prepare, FB_Step_Fighting] or not familyID:
        GameWorld.Log("DoEnterFB... fbStep=%s,familyID=%s,playerID=%s PlayerLeaveFB" % (fbStep, familyID, playerID), mgr.zoneID)
        PlayerControl.PlayerLeaveFB(curPlayer)
        return
    GameWorld.Log("DoEnterFB... fbStep=%s,familyID=%s,playerID=%s" % (fbStep, familyID, playerID), mgr.zoneID)
    #--- 测试 ---
#    testFamilyCount = 50
#    if len(mgr.battleFamilyDict) < testFamilyCount:
#        import random
#        for i in range(testFamilyCount):
#            fakeID = i + 10
#            fakeName = "假名字%s" % fakeID
#            fakeName = fakeName.decode(ShareDefine.Def_Game_Character_Encoding).encode(GameWorld.GetCharacterEncoding())
#            fakeFamily = mgr.addBattleFamily(fakeID, fakeName)
#            fakeFamily.addFamilyScore(random.randint(1000, 1500))
    #-----------
    batFamily = mgr.addBattleFamily(familyID, familyName)
    batPlayer = mgr.getBattlePlayer(playerID)
    batPlayer.doPlayerEnter(curPlayer, tick)
    batFamily.addJoinPlayer(playerID)
    batFamily.setPlayerToRebornPoint(curPlayer)
    if fbStep == FB_Step_Prepare:
        notify_tick = GetBFStepTime()[Time_Prepare] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
        curPlayer.Sync_TimeTick(IPY_GameWorld.tttWaitStart, 0, max(notify_tick, 0), True)
    elif fbStep == FB_Step_Fighting:
        notify_tick = GetBFStepTime()[Time_Fight] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
        curPlayer.Sync_TimeTick(IPY_GameWorld.tttTowerTake, 0, max(notify_tick, 0), True)
    NotifyCrossFamilyFlagHelp(True, curPlayer)
    return
def NotifyCrossFamilyFlagHelp(isAll=False, curPlayer=None, helpEx=None):
    ## 广播战场帮助信息,针对所有玩家
    mgr = GetBattleMgr()
    if curPlayer:
        __notifyPlayerHelp(curPlayer, mgr, isAll, helpEx)
    else:
        playerManager = GameWorld.GetMapCopyPlayerManager()
        for index in xrange(playerManager.GetPlayerCount()):
            player = playerManager.GetPlayerByIndex(index)
            if not player:
                continue
            playerID = player.GetPlayerID()
            if playerID not in mgr.battlePlayerDict:
                #GameWorld.DebugLog("还未加入战斗,暂不处理! playerID=%s" % playerID)
                continue
            __notifyPlayerHelp(player, mgr, isAll, helpEx)
        # 重置未通知的
        mgr.worldHelpDict = {}
        for familyID in mgr.battleFamilyDict.keys():
            batFamily = mgr.getBattleFamily(familyID)
            batFamily.familyHelpDict = {}
    return
def __notifyPlayerHelp(curPlayer, mgr, isAll, helpEx):
    if not mgr:
        mgr = GetBattleMgr()
    playerID = curPlayer.GetPlayerID()
    batPlayer = mgr.getBattlePlayer(playerID)
    batFamily = mgr.getBattleFamily(batPlayer.familyID)
    helpDict = {}
    helpDict.update(mgr.getWorldHelpInfo(isAll, batPlayer.familyID))
    helpDict.update(batFamily.getFamilyHelpInfo(isAll))
    helpDict.update(batPlayer.getPlayerHelpInfo(isAll))
    if helpEx:
        helpDict.update(helpEx)
    GameWorld.DebugLog("FBHelp: %s" % helpDict, playerID)
    FBCommon.Notify_FBHelp(curPlayer, helpDict)
    batPlayer.playerHelpDict = {}
    return
##获得副本帮助信息, 用于通知阵营比分条
def DoFBHelp(curPlayer, tick):
    return
##玩家退出副本
def DoExitFB(curPlayer, tick):
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    if fbStep != FB_Step_Fighting:
        return
    mgr = GetBattleMgr()
    playerID = curPlayer.GetPlayerID()
    GameWorld.Log("DoExitFB... playerID=%s,fbStep=%s" % (playerID, fbStep), mgr.zoneID)
    batPlayer = mgr.getBattlePlayer(playerID)
    batPlayer.onlineCalcTick = 0
    return
##玩家主动离开副本.
def DoPlayerLeaveFB(curPlayer, tick):
    return
##副本总逻辑计时器
# @param tick 时间戳
# @return 无意义
# @remarks 副本总逻辑计时器
def OnProcess(tick):
    fbStep = GameWorld.GetGameFB().GetFBStep()
    # 副本准备
    if fbStep == FB_Step_Prepare:
        __DoLogic_FB_Prepare(fbStep, tick)
    # 副本进行中
    elif fbStep == FB_Step_Fighting:
        __DoLogic_FB_Fighting(tick)
    # 副本结束
    elif fbStep == FB_Step_LeaveTime:
        __DoLogic_FB_Leave(tick)
    return
def __DoLogic_FB_Prepare(fbStep, tick):
    remaindTick = GetBFStepTime()[Time_Prepare] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
    if remaindTick > 0:
        return
    FBCommon.SetFBStep(FB_Step_Fighting, tick)
    mgr = GetBattleMgr()
    fightTime = GetBFStepTime()[Time_Fight] * 1000
    playerManager = GameWorld.GetMapCopyPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        playerID = curPlayer.GetPlayerID()
        if not playerID:
            continue
        if playerID not in mgr.battlePlayerDict:
            #GameWorld.DebugLog("还未加入战斗,暂不处理! playerID=%s" % playerID)
            continue
        curPlayer.Sync_TimeTick(IPY_GameWorld.tttTowerTake, 0, fightTime, True)
        batPlayer = mgr.getBattlePlayer(playerID)
        batPlayer.onlineCalcTick = tick # 开始战斗重新统计在线收益
        batFamily = mgr.getBattleFamily(batPlayer.familyID)
        batFamily.setPlayerToRebornPoint(curPlayer)
    NotifyCrossFamilyFlagHelp(True)
    #PlayerControl.FBNotify("CrossBattlefieldStartFighting")
    return
def __DoLogic_FB_Fighting(tick):
    passTick = tick - GameWorld.GetGameFB().GetFBStepTick()
    remaindTick = GetBFStepTime()[Time_Fight] * 1000 - passTick
    if remaindTick > 0:
        __refreshFamilyHome(tick)
        __refreshFlagoutside(tick)
        gameFB = GameWorld.GetGameFB()
        lastTick = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NotifyFBHelpTick)
        if tick - lastTick >= FightRefreshInterval:
            gameFB.SetGameFBDict(ChConfig.Def_FB_NotifyFBHelpTick, tick)
            refreshCrossFamilyFlagwar(tick)
            NotifyCrossFamilyFlagHelp()
        return
    DoOver(tick)
    return
def __DoLogic_FB_Leave(tick):
    remaindTick = GetBFStepTime()[Time_Leave] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
    if remaindTick > 0:
        return
    FBCommon.DoLogic_FBKickAllPlayer()
    GameWorldProcess.CloseFB(tick)
    FBCommon.SetFBStep(FB_Step_Over, tick)
    return
def __refreshFamilyHome(tick):
    # 刷新营地相关,如回血等
    restoreHPPerBySecond = IpyGameDataPY.GetFuncCfg("CrossFamilyFlagwar", 3) # 每秒回血百分比
    mgr = GetBattleMgr()
    copyMapMgr = GameWorld.GetMapCopyPlayerManager()
    for familyID in mgr.battleFamilyDict.keys():
        batFamily = mgr.getBattleFamily(familyID)
        safePosX, safePosY, safeRadius = batFamily.rebornPoint
        for playerID in batFamily.homePlayerIDList[::-1]:
            curPlayer = copyMapMgr.FindPlayerByID(playerID)
            if not curPlayer:
                continue
            batPlayer = mgr.getBattlePlayer(playerID)
            if GameWorld.GetDist(curPlayer.GetPosX(), curPlayer.GetPosY(), safePosX, safePosY) > safeRadius:
                batFamily.homePlayerIDList.remove(playerID)
                batPlayer.restoreHPTick = 0
                continue
            # 营地回血
            restoreSeconds = (tick - batPlayer.restoreHPTick) / 1000.0 if batPlayer.restoreHPTick else 1 # 首次保底1秒
            if restoreSeconds < 1:
                continue
            maxHP = GameObj.GetMaxHP(curPlayer)
            if GameObj.GetHP(curPlayer) < maxHP:
                restoreHP = int(maxHP * restoreHPPerBySecond / 100.0 * round(restoreSeconds, 1))
                SkillCommon.SkillAddHP(curPlayer, 0, restoreHP)
            batPlayer.restoreHPTick = tick
    return
def __refreshFlagoutside(tick):
    # 刷新战旗出界
    mgr = GetBattleMgr()
    if not mgr.playerFlagDict:
        return
    outsideR, protectSeconds = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarFlag", 4)
    copyMapMgr = GameWorld.GetMapCopyPlayerManager()
    for playerID, flagNPC in mgr.playerFlagDict.items():
        curPlayer = copyMapMgr.FindPlayerByID(playerID)
        if not curPlayer:
            # 找不到玩家,直接归还战旗
            setFlagOwner(flagNPC, None, tick)
            continue
        flagPosX, flagPosY = flagNPC.GetPosX(), flagNPC.GetPosY()
        batPlayer = mgr.getBattlePlayer(playerID)
        if GameWorld.GetDist(curPlayer.GetPosX(), curPlayer.GetPosY(), flagPosX, flagPosY) <= outsideR:
            batPlayer.outsideFlagTick = 0
            batPlayer.outsideFlagNotifySecond = 0
        else:
            if not batPlayer.outsideFlagTick:
                batPlayer.outsideFlagTick = tick
                continue
            passSeconds = (tick - batPlayer.outsideFlagTick) / 1000
            if passSeconds < protectSeconds:
                remainSecond = protectSeconds - passSeconds
                if remainSecond != batPlayer.outsideFlagNotifySecond:
                    batPlayer.outsideFlagNotifySecond = remainSecond
                    PlayerControl.NotifyCode(curPlayer, "CrossFamilyFlagwarOutsideflag", [flagNPC.GetNPCID(), remainSecond])
                continue
            setFlagOwner(flagNPC, None, tick)
    return
def setFlagOwner(flagNPC, newOwner, tick):
    if not flagNPC:
        return
    ownerBuffDict = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarFlag", 5, {})
    flagNPCID = flagNPC.GetNPCID()
    flagType = getFlagType(flagNPC)
    if not flagType:
        return
    mgr = GetBattleMgr()
    copyMapMgr = GameWorld.GetMapCopyPlayerManager()
    GameWorld.DebugLog("设置战旗归属: flagNPCID=%s,flagType=%s" % (flagNPCID, flagType), mgr.zoneID)
    for oldPlayerID, npc in mgr.playerFlagDict.items():
        if not npc or flagNPCID != npc.GetNPCID():
            continue
        GameWorld.DebugLog("    移除旧归属: flagNPCID=%s,oldPlayerID=%s" % (flagNPCID, oldPlayerID), mgr.zoneID)
        mgr.popPlayerFlag(oldPlayerID)
        oldOwner = copyMapMgr.FindPlayerByID(oldPlayerID)
        if oldOwner:
            PlayerControl.NotifyCode(oldOwner, "CrossFamilyFlagwarFlagDrop", [flagNPCID])
            # 删除归属buff
            for delBuffID in ownerBuffDict.values():
                BuffSkill.DelBuffBySkillID(oldOwner, delBuffID, tick)
    if newOwner:
        newPlayerID = newOwner.GetPlayerID()
        # 已经有战旗,判断战旗类型,高级可替换低级
        if newPlayerID in mgr.playerFlagDict:
            bodyFlagNPC = mgr.playerFlagDict[newPlayerID]
            bodyFlagType = getFlagType(bodyFlagNPC)
            bodyFlagNPCID = bodyFlagNPC.GetNPCID()
            if bodyFlagType >= flagType:
                GameWorld.DebugLog("    新归属者已有高级战旗,保留原归属: bodyFlagNPCID=%s,bodyFlagType=%s >= flagType=%s"
                                   % (bodyFlagNPCID, bodyFlagType, flagType), mgr.zoneID)
                setFlagOwnerNone(flagNPC)
                return
            GameWorld.DebugLog("    新归属者已有低级战旗,先归还原低级战旗: bodyFlagNPCID=%s,bodyFlagType=%s < flagType=%s"
                               % (bodyFlagNPCID, bodyFlagType, flagType), mgr.zoneID)
            setFlagOwner(bodyFlagNPC, None, tick)
        batPlayer = mgr.getBattlePlayer(newPlayerID)
        batFamily = mgr.getBattleFamily(batPlayer.familyID)
        batPlayer.outsideFlagTick = 0
        mgr.setPlayerFlag(newPlayerID, flagNPC)
        PlayerControl.NotifyCode(newOwner, "CrossFamilyFlagwarFlagOwn", [flagNPCID])
        # 归属积分
        ownScoreDict = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarFlag", 2, {})
        ownPlayerScore, ownFamilyScore = ownScoreDict.get(flagType, [0, 0])
        batPlayer.addPlayerScore(ownPlayerScore)
        batFamily.addFamilyScore(ownFamilyScore)
        # 归属buff
        ownerBuffID = ownerBuffDict.get(flagType, 0)
        if ownerBuffID:
            SkillCommon.AddBuffBySkillType_NoRefurbish(newOwner, ownerBuffID, tick)
        flagNPC.SetVisible(False)
        GameWorld.DebugLog("    更新新归属: flagNPCID=%s,newPlayerID=%s,ownerBuffID=%s" % (flagNPCID, newPlayerID, ownerBuffID), mgr.zoneID)
    # 没有新归属,归还战旗
    else:
        GameWorld.DebugLog("    没有新归属,归还战旗: flagNPCID=%s" % flagNPCID, mgr.zoneID)
        setFlagOwnerNone(flagNPC)
    return
def setFlagOwnerNone(flagNPC):
    GameWorld.DebugLog("    setFlagOwnerNone: npcID=%s" % flagNPC.GetNPCID())
    flagNPC.SetVisible(True)
    return
def refreshCrossFamilyFlagwar(tick):
    ## 刷新战场相关
    olPlayerScore, olFamilyScore = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarScore", 2) # 玩家在战场时每秒固定获得  个人得分|仙盟得分
    flagTimeScoreDict = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarFlag", 3, {})
    mgr = GetBattleMgr()
    copyMapMgr = GameWorld.GetMapCopyPlayerManager()
    for familyID in mgr.battleFamilyDict.keys():
        batFamily = mgr.getBattleFamily(familyID)
        batFamily.battlePlayerSortList = []
        safePosX, safePosY, safeRadius = batFamily.rebornPoint
        for playerID in batFamily.battlePlayerIDList:
            batPlayer = mgr.getBattlePlayer(playerID)
            batFamily.battlePlayerSortList.append(batPlayer)
            curPlayer = copyMapMgr.FindPlayerByID(playerID)
            if not curPlayer:
                continue
            onlineTimes = 0
            # 累计参与战斗时长
            if batPlayer.onlineCalcTick:
                onlineTimes = max(0, tick - batPlayer.onlineCalcTick) / 1000
                batPlayer.onlineCalcTick = tick
                batPlayer.addPlayerScore(onlineTimes * olPlayerScore)
                batFamily.addFamilyScore(onlineTimes * olFamilyScore)
            # 回营地
            if GameWorld.GetDist(curPlayer.GetPosX(), curPlayer.GetPosY(), safePosX, safePosY) <= safeRadius:
                if playerID not in batFamily.homePlayerIDList:
                    batFamily.homePlayerIDList.append(playerID)
            # 战旗定时积分
            if playerID in mgr.playerFlagDict:
                flagType = getFlagType(mgr.playerFlagDict[playerID])
                ftPlayerScore, ftFamilyScore = flagTimeScoreDict.get(flagType, [0, 0])
                batPlayer.addPlayerScore(onlineTimes * ftPlayerScore)
                batFamily.addFamilyScore(onlineTimes * ftFamilyScore)
        batFamily.sortBattlePlayerScore()
    # 放最后排序,上面的逻辑可能还会加分
    mgr.sortBattleFamilyScore()
    return
def getFlagType(curNPC):
    if not curNPC:
        return 0
    npcID = curNPC.GetNPCID()
    flagTypeDict = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarFlag", 1, {})
    for ft, npcIDList in flagTypeDict.items():
        if npcID in npcIDList:
            return ft
    return 0
##是否可以夺旗
def OnCanCollect(curPlayer, curNPC, tick):
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    # 非战斗阶段不可采集
    if fbStep != FB_Step_Fighting:
        return False
    playerID = curPlayer.GetPlayerID()
    mgr = GetBattleMgr()
    if playerID in mgr.playerFlagDict:
        ownerFlagType = getFlagType(mgr.playerFlagDict[playerID])
        if ownerFlagType >= getFlagType(curNPC):
            GameWorld.Log("已经拥有更高级的战旗,无法采集! ownerFlagType=%s >= %s" % (ownerFlagType, getFlagType(curNPC)), playerID)
            return False
    return True
## 开始采集
def OnBeginCollect(curPlayer, curNPC):
    return
## 退出采集
def OnExitCollect(curPlayer, curNPC):
    #if not curNPC or not hasattr(curNPC, "GetNPCID"):
    #    return
    return
##玩家收集成功(塔, 旗)
def OnCollectOK(curPlayer, npcID, tick):
    #GameWorld.DebugLog("OnCollectOK npcID=%s" % npcID, curPlayer.GetPlayerID())
    tagObj = curPlayer.GetActionObj()
    if not tagObj:
        return
    if tagObj.GetGameObjType() != IPY_GameWorld.gotNPC:
        return
    curNPC = GameWorld.GetNPCManager().GetNPCByIndex(tagObj.GetIndex())
    #npcID = curNPC.GetNPCID()
    flagType = getFlagType(curNPC)
    AICommon.ClearPlayerPreparing(curNPC, curPlayer, "CrossFamilyFlagwarCollectOKSlow")
    if flagType:
        setFlagOwner(curNPC, curPlayer, tick)
    return
## PVP伤害相关
def OnPVPDamage(curPlayer, damageValue, tagPlayer, tick):
    playerID = curPlayer.GetPlayerID()
    mgr = GetBattleMgr()
    batPlayer = mgr.getBattlePlayer(playerID)
    batPlayer.hurtTotal += damageValue
    #GameWorld.DebugLog("OnPVPDamage: damageValue=%s,hurtTotal=%s" % (damageValue, batPlayer.hurtTotal), playerID)
    return
##处理副本中杀死玩家逻辑
def DoFBOnKill_Player(curPlayer, defender, tick):
    playerID = curPlayer.GetPlayerID()
    defPlayerID = defender.GetPlayerID()
    #GameWorld.DebugLog("DoFBOnKill_Player playerID=%s,defPlayerID=%s" % (playerID, defPlayerID))
    mgr = GetBattleMgr()
    killPlayerScore, killFamilyScore = IpyGameDataPY.GetFuncEvalCfg("CrossFamilyFlagwarScore", 1) # 击杀玩家 个人得分|仙盟得分
    batPlayer = mgr.getBattlePlayer(playerID)
    batFamily = mgr.getBattleFamily(batPlayer.familyID)
    batPlayer.addPlayerScore(killPlayerScore)
    batFamily.addFamilyScore(killFamilyScore)
    # 获得对方战旗归属
    if defPlayerID in mgr.playerFlagDict:
        setFlagOwner(mgr.playerFlagDict[defPlayerID], curPlayer, tick)
    return True
def OnCanFBReborn(curPlayer, rebornType):
    playerID = curPlayer.GetPlayerID()
    if rebornType == ChConfig.rebornType_Health:
        mgr = GetBattleMgr()
        playerID = curPlayer.GetPlayerID()
        batPlayer = mgr.getBattlePlayer(playerID)
        healthRebornMax = IpyGameDataPY.GetFuncCfg("CrossFamilyFlagwarReborn", 1)
        if healthRebornMax and batPlayer.healthRebornCount >= healthRebornMax:
            GameWorld.Log("已达原地健康复活次数上限! playerID=%s" % playerID, mgr.zoneID)
            return False
    return True
def OnPlayerReborn():
    ## 是否副本复活
    return True
## 玩家复活后处理
def OnPlayerRebornOver(curPlayer, rebornType):
    mgr = GetBattleMgr()
    playerID = curPlayer.GetPlayerID()
    batPlayer = mgr.getBattlePlayer(playerID)
    if rebornType == ChConfig.rebornType_Health:
        batPlayer.addHealthRebornCount()
    else:
        # 非原地
        batFamily = mgr.getBattleFamily(curPlayer.GetFamilyID())
        batFamily.setPlayerToRebornPoint(curPlayer)
        batPlayer.continueKillCount = 0 # 中断连杀数
    FBCommon.Notify_FBHelp(curPlayer, batPlayer.getPlayerHelpInfo(False))
    return
def DoOver(tick):
    mgr = GetBattleMgr()
    zoneID = mgr.zoneID
    gameFB = GameWorld.GetGameFB()
    gameWorld = GameWorld.GetGameWorld()
    copyMapID = gameWorld.GetCopyMapID()
    fbStep = gameFB.GetFBStep()
    if fbStep > FB_Step_Fighting:
        GameWorld.ErrLog("跨服仙盟夺旗战触发重复结算,不处理! zoneID=%s,copyMapID=%s" % (mgr.zoneID, copyMapID), zoneID)
        return
    FBCommon.SetFBStep(FB_Step_LeaveTime, tick)
    GameWorld.Log("跨服仙盟夺旗战结算! zoneID=%s,copyMapID=%s" % (mgr.zoneID, copyMapID), zoneID)
    refreshCrossFamilyFlagwar(tick) # 结算前强刷一次
    NotifyCrossFamilyFlagHelp(True)
    leaveTime = GetBFStepTime()[Time_Leave] * 1000
    copyMapMgr = GameWorld.GetMapCopyPlayerManager()
    drDict = {"mapID":GameWorld.GetMap().GetMapID(), "realMapID":gameWorld.GetRealMapID(), "copyMapID":copyMapID, "zoneID":zoneID}
    drBatFamilyList = []
    battleFamilyList = []
    for familyRank, batFamily in enumerate(mgr.battleFamilySortList, 1):
        familyID = batFamily.familyID
        #batFamily = mgr.getBattleFamily(familyID)
        familyScore = batFamily.score
        familyName = batFamily.name
        GameWorld.Log("familyRank=%s,familyID=%s,familyScore=%s" % (familyRank, familyID, familyScore), zoneID)
        overPlayerList = [] # 同步前端结算
        drPlayerList = [] # 流向记录
        battlePlayerList = [] # 同步GameServer结算
        for playerRank, batPlayer in enumerate(batFamily.battlePlayerSortList, 1):
            playerID = batPlayer.playerID
            #batPlayer = mgr.getBattlePlayer(playerID)
            score = batPlayer.score
            #job = batPlayer.job
            #realmLV = batPlayer.realmLV
            name = batPlayer.name
            hurtTotal = batPlayer.hurtTotal
            GameWorld.Log("     familyID=%s,playerRank=%s,playerID=%s,score=%s,hurtTotal=%s,accID=%s"
                          % (familyID, playerRank, playerID, score, hurtTotal, batPlayer.accID), zoneID)
            overPlayerList.append([name, score])
            battlePlayerList.append([playerID, score, hurtTotal])
            drPlayerList.append({"playerID":playerID, "accID":batPlayer.accID, "playerRank":playerRank, "score":score, "hurtTotal":hurtTotal})
        overDict = {"familyScore":familyScore, "familyRank":familyRank, "playerList":overPlayerList}
        for playerRank, batPlayer in enumerate(batFamily.battlePlayerSortList, 1):
            playerID = batPlayer.playerID
            player = copyMapMgr.FindPlayerByID(playerID)
            if not player:
                continue
            player.Sync_TimeTick(IPY_GameWorld.tttLeaveMap, 0, leaveTime, True)
            lineID = 0
            overDict.update({"familyScore":familyScore, "familyRank":familyRank})
            FBCommon.NotifyFBOver(player, ChConfig.Def_FBMapID_CrossFamilyFlagwar, lineID, True, overDict)
        battleFamilyList.append([familyRank, familyID, familyName, familyScore, battlePlayerList])
        drBatFamilyList.append({"familyID":familyID, "familyScore":familyScore, "familyRank":familyRank, "drPlayerList":drPlayerList})
    # 同步GameServer 比赛结果
    msgInfo = str([zoneID, battleFamilyList])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "CrossFamilyFlagwarOver", msgInfo, len(msgInfo))
    # 记录流向
    drDict["batFamilyList"] = drBatFamilyList
    DataRecordPack.SendEventPack("CrossFamilyFlagwarOver", drDict)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -1760,6 +1760,15 @@
                        ("WORD", "PosY", 0),
                        ),
                "CrossFamilyFlagwarZoneMap":(
                        ("BYTE", "ZoneID", 0),
                        ("DWORD", "MapID", 1),
                        ("DWORD", "DataMapID", 1),
                        ("BYTE", "CopyMapID", 1),
                        ("WORD", "PosX", 0),
                        ("WORD", "PosY", 0),
                        ),
                "GatherSoul":(
                        ("DWORD", "ItemID", 1),
                        ("list", "AttrType", 0),
@@ -5840,6 +5849,25 @@
    def GetPosX(self): return self.PosX # 坐标X
    def GetPosY(self): return self.PosY # 坐标Y
# 跨服分区地图逐鹿万界
class IPY_CrossFamilyFlagwarZoneMap():
    def __init__(self):
        self.ZoneID = 0
        self.MapID = 0
        self.DataMapID = 0
        self.CopyMapID = 0
        self.PosX = 0
        self.PosY = 0
        return
    def GetZoneID(self): return self.ZoneID # 分区ID
    def GetMapID(self): return self.MapID # 场景地图ID
    def GetDataMapID(self): return self.DataMapID # 数据地图ID
    def GetCopyMapID(self): return self.CopyMapID # 虚拟线路ID
    def GetPosX(self): return self.PosX # 坐标X
    def GetPosY(self): return self.PosY # 坐标Y
# 聚魂表
class IPY_GatherSoul():
    
@@ -7164,6 +7192,8 @@
        self.ipyCrossPenglaiZoneMapLen = len(self.ipyCrossPenglaiZoneMapCache)
        self.ipyCrossDemonLandZoneMapCache = self.__LoadFileData("CrossDemonLandZoneMap", IPY_CrossDemonLandZoneMap)
        self.ipyCrossDemonLandZoneMapLen = len(self.ipyCrossDemonLandZoneMapCache)
        self.ipyCrossFamilyFlagwarZoneMapCache = self.__LoadFileData("CrossFamilyFlagwarZoneMap", IPY_CrossFamilyFlagwarZoneMap)
        self.ipyCrossFamilyFlagwarZoneMapLen = len(self.ipyCrossFamilyFlagwarZoneMapCache)
        self.ipyGatherSoulCache = self.__LoadFileData("GatherSoul", IPY_GatherSoul)
        self.ipyGatherSoulLen = len(self.ipyGatherSoulCache)
        self.ipyGatherSoulCompoundCache = self.__LoadFileData("GatherSoulCompound", IPY_GatherSoulCompound)
@@ -7782,6 +7812,8 @@
    def GetCrossPenglaiZoneMapByIndex(self, index): return self.ipyCrossPenglaiZoneMapCache[index]
    def GetCrossDemonLandZoneMapCount(self): return self.ipyCrossDemonLandZoneMapLen
    def GetCrossDemonLandZoneMapByIndex(self, index): return self.ipyCrossDemonLandZoneMapCache[index]
    def GetCrossFamilyFlagwarZoneMapCount(self): return self.ipyCrossFamilyFlagwarZoneMapLen
    def GetCrossFamilyFlagwarZoneMapByIndex(self, index): return self.ipyCrossFamilyFlagwarZoneMapCache[index]
    def GetGatherSoulCount(self): return self.ipyGatherSoulLen
    def GetGatherSoulByIndex(self, index): return self.ipyGatherSoulCache[index]
    def GetGatherSoulCompoundCount(self): return self.ipyGatherSoulCompoundLen
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AICommon.py
@@ -223,7 +223,7 @@
#  @param curNPC 当前NPC
#  @return None
#  @remarks 清空NPC伤血列表, 并清空玩家Loading进度条状态
def ClearPlayerPreparing(curNPC, srcPlayer=None):
def ClearPlayerPreparing(curNPC, srcPlayer=None, notifyKey=""):
    curNPC_HurtList = curNPC.GetPlayerHurtList()
    for i in range(curNPC_HurtList.GetHurtCount()):
        hurtValue = curNPC_HurtList.GetHurtAt(i)
@@ -254,7 +254,9 @@
        
        #设置空闲状态
        PlayerControl.ChangePlayerAction(curPlayer, IPY_GameWorld.paNull)
        if notifyKey:
            PlayerControl.NotifyCode(curPlayer, notifyKey, [curNPC.GetNPCID()])
    #清空这个NPC的伤血列表
    curNPC_HurtList.Clear()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -812,7 +812,9 @@
Def_CBT_BattlefieldWScore, # 跨服战场每周积分榜  153
Def_CBT_BattlefieldWScoreLastWeek, # 跨服战场上周积分榜  154
Def_CBT_YaomoBossHurt, # 跨服妖魔boss最新一次伤血排名  155
) = range(150, 155 + 1)
Def_CBT_FamilyFlagwar, # 逐鹿万界 - 单场榜  156
Def_CBT_FamilyFlagwarWeek, # 逐鹿万界 - 周总榜  157
) = range(150, 157 + 1)
#职业对应战力排行榜类型
JobFightPowerBillboardDict = {
@@ -1486,6 +1488,7 @@
CrossServerMsg_ActAllRechargeInfo = "ActAllRechargeInfo"# 跨服全民充值信息
CrossServerMsg_CrossDailyActionState = "CrossDailyActionState" # 跨服日常任务状态信息
CrossServerMsg_CrossYaomoBossHurtInfo = "CrossYaomoBossHurtInfo" # 跨服妖魔boss玩家伤害信息
CrossServerMsg_FamilyFlagwarOver = "FamilyFlagwarOver"  # 逐鹿万界结算信息
# 子服发送跨服信息定义
ClientServerMsg_ServerInitOK = "ServerInitOK"           # 子服启动成功
@@ -1776,7 +1779,8 @@
# 跨服每日活动编号定义, 从150开始
CrossDailyActionIDList = (
CrossDailyActionID_YaomoBoss, # 妖魔boss 150
) = range(150, 150 + 1)
CrossDailyActionID_FamilyWarFlag, # 跨服仙盟夺旗战/逐鹿万界 151
) = range(150, 150 + 2)
# 成就类型定义
SuccessTypeList = (