From 19576609e69aaf5ff7ece05bf007a5f9eb1eaf9a Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期六, 24 十一月 2018 22:15:16 +0800
Subject: [PATCH] 4762 【1.3】【后端】组队功能和助战积分优化;(初版,可测试基本流程)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_NormalNPC.py     |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_NormalNPC.py        |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py                         |   12 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py                                     |    5 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py  |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py                           |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py  |    3 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py                                              |   26 
 ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py                                                            |   38 +
 ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py                                                         |    7 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py                |    5 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py                           |   21 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_QueenRelics.py |   67 +--
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerControl.py                                                |   13 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py                                    |    7 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFBHelpBattle.py                                           |  371 +++++++++++++++++
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py                                                  |   12 
 ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py                                                     |    7 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBHelpBattle.py          |  438 ++++++++++++++++++++
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFriend.py                                                 |    7 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py                |    6 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py                                                 |    5 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_FBHelpBattle.py       |   50 ++
 ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py                                                          |    6 
 PyNetPack                                                                                                             |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                                            |   20 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_20.py                            |   50 ++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py                      |    3 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                                       |   19 
 29 files changed, 1,119 insertions(+), 85 deletions(-)

diff --git a/PyNetPack b/PyNetPack
new file mode 160000
index 0000000..7f19b08
--- /dev/null
+++ b/PyNetPack
@@ -1 +1 @@
-Subproject commit 0000000000000000000000000000000000000000
+Subproject commit 7f19b08a9d54dfcd64e3858a5eebb3132db87fc9
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
index bcff5b0..c3046a7 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
@@ -783,3 +783,41 @@
     fbtAll,             # 8所有
     fbtVsRoom,          # 9PK房间
 ) = range(10)
+
+#VIP特权编号列表, 使用到具体特权时,修改下具体的命名
+VIPPrivilegeList = (
+VIPPrivilege_VIPKillNPCAddAtk, #1 vip法宝-加攻 - 废弃
+VIPPrivilege_2, #2 vip宝石孔-废弃,读功能配置表 GemOpenVip
+VIPPrivilege_TeamVIPBuff, #3 VIPbuff-攻守同盟, 组队属性加成
+VIPPrivilege_BourseTax, #4 交易税减免额度(万分比)
+VIPPrivilege_5, #5 私人秘书(消息推送)
+VIPPrivilege_FreeTransport, #6 免费传送
+VIPPrivilege_7, #7 自动仙盟任务 - 前端
+VIPPrivilege_CollTimeReduceRate, #8 采集大师, 采旗时间减少万分比
+VIPPrivilege_9, #9 百战不死 - 废弃
+VIPPrivilege_FightExpRate,    #10 杀怪经验提升(万分比)
+VIPPrivilege_EatItem,    #11 吞噬装备经验提升(万分比)
+VIPPrivilege_NPCSPRate,    #12 杀怪SP值(万分比)
+VIPPrivilege_MoneyPray,    #13 金币祈愿
+VIPPrivilege_ExpPray, #14 经验祈愿
+VIPPrivilege_15,    #15 个人boss额外次数 - 副本总表统一处理
+VIPPrivilege_BossHome,    #16 boss之家层数权限
+VIPPrivilege_17,    #17 灵宠副本购买次数 - 副本总表统一处理
+VIPPrivilege_18,    #18 洗炼副本购买次数 - 副本总表统一处理
+VIPPrivilege_19,    #19 虚无禁地额外次数 - 副本总表统一处理
+VIPPrivilege_20,    #20 暮光神庙购买次数
+VIPPrivilege_21,    #21 恶魔深渊购买次数 - 副本总表统一处理
+VIPPrivilege_22,    #22 黑暗之门购买次数
+VIPPrivilege_FamilyGoldPack,    #23 仙盟钻石红包
+VIPPrivilege_BoursePwd,    #24 集市上架使用密码
+VIPPrivilege_25,    #25 封魔坛购买次数 - 副本总表统一处理
+VIPPrivilege_BoursePwd,    #26 VIP被动技能孔——VIP4专属被动技能孔
+VIPPrivilege_BOSSHomeKillLimit,    #27 BOSS之家疲劳值上限
+VIPPrivilege_FMTDouble,    #28 封魔坛双倍击杀
+VIPPrivilege_29,    #29 娲皇遗迹购买次数 - 副本总表统一处理
+VIPPrivilege_BindJadeWheel,    #30 绑玉转盘次数
+VIPPrivilege_PrayElixir,    #31 丹药祈福次数
+VIPPrivilege_32,    #32 封魔坛自动挑战
+VIPPrivilege_XianyuanCoinUpperAdd,    #33 仙缘币上限加成
+VIPPrivilege_XianyuanCoinAddPer,    #34 仙缘币获得倍率加成(万分比)
+) = range(1, 35)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
index e1d8960..13df705 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
@@ -65,6 +65,7 @@
 import PlayerGeTui
 import PlayerStore
 import GameWorldActionControl
+import PlayerFBHelpBattle
 import GMT_CTG
 import PyGameData
 import GMShell
@@ -660,6 +661,9 @@
     elif packType == IPY_GameServer.CDBPlayerRefresh_ExAttr10:
         PlayerControl.SetChatBubbleBox(curPlayer, packValue)
         
+    elif packType == IPY_GameServer.CDBPlayerRefresh_ExAttr11:
+        PlayerControl.SetTodayXianyuanCoin(curPlayer, packValue)
+
     elif packType == IPY_GameServer.CDBPlayerRefresh_OperateInfo:
         curPlayer.SetOperateInfo(packValue);
     
@@ -673,7 +677,8 @@
     PlayerTeam.PlayerTeamMemberRefresh(curPlayer, packType, packValue, tick)
     #家族刷新
     PlayerFamily.PlayerRefresh(curPlayer, tick)
-
+    #副本助战
+    PlayerFBHelpBattle.UpdateCheckInPlayerInfoByRefresh(curPlayer, packType, packValue)
     return
 
 ## 玩家切换地图的响应(参数 -> 当前玩家,当前时间)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerControl.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerControl.py
index 8aba631..75f0f65 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerControl.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerControl.py
@@ -202,10 +202,23 @@
 #        return 0
     return curPlayer.GetVIPLv()
 
+## 根据特权ID 和 VIP等级获得特权值
+def GetPrivilegeValue(vipLV, privilegeID):
+    if privilegeID not in ChConfig.VIPPrivilegeList:
+        return 0
+    vipMsg = IpyGameDataPY.GetIpyGameData('VipPrivilege', privilegeID)
+    if not vipMsg:
+        return 0
+    return getattr(vipMsg, 'GetVIP%d' % vipLV)()
+
 ##聊天气泡框
 def GetChatBubbleBox(curPlayer): return curPlayer.GetExAttr10()
 def SetChatBubbleBox(curPlayer, value): return curPlayer.SetExAttr10(value)
 
+##今日已获得仙缘币
+def GetTodayXianyuanCoin(curPlayer): return curPlayer.GetExAttr11()
+def SetTodayXianyuanCoin(curPlayer, value): return curPlayer.SetExAttr11(value)
+
 ## 是否脱机挂机状态
 def GetIsTJG(curPlayer):
     if curPlayer.GetDictByKey(ChConfig.Def_OnlineType):
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFBHelpBattle.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFBHelpBattle.py
new file mode 100644
index 0000000..eb7a34a
--- /dev/null
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFBHelpBattle.py
@@ -0,0 +1,371 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package PlayerFBHelpBattle
+#
+# @todo:副本助战系统
+# @author hxp
+# @date 2018-11-24
+# @version 1.0
+#
+# 详细描述: 副本助战系统
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2018-11-24 22:30"""
+#-------------------------------------------------------------------------------
+
+import PlayerFriend
+import PlayerViewCache
+import IpyGameDataPY
+import PyGameData
+import GameWorld
+
+import random
+import time
+import IPY_GameServer
+import PlayerControl
+import ChConfig
+import ChPyNetSendPack
+import NetPackCommon
+
+MaxRobotID = 100 # 最大机器人NPC定义ID
+    
+## 助战玩家简要信息
+class HelpBattlePlayer():
+    
+    def __init__(self, playerID):
+        self.playerID = playerID
+        self.job = 0
+        self.playerName = ""
+        self.playerLV = 0
+        self.realmLV = 0
+        self.fightPower = 0
+        self.familyID = 0
+        self.vipLV = 0
+        self.checkInCount = 0
+        self.checkInTime = 0
+        self.todayHelpCountDict = {} # 今天已助战次数 {(mapID, lineID):count, ...}, 通用次数时lineID默认为0
+        self.todayXianyuanCoin = 0 # 今日已获得仙缘币
+        return
+
+## 助战记录 - 目前只记录未通知玩家的助战信息
+class FBHelpBattleRecord():
+    
+    def __init__(self):
+        self.callPlayerID = 0 # 召唤他的玩家ID
+        self.callPlayerName = ""
+        self.mapID = 0
+        self.funcLineID = 0
+        self.xianyuanCoinAdd = 0 # 0代表到达上限
+        self.relation = 0 # 当时的关系
+        self.vipLV = 0 # 当时的VIP等级
+        self.recordTime = 0 # 记录的时间
+        return
+    
+## 是否在助战登记列表里
+def IsInHelpBattleCheckInList(playerID): return playerID in PyGameData.g_fbHelpBattleCheckInPlayerDict
+
+def MapServer_FBHelpBattle(curPlayer, msgList):
+    ## 地图玩家请求助战相关操作
+    GameWorld.DebugLog("MapServer_FBHelpBattle %s" % str(msgList), curPlayer.GetPlayerID())
+    if not msgList:
+        return ""
+    
+    cmd = msgList[0]
+    result = []
+    
+    # 登记
+    if cmd == "CheckIn":
+        result = __DoPlayerFBHelpBattleCheckIn(curPlayer, msgList)
+        
+    # 刷新助战列表    
+    elif cmd == "Refresh":
+        result = __DoFBHelpBattleRefresh(curPlayer, msgList)
+        
+    # 召唤
+    elif cmd == "Call":
+        result = __DoFBHelpBattleCall(curPlayer, msgList)
+    
+    if result == None:
+        return
+    
+    return msgList + result
+
+def __DoPlayerFBHelpBattleCheckIn(curPlayer, msgList):
+    ## 玩家登记
+    checkInCount, fightPower = msgList[1:]
+    curTime = int(time.time())
+    playerID = curPlayer.GetPlayerID()
+    curCache = PlayerViewCache.ViewCacheMgr.FindCache(playerID)
+    haveViewCache = 1 if curCache else 0
+    todayXianyuanCoin = PlayerControl.GetTodayXianyuanCoin(curPlayer)
+    
+    helpBattlePlayer = PyGameData.g_fbHelpBattleCheckInPlayerDict.get(playerID)
+    if not helpBattlePlayer:
+        helpBattlePlayer = HelpBattlePlayer(playerID)
+        PyGameData.g_fbHelpBattleCheckInPlayerDict[playerID] = helpBattlePlayer
+    helpBattlePlayer.playerName = curPlayer.GetName()
+    helpBattlePlayer.playerLV = curPlayer.GetLV()
+    helpBattlePlayer.job = curPlayer.GetJob()
+    helpBattlePlayer.realmLV = curPlayer.GetOfficialRank()
+    helpBattlePlayer.fightPower = fightPower
+    helpBattlePlayer.familyID = curPlayer.GetFamilyID()
+    helpBattlePlayer.vipLV = curPlayer.GetVIPLv()
+    helpBattlePlayer.checkInCount = checkInCount + 1
+    helpBattlePlayer.checkInTime = curTime
+    helpBattlePlayer.todayXianyuanCoin = todayXianyuanCoin
+    
+    isOK = 1 # 默认成功
+    GameWorld.Log("玩家助战登记: playerLV=%s,fightPower=%s,familyID=%s,vipLV=%s,todayXianyuanCoin=%s,checkInCount=%s,haveViewCache=%s" 
+                  % (curPlayer.GetLV(), fightPower, curPlayer.GetFamilyID(), curPlayer.GetVIPLv(), todayXianyuanCoin, checkInCount + 1, haveViewCache), playerID)
+    return [isOK, haveViewCache]
+
+def UpdateCheckInPlayerInfo(playerID, fightPower, familyID):
+    ## 更新登记的助战玩家等级战力
+    if playerID not in PyGameData.g_fbHelpBattleCheckInPlayerDict:
+        return
+    helpBattlePlayer = PyGameData.g_fbHelpBattleCheckInPlayerDict[playerID]
+    helpBattlePlayer.fightPower = fightPower
+    helpBattlePlayer.familyID = familyID
+    GameWorld.DebugLog("更新助战玩家等级战力: fightPower=%s,familyID=%s" % (fightPower, familyID), playerID)
+    return
+
+def UpdateCheckInPlayerInfoByRefresh(curPlayer, refreshType, value):
+    ## 更新登记的助战玩家仙盟ID
+    playerID = curPlayer.GetPlayerID()
+    if playerID not in PyGameData.g_fbHelpBattleCheckInPlayerDict:
+        return
+    helpBattlePlayer = PyGameData.g_fbHelpBattleCheckInPlayerDict[playerID]
+    if refreshType == IPY_GameServer.CDBPlayerRefresh_LV:
+        helpBattlePlayer.playerLV = value
+    elif refreshType == IPY_GameServer.CDBPlayerRefresh_ExAttr11:
+        helpBattlePlayer.todayXianyuanCoin = value        
+    elif refreshType == IPY_GameServer.CDBPlayerRefresh_VIPLv:
+        helpBattlePlayer.vipLV = value
+    else:
+        return
+    GameWorld.DebugLog("更新助战玩家信息: refreshType=%s,value=%s" % (refreshType, value), playerID)
+    return
+
+def __DoFBHelpBattleRefresh(curPlayer, msgList):
+    ## 助战列表刷新
+    mapID, funcLineID, isClientRefresh, costMoneyList, calledPlayerIDDict = msgList[1:]
+    
+    helpBattlePlayerDict = {} # 同步给地图服务器的待选助战玩家列表信息
+    
+    ipyData = IpyGameDataPY.GetIpyGameData("FBHelpBattle", mapID, funcLineID)
+    if not ipyData:
+        return [helpBattlePlayerDict]
+    
+    fightPowerMin = ipyData.GetFightPowerMin()
+    fightPowerMax = ipyData.GetFightPowerMax()
+    limitLV = ipyData.GetLVLimit()
+    dayFreeHelpCountInfo = ipyData.GetDayFreeHelpCount() # 每日免费助战次数,[每日免费助战次数, 是否所有层通用]
+    dayFreeHelpCount = 0 # 0为无限制次数
+    helpCountLineID = funcLineID # 助战次数所属lineID,当所有层通用时,默认为0
+    if dayFreeHelpCountInfo and len(dayFreeHelpCountInfo) == 2:
+        dayFreeHelpCount, isAllLineCount = dayFreeHelpCountInfo
+        if isAllLineCount:
+            helpCountLineID = 0
+    helpCountKey = (mapID, helpCountLineID)
+    
+    playerID = curPlayer.GetPlayerID()
+    GameWorld.Log("刷新助战列表: mapID=%s,funcLineID=%s,helpCountLineID=%s,isClientRefresh=%s,costMoneyList=%s,calledPlayerIDDict=%s" 
+                  % (mapID, funcLineID, helpCountLineID, isClientRefresh, costMoneyList, calledPlayerIDDict), playerID)
+    
+    goldCallCount = 0
+    
+    #已经召唤的保留
+    for calledPlayerID, callInfo in calledPlayerIDDict.items():
+        needGoldCall, job, relation = callInfo
+        # 玩家镜像
+        if calledPlayerID in PyGameData.g_fbHelpBattleCheckInPlayerDict:
+            helpBattlePlayer = PyGameData.g_fbHelpBattleCheckInPlayerDict[calledPlayerID]
+            helpBattlePlayerDict[calledPlayerID] = __GetNotifyMapServerHelpPlayerInfoDict(helpBattlePlayer, needGoldCall, job, relation)
+            if needGoldCall:
+                goldCallCount += 1
+        # 机器人NPC
+        elif 1 <= calledPlayerID <= MaxRobotID:
+            helpBattlePlayerDict[calledPlayerID] = __GetNotifyMapServerHelpPlayerInfoDict(None, False, job)
+        else:
+            GameWorld.ErrLog("已召唤的助战玩家找不到镜像缓存!理论上不存在该情况,镜像缓存释放会比登记有效时长多半小时!")
+            continue
+            
+    if helpBattlePlayerDict:
+        GameWorld.Log("已召唤的助战: %s" % str(helpBattlePlayerDict), playerID)
+        
+    curTime = int(time.time())
+    maxHelpPlayerSelectCount =  IpyGameDataPY.GetFuncCfg("HelpBattleCall", 1) # 最大可以选择助战的玩家个数
+    maxGoldHelpPlayerCount = IpyGameDataPY.GetFuncCfg("HelpBattleCall", 3) # 最大付费召唤人数
+    checkInValidHours = IpyGameDataPY.GetFuncCfg("HelpBattleCheckIn", 1) # 登记有效时长,小时
+    checkInValidSeconds = checkInValidHours * 3600
+    
+    checkInPlayerIDList = PyGameData.g_fbHelpBattleCheckInPlayerDict.keys()
+    random.shuffle(checkInPlayerIDList) # 刷新纯随机
+    GameWorld.Log("    登记助战人数=%s" % (len(checkInPlayerIDList)), playerID)
+    for checkInPlayerID in checkInPlayerIDList:
+        if checkInPlayerID == playerID:
+            GameWorld.DebugLog("    自己不处理, checkInPlayerID=%s" % checkInPlayerID)
+            continue
+        if checkInPlayerID in helpBattlePlayerDict:
+            GameWorld.DebugLog("    已经在助战里的不处理, checkInPlayerID=%s" % checkInPlayerID)
+            continue
+        if len(helpBattlePlayerDict) >= maxHelpPlayerSelectCount:
+            GameWorld.DebugLog("    超过最大个数了不处理, checkInPlayerID=%s" % checkInPlayerID)
+            break
+        helpBattlePlayer = PyGameData.g_fbHelpBattleCheckInPlayerDict[checkInPlayerID]
+        checkInPlayerLV = helpBattlePlayer.playerLV
+        checkInPlayerFightPower = helpBattlePlayer.fightPower
+        checkInTime = helpBattlePlayer.checkInTime
+        if checkInPlayerLV < limitLV:
+            GameWorld.DebugLog("    等级不足, checkInPlayerID=%s,checkInPlayerLV=%s < limitLV=%s" % (checkInPlayerID, checkInPlayerLV, limitLV))
+            continue
+        if fightPowerMin and checkInPlayerFightPower < fightPowerMin:
+            GameWorld.DebugLog("    战力不足, checkInPlayerID=%s,checkInPlayerFightPower=%s < fightPowerMin=%s" % (checkInPlayerID, checkInPlayerFightPower, fightPowerMin))
+            continue
+        if fightPowerMax and checkInPlayerFightPower > fightPowerMax:
+            GameWorld.DebugLog("    战力超出, checkInPlayerID=%s,checkInPlayerFightPower=%s > fightPowerMax=%s" % (checkInPlayerID, checkInPlayerFightPower, fightPowerMax))
+            continue
+        passTime = curTime - checkInTime
+        if passTime > checkInValidSeconds:
+            GameWorld.DebugLog("    登记超时, checkInPlayerID=%s,checkInTime=%s,passTime=%s > checkInValidSeconds=%s" % (checkInPlayerID, checkInTime, passTime, checkInValidSeconds))
+            continue
+        needGoldCall = False
+        if dayFreeHelpCount:
+            todayHelpCount = helpBattlePlayer.todayHelpCountDict.get(helpCountKey, 0)
+            needGoldCall = todayHelpCount >= dayFreeHelpCount
+            if needGoldCall and goldCallCount >= maxGoldHelpPlayerCount:
+                GameWorld.DebugLog("    超过最大付费召唤人数, checkInPlayerID=%s,goldCallCount=%s > maxGoldHelpPlayerCount=%s" % (checkInPlayerID, goldCallCount, maxGoldHelpPlayerCount))
+                continue
+            goldCallCount += 1
+        relation = __GetHelpBattleRelation(curPlayer, helpBattlePlayer)
+        helpBattlePlayerDict[checkInPlayerID] = __GetNotifyMapServerHelpPlayerInfoDict(helpBattlePlayer, needGoldCall, helpBattlePlayer.job, relation)
+        
+    # 不足的机器人NPC补足
+    openJobList = IpyGameDataPY.GetFuncEvalCfg("OpenJob", 1) # 开放的职业
+    lackCount = maxHelpPlayerSelectCount - len(helpBattlePlayerDict)
+    robotID = 0 # 机器人NPC定义ID从1开始
+    while lackCount > 0 and robotID < MaxRobotID:
+        robotID += 1
+        if robotID in helpBattlePlayerDict:
+            continue
+        lackCount -= 1
+        randJob = random.choice(openJobList)
+        helpBattlePlayerDict[robotID] = __GetNotifyMapServerHelpPlayerInfoDict(None, False, randJob)
+    
+    GameWorld.Log("    helpBattlePlayerDict=%s" % (helpBattlePlayerDict), playerID)
+    return [helpBattlePlayerDict]
+
+def __GetHelpBattleRelation(curPlayer, helpBattlePlayer):
+    ## 获取助战社交关系 0-无,1-好友,2-盟友
+    if not helpBattlePlayer:
+        return 0
+    playerID = curPlayer.GetPlayerID()
+    tagPlayerID = helpBattlePlayer.playerID
+    tagFamilyID = helpBattlePlayer.familyID
+    relationList = IpyGameDataPY.GetFuncEvalCfg("HelpBattlePoint", 3, []) # 社交关系优先级
+    for checkRelation in relationList:
+        if checkRelation == 1:
+            if PlayerFriend.IsFriend(playerID, tagPlayerID):
+                return checkRelation
+        if checkRelation == 2:
+            if curPlayer.GetFamilyID() == tagFamilyID:
+                return checkRelation
+    return 0
+
+def __GetNotifyMapServerHelpPlayerInfoDict(helpBattlePlayer, needGoldCall, job, relation=0):
+    ## 获取同步给地图的助战玩家简要信息
+    helpPlayerDict = {"Job":job}
+    if not helpBattlePlayer:
+        return helpPlayerDict
+    if needGoldCall:
+        helpPlayerDict["NeedGoldCall"] = 1
+    helpPlayerDict["Name"] = helpBattlePlayer.playerName
+    helpPlayerDict["LV"] = helpBattlePlayer.playerLV
+    #helpPlayerDict["Job"] = helpBattlePlayer.job
+    helpPlayerDict["RealmLV"] = helpBattlePlayer.realmLV
+    helpPlayerDict["FightPower"] = helpBattlePlayer.fightPower
+    helpPlayerDict["Relation"] = relation
+    return helpPlayerDict
+
+def __DoFBHelpBattleCall(curPlayer, msgList):
+    ''' 助战召唤,不管最终过关与否,被召唤方都直接算助战成功,这里处理被召唤的,主动方在地图直接处理
+    '''
+    mapID, funcLineID, calledPlayerDict = msgList[1:]
+    fbFuncIpyData = IpyGameDataPY.GetIpyGameData("FBFunc", mapID)
+    fbHelpIpyData = IpyGameDataPY.GetIpyGameData("FBHelpBattle", mapID, funcLineID)
+    if not fbFuncIpyData or not fbHelpIpyData or not calledPlayerDict:
+        return
+    
+    curTime = int(time.time())
+    tagPlayerID = curPlayer.GetPlayerID()
+    tagPlayerName = curPlayer.GetName()
+    playerMgr = GameWorld.GetPlayerManager()
+    xianyuanCoinUpper = IpyGameDataPY.GetFuncCfg("HelpBattlePoint", 1) # 每日仙缘币上限
+    baseHelpPoint = fbFuncIpyData.GetHelpPoint() # 助战 - 基础仙缘币
+    relationCoinAddDict = IpyGameDataPY.GetFuncEvalCfg("HelpBattlePoint", 2, {}) # 社交关系加成 {"社交关系":[过关加成, 助战加成], ...} 
+    GameWorld.DebugLog("召唤助战: mapID=%s, funcLineID=%s, calledPlayerDict=%s" % (mapID, funcLineID, calledPlayerDict), tagPlayerID)
+    
+    for calledPlayerID, relation in calledPlayerDict.items():
+        if calledPlayerID not in PyGameData.g_fbHelpBattleCheckInPlayerDict:
+            continue
+        addCoinRate = 10000 # 基础倍率
+        playerXianyuanCoinUpper = xianyuanCoinUpper
+        relationAddList = relationCoinAddDict.get(str(relation), [])
+        relationAdd = relationAddList[1] if len(relationAddList) == 2 else 0
+        
+        helpBattlePlayer = PyGameData.g_fbHelpBattleCheckInPlayerDict[calledPlayerID]
+        todayXianyuanCoin = helpBattlePlayer.todayXianyuanCoin # 今日已获得仙缘币
+        vipLV = helpBattlePlayer.vipLV
+        if vipLV:
+            xianyuanCoinUpperAdd = PlayerControl.GetPrivilegeValue(vipLV, ChConfig.VIPPrivilege_XianyuanCoinUpperAdd)
+            xianyuanCoinAddPer = PlayerControl.GetPrivilegeValue(vipLV, ChConfig.VIPPrivilege_XianyuanCoinAddPer)
+            
+            playerXianyuanCoinUpper += xianyuanCoinUpperAdd
+            addCoinRate += xianyuanCoinAddPer
+            
+        # 仙缘币公式=(通关仙缘币或助战仙缘币+社交关系加成)*VIP倍数
+        coinAdd = int((baseHelpPoint + relationAdd) * addCoinRate / 10000.0)
+        canAddMax = max(playerXianyuanCoinUpper - todayXianyuanCoin, 0)
+        coinAddReal = min(coinAdd, canAddMax) # 实际加仙缘币
+        GameWorld.DebugLog("    助战增加仙缘币: coinAddReal=%s" % (coinAddReal), calledPlayerID)
+        
+        # GameServer 直接先加
+        helpBattlePlayer.todayXianyuanCoin += coinAddReal
+        
+        helpRecord = FBHelpBattleRecord()
+        helpRecord.callPlayerID = tagPlayerID # 召唤他的玩家ID
+        helpRecord.callPlayerName = tagPlayerName
+        helpRecord.mapID = mapID
+        helpRecord.funcLineID = funcLineID
+        helpRecord.xianyuanCoinAdd = coinAddReal
+        helpRecord.relation = relation
+        helpRecord.vipLV = vipLV
+        helpRecord.recordTime = curTime
+        
+        calledPlayer = playerMgr.FindPlayerByID(calledPlayerID)
+        # 非脱机在线直接通知地图
+        if calledPlayer and not PlayerControl.GetIsTJG(calledPlayer):
+            SendMapServer_FBHelpBattleRecord(calledPlayer, [helpRecord])
+        else:
+            unNotifyRecordList = PyGameData.g_fbHelpBattleRecord.get(calledPlayerID, [])
+            unNotifyRecordList.append(helpRecord)
+            PyGameData.g_fbHelpBattleRecord[calledPlayerID] = unNotifyRecordList
+            
+    return
+
+def SendMapServer_FBHelpBattleRecord(curPlayer, syncHelpRecordList):
+    if not syncHelpRecordList:
+        return
+    
+    helpRecordList = []
+    for record in syncHelpRecordList:
+        helpRecordList.append([record.callPlayerID, record.callPlayerName, record.mapID, record.funcLineID, 
+                               record.xianyuanCoinAdd, record.relation, record.vipLV, record.recordTime])
+    addXianyuanCoinMsg = str(["HelpRecord", helpRecordList])
+    curPlayer.MapServer_QueryPlayerResult(0, 0, 'FBHelpBattle', addXianyuanCoinMsg, len(addXianyuanCoinMsg))
+    GameWorld.DebugLog("    MapServer_QueryPlayerResult %s" % addXianyuanCoinMsg, curPlayer.GetPlayerID())
+    return
+
+
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
index 2c00e8b..c0f96d8 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
@@ -348,7 +348,8 @@
     
     # 玩家战盟名变更处理
     __OnFamilyNameChange(jionPlayer.GetPlayerID(), curFamily.GetName())
-    
+    #玩家缓存
+    PlayerViewCache.OnPlayerFamilyChange(jionPlayer.GetPlayerID(), curFamily.GetID(), curFamily.GetName())
     #加入仙盟联赛成员
     GameWorldFamilyWar.AddFamilyWarMem(jionPlayer.GetPlayerID(), curFamily.GetID())
     GameWorldFamilyWar.CheckPlayerJoinFamilyWarInfo(jionPlayer)
@@ -1524,7 +1525,7 @@
     # 玩家战盟名变更处理
     __OnFamilyNameChange(leavePlayerID, '')
     AddFamilyIDToFightPowerChangeList(curFamily.GetID())
-    PlayerViewCache.OnPlayerLeaveFamily(leavePlayerID)
+    PlayerViewCache.OnPlayerFamilyChange(leavePlayerID, 0, "")
     return
 
 #//////////////////////////////////////////////////////////////
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFriend.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFriend.py
index 62c7c82..d88e04a 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFriend.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFriend.py
@@ -147,6 +147,13 @@
     
     return PyDataManager.GetFriendManager().AddFriends(playerID)
 
+def IsFriend(playerID, tagPlayerID):
+    ## 判断双方是否好友
+    curFriends = PyDataManager.GetFriendManager().GetFriends(playerID)
+    if not curFriends:
+        return False
+    return curFriends.Find(tagPlayerID) != None
+
 #---------------------------------------------------------------------
 ##好友检查
 # @param curPlayer 申请好友的玩家
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
index 219378c..e5909fc 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
@@ -60,6 +60,7 @@
 #import PlayerFamilyTech
 import MergeChildMsg
 import PlayerFamilyRedPacket
+import PlayerFBHelpBattle
 import PlayerFamilyStore
 import PlayerFamilySWRH
 import GameWorldProcess
@@ -843,6 +844,17 @@
 #        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(srcPlayerID)
 #        resultName = '%s' % PlayerFamilyTech.OnQuery_PlayerFamilyTechLVUP(curPlayer, eval(resultName))
     
+    
+    # 副本助战
+    if callName =="FBHelpBattle":
+        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(srcPlayerID)
+        if not curPlayer:
+            return
+        ret = PlayerFBHelpBattle.MapServer_FBHelpBattle(curPlayer, eval(resultName))
+        if ret == None:
+            return
+        resultName = '%s' % ret
+        
     # 战盟仓库
     if callName == "FamilyStore":
         curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(srcPlayerID)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
index e94851e..731364b 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
@@ -26,6 +26,8 @@
 import ReadChConfig
 import PlayerFamily
 import IPY_GameServer
+import PlayerFBHelpBattle
+import IpyGameDataPY
 import ShareDefine
 import ChConfig
 
@@ -81,6 +83,13 @@
         curCache.SetPlusDataNoSave(PlusData,len(PlusData))
     curCache.SetNeedSaveDB(isSaveDB) #设置需要保存到数据库
     
+    # 同步更新助战信息
+    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(PlayerID):
+        PropDataDict = eval(PropData)
+        fightPower = PropDataDict.get("FightPower", 0)
+        familyID = PropDataDict.get("FamilyID", 0)
+        PlayerFBHelpBattle.UpdateCheckInPlayerInfo(PlayerID, fightPower, familyID)
+        
     #暂时关闭
     #===========================================================================
     # FamilyIDKey = "FamilyID"
@@ -109,7 +118,10 @@
 #  @param PlayerID, PlayerLV
 #  @return None
 def IsNeedSaveLogoutPlayer(PlayerID, PlayerLV):
-    SaveDBLimitLV, NeedCheckBillBoardType, HighLadderLimitOrder = ReadChConfig.GetEvalChConfig("CacheSaveLimit")
+    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(PlayerID):
+        return True
+    
+    SaveDBLimitLV = IpyGameDataPY.GetFuncCfg("PlayerViewCache", 1)
     #校验玩家等级
     if PlayerLV < SaveDBLimitLV:
         return False
@@ -118,7 +130,9 @@
 
 # 上榜用户
 def IsNeedSaveViewCacheAllInfo(PlayerID):
-    SaveDBLimitLV, NeedCheckBillBoardType, HighLadderLimitOrder = ReadChConfig.GetEvalChConfig("CacheSaveLimit")
+    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(PlayerID):
+        return True
+    NeedCheckBillBoardType = IpyGameDataPY.GetFuncEvalCfg("PlayerViewCache", 2)
     #校验玩家是否上排行榜
     billboardMgr = GameWorld.GetBillboard()
     for BillBoardType in NeedCheckBillBoardType:
@@ -282,14 +296,14 @@
     NetPackCommon.SendFakePack(curPlayer, answerPack)
     return
 
-def OnPlayerLeaveFamily(playerID):
-    GameWorld.DebugLog("ViewCache->OnPlayerLeaveFamily", playerID)
+def OnPlayerFamilyChange(playerID, familyID, familyName):
+    GameWorld.DebugLog("ViewCache->OnPlayerFamilyChange", playerID)
     curCache = ViewCacheMgr.FindCache(playerID)
     if not curCache:
         return
     PropData = eval(curCache.GetPropData())
-    PropData["FamilyID"] = 0
-    PropData["FamilyName"] = ""
+    PropData["FamilyID"] = familyID
+    PropData["FamilyName"] = familyName
     playerLV = PropData["LV"]
     
     PropData = json.dumps(PropData, ensure_ascii=False)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py b/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
index 94c1825..2cfbdd0 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
@@ -78,4 +78,8 @@
 g_partyheroAnswerDict = {} #仙盟宴会玩家答题数量 {playerid:答题数量,..}
 
 g_dogzNPCRefreshTimeDict = {} # 神兽副本NPC刷新时间{npcid:剩余刷新时间}
-g_bourseItemTradingTimeDict = {} # 物品开始交易的时间记录 {guid:tick}
\ No newline at end of file
+g_bourseItemTradingTimeDict = {} # 物品开始交易的时间记录 {guid:tick}
+
+g_fbHelpBattleCheckInPlayerDict = {} # 副本助战玩家登记缓存
+g_fbHelpBattleRecord = {} # 未同步的副本助战记录
+
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
index 37ba082..bf7a904 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
@@ -759,7 +759,7 @@
 CDBPlayerRefresh_RealmPoint,            # 境界修炼点
 CDBPlayerRefresh_Ysog,                  # 魔精
 CDBPlayerRefresh_FamilyActivity,        # 仙盟活跃令
-CDBPlayerRefresh_FBHelpPoint,           # 副本助战积分
+CDBPlayerRefresh_Xianyuancoin,          # 仙缘币
 CDBPlayerRefresh_PKState,               # 战斗状态 175
 CDBPlayerRefresh_BossState,             # boss状态
 CDBPlayerRefresh_BaseAtkMin,            # 基础最小攻击
@@ -789,7 +789,8 @@
 TYPE_Price_RealmPoint = 13    # 境界修炼点
 TYPE_Price_Ysog = 14    # 魔精
 TYPE_Price_FamilyActivity = 15    # 仙盟活跃令
-TYPE_Price_FBHelpPoint = 16    # 副本助战积分
+TYPE_Price_FBHelpPoint = 16    # 副本助战积分, 废弃
+TYPE_Price_XianyuanCoin = 17    # 仙缘币
 TYPE_Price_Rune = 23    # 符印精华点
 TYPE_Price_RuneSplinters = 24    # 符印碎片
 TYPE_Price_TreasureScore = 25    # 寻宝积分
@@ -820,7 +821,7 @@
                            TYPE_Price_RealmPoint:CDBPlayerRefresh_RealmPoint,
                            TYPE_Price_Ysog:CDBPlayerRefresh_Ysog,
                            TYPE_Price_FamilyActivity:CDBPlayerRefresh_FamilyActivity,
-                           TYPE_Price_FBHelpPoint:CDBPlayerRefresh_FBHelpPoint,
+                           TYPE_Price_XianyuanCoin:CDBPlayerRefresh_Xianyuancoin,
                            TYPE_Price_Danjing:CDBPlayerRefresh_Danjing,
                            }
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index a164022..ebf8152 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -1131,6 +1131,26 @@
 PacketSubCMD_1=0x75
 PacketCallFunc_1=BuyFBEnterCount
 
+;副本助战
+[FBHelpBattle]
+ScriptName = GameWorldLogic\FBProcess\FBHelpBattle.py
+Writer = hxp
+Releaser = hxp
+RegType = 0
+RegisterPackCount = 3
+
+PacketCMD_1=0xB1
+PacketSubCMD_1=0x5
+PacketCallFunc_1=OnHelpBattleCheckIn
+
+PacketCMD_2=0xB1
+PacketSubCMD_2=0x6
+PacketCallFunc_2=OnHelpBattleCall
+
+PacketCMD_3=0xB1
+PacketSubCMD_3=0x7
+PacketCallFunc_3=OnHelpBattleRefresh
+
 ;组队
 [PlayerTeam]
 ScriptName = Player\PlayerTeam.py
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
index 129f1b8..5666adf 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
@@ -1578,6 +1578,11 @@
         elif defObj.GetGameNPCObjType() == IPY_GameWorld.gnotTruck:
             remainHP = max(PlayerTruck.GetTruckDestroyMinHP(defObj), remainHP)
             GameObj.SetHP(defObj, remainHP)
+        
+        elif defObj.GetType() == ChConfig.ntHelpBattleRobot:
+            remainHP = min(dHP, max(GameObj.GetMaxHP(defObj)/2, remainHP)) # 助战机器人剩余血量不能少于一半
+            GameObj.SetHP(defObj, remainHP)
+            
         else:
             #防守方是怪物NPC,只扣其血
             GameObj.SetHP(defObj, remainHP)
@@ -2470,7 +2475,6 @@
     
     #---NPC处理---
     if not ChNPC.OnCheckCanDie(atkObj, curObjDetel, curSkill, tick):
-        GameObj.SetHP(curObjDetel, 1)
         return
     
     npcControl = NPCCommon.NPCControl(curObjDetel)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py
index d7e67ba..f488237 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py
@@ -100,7 +100,8 @@
     
     #普通NPC
     if GameObj.GetHP(defender) <= 0:
-        
+        if not ChNPC.OnCheckCanDie(attacker, defender, skill, tick):
+            return
         #副本
         FBLogic.DoFB_Npc_KillNPC(attacker, defender, tick)
             
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_NormalNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_NormalNPC.py
index fabd3ff..be3835c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_NormalNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_NormalNPC.py
@@ -111,7 +111,6 @@
         curPlayer = PetControl.GetPetOwner(curPet)  # 宠物主人
         
         if not ChNPC.OnCheckCanDie(curPlayer, curTagNPC, skill, tick):
-            GameObj.SetHP(curTagNPC, 1)
             return
         
         if curPlayer != None:
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_NormalNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_NormalNPC.py
index ae6aa90..2465e39 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_NormalNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_NormalNPC.py
@@ -111,7 +111,6 @@
     
     if GameObj.GetHP(curTagNormalNPC) <= 0:
         if not ChNPC.OnCheckCanDie(curPlayer, curTagNormalNPC, skill, tick):
-            GameObj.SetHP(curTagNormalNPC, 1)
             return
         #执行击杀NPC逻辑
         FBLogic.DoFB_Player_KillNPC(curPlayer , curTagNormalNPC , tick)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py
index 5d3bdc6..229ccf4 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py
@@ -144,7 +144,6 @@
     curPlayer = NPCCommon.GetSummonNPCOwner(IPY_GameWorld.gotPlayer, curSummonNPC)
     
     if not ChNPC.OnCheckCanDie(curPlayer, curTagNPC, skill, tick):
-        GameObj.SetHP(curTagNPC, 1)
         return
     
     #2011-05-12 chenxuewei 有没主人都只要通知一次即可,避免重复通知
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index e30008f..c95a629 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1877,10 +1877,14 @@
 Def_FB_DropDoCountRate = 'DropDoCountRate' # 怪物掉落执行次数万分率
 Def_FB_DropDoCountAdd = 'DropDoCountAdd' # 怪物掉落执行次数加成固定值
 Def_FB_SingleFBPlayerID = 'SingleFBPlayerID' # 个人副本玩家ID
+Def_FB_HelpBattleFBObjID = 'HelpBattleFBObjID_%s' # 助战副本助战NPC机器人/玩家ID对应实例ID, 参数为玩家ID
+Def_FB_HelpBattleGoldCall = 'HelpBattleGoldCall_%s' # 助战机器人是否花费召唤, 参数为玩家ID
 
 FBPlayerDict_EncourageLV = 'FBPlayerDict_EncourageLV'   # 鼓舞等级
 FBPlayerDict_IsDelTicket = 'FBPlayerDict_IsDelTicket'   # 是否已扣除入场券/进入次数
 FBPlayerDict_IsHelpFight = 'FBPlayerDict_IsHelpFight' # 是否助战
+
+FBPD_HelpBattleRefreshCount = 'FBPD_HelpBattleRefreshCount' # 助战已刷新次数
 
 #领取奖励标识
 (
@@ -3662,6 +3666,9 @@
 Def_PDict_IceLodeLastCheckTime = "IceLodeLastCheckTime" #上次补发星级奖励时间
 Def_PDict_IceLodeDayLV = "IceLodeDayLV" #今日等级
 Def_PDict_IceLodeIsInFBOnDay = "IceLodeIsInFBOnDay" #在副本里过天
+
+#助战
+Def_PDict_HelpBattleCheckInCount = "HelpBattleCheckInCount" #助战登记次数, 登记次数*10+今日是否登记过
 #-------------------------------------------------------------------------------
 #类型 Def_PDictType_OnlinePrize
 Def_PDict1_OnlinePrizeCnt = "OnlinePrizeCnt"  # 新手在线已领取奖励次数
@@ -4530,6 +4537,7 @@
 Def_Cost_BindJadeWheel, # 绑玉转盘
 Def_Cost_WishingWell, # 许愿池刷新
 Def_Cost_GodWeapon, # 神兵
+Def_Cost_FBHelpBattle, # 副本助战
 #-----------以下为暂时没用的,先不删除,如有新增消费点则放在这些之前------------
 Def_Cost_RefreshArrestTask, # 刷新悬赏任务
 Def_Cost_OffLineExp, # 兑换离线经验
@@ -4550,7 +4558,7 @@
 Def_Cost_Trade, # 交易
 Def_Cost_Rename, # 改名
 Def_Cost_SkillLvUp, # 技能升级
-) = range(2000, 2000 + 58)
+) = range(2000, 2000 + 59)
 
 Def_Cost_Reason_SonKey = "reason_name_son" # 消费点原因子类说明key
 
@@ -4636,6 +4644,7 @@
 Def_Cost_BindJadeWheel:"BindJadeWheel",
 Def_Cost_WishingWell:"WishingWell",
 Def_Cost_GodWeapon:"GodWeapon",
+Def_Cost_FBHelpBattle:"FBHelpBattle",
 }
 ## -----------------------------------------------------
 
@@ -4934,7 +4943,10 @@
 VIPPrivilege_29,    #29 娲皇遗迹购买次数 - 副本总表统一处理
 VIPPrivilege_BindJadeWheel,    #30 绑玉转盘次数
 VIPPrivilege_PrayElixir,    #31 丹药祈福次数
-) = range(1, 32)
+VIPPrivilege_32,    #32 封魔坛自动挑战
+VIPPrivilege_XianyuanCoinUpperAdd,    #33 仙缘币上限加成
+VIPPrivilege_XianyuanCoinAddPer,    #34 仙缘币获得倍率加成(万分比)
+) = range(1, 35)
 
 
 (
@@ -4958,8 +4970,9 @@
 ntTouchKill, #触碰后自杀类 17
 ntUndeath, #不死类型 18
 ntRobot, #上古战场机器人类型 19
+ntHelpBattleRobot, #助战机器人 20
 ntMax
-) = range(21)
+) = range(22)
 
 
 (Def_SkillFuncType_Common, #0为通用技能
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
index aff2794..24fb0bf 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -535,6 +535,18 @@
         callFunc(curPlayer, tick)
     return
 
+def OnCallHelpBattleOK(curPlayer, tick):
+    ## 召唤助战完成
+    do_FBLogic_ID = __GetFBLogic_MapID(GameWorld.GetMap().GetMapID())
+    
+    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnCallHelpBattleOK"))
+    
+    if callFunc != None:
+        GameWorld.Log("OnCallHelpBattleOK...", curPlayer.GetPlayerID())
+        callFunc(curPlayer, tick)
+        
+    return
+
 def InitFBNPCStrengthenData(curPlayer, gameMap):
     #副本NPC成长相关数据在 DoEnterFB 之前初始化
     gameFB = GameWorld.GetGameFB()
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBHelpBattle.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBHelpBattle.py
new file mode 100644
index 0000000..9158120
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBHelpBattle.py
@@ -0,0 +1,438 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GameWorldLogic.FBProcess.FBHelpBattle
+#
+# @todo:副本助战系统
+# @author hxp
+# @date 2018-11-24
+# @version 1.0
+#
+# 详细描述: 副本助战系统
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2018-11-24 22:30"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import ChConfig
+import PlayerControl
+import ChPyNetSendPack
+import NetPackCommon
+import PlayerViewCacheTube
+import FBCommon
+import IpyGameDataPY
+import ShareDefine
+import PyGameData
+import random
+import GameMap
+import NPCCommon
+import FBLogic
+import PlayerVip
+
+def DoPlayerOnDay(curPlayer):
+    checkInInfo = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HelpBattleCheckInCount)
+    checkInCount = checkInInfo / 10 # 累计登记
+    checkInInfo = (checkInCount + 1) * 10 + 0
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HelpBattleCheckInCount, checkInInfo)
+    return
+
+def GameServer_FBHelpBattleResult(curPlayer, msgList, tick):
+    ## GameServer结果返回处理
+    
+    if not msgList:
+        return
+    
+    cmd = msgList[0]
+    # 登记
+    if cmd == "CheckIn":
+        __OnHelpBattleCheckInResult(curPlayer, msgList, tick)
+        
+    # 刷新
+    if cmd == "Refresh":
+        __OnHelpBattleRefreshResult(curPlayer, msgList, tick)
+        
+    # 助战记录
+    if cmd == "HelpRecord":
+        __OnHelpBattleRecord(curPlayer, msgList, tick)
+        
+    return
+
+#// B1 05 助战登记 #tagCMHelpBattleCheckIn
+#
+#struct    tagCMHelpBattleCheckIn
+#{
+#    tagHead         Head;
+#};
+def OnHelpBattleCheckIn(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    checkInInfo = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HelpBattleCheckInCount)
+    checkInCount = checkInInfo / 10 # 累计登记
+    todayIsCheckIn = checkInInfo % 10 # 今天是否已登记
+    if todayIsCheckIn:
+        GameWorld.DebugLog("当天已登记,不需要重复登记!", curPlayer.GetPlayerID())
+        return
+    fightPower = curPlayer.GetFightPower()
+    msgInfo = str(["CheckIn", checkInCount, fightPower])
+    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(curPlayer.GetID(), 0, 0, "FBHelpBattle", msgInfo, len(msgInfo))
+    return
+
+def __OnHelpBattleCheckInResult(curPlayer, msgList, tick):
+    ## 登记返回
+    cmd, checkInCount, fightPower, isOK, haveViewCache = msgList
+    if isOK:
+        checkInInfo = (checkInCount + 1) * 10 + 1
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HelpBattleCheckInCount, checkInInfo)
+        
+        # 没有数据缓存的话,马上同步一次
+        if not haveViewCache:
+            PlayerViewCacheTube.UpdateGameServerPlayerCache(curPlayer, tick, False)
+            
+    checkInPack = ChPyNetSendPack.tagMCHelpBattleCheckInResult()
+    checkInPack.IsOK = isOK
+    NetPackCommon.SendFakePack(curPlayer, checkInPack)
+    
+    GameWorld.DebugLog("FBHelpBattleResult: %s,checkInCount=%s,fightPower=%s,isOK=%s,haveViewCache=%s" 
+                       % (cmd, checkInCount, fightPower, isOK, haveViewCache), curPlayer.GetPlayerID())
+    return
+
+#// B1 06 助战召唤 #tagCMHelpBattleCall
+#
+#struct    tagCMHelpBattleCall
+#{
+#    tagHead         Head;
+#    BYTE        IsOneKeyCall;    // 是否一键召唤
+#    DWORD        PlayerID;        // 召唤的玩家ID,大于1小于100代表机器人
+#    BYTE        IsGoldCall;    // 是否仙玉召唤
+#};
+def OnHelpBattleCall(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    isOneKeyCall = clientData.IsOneKeyCall
+    calledPlayerID = clientData.PlayerID
+    #isGoldCall = clientData.IsGoldCall
+    
+    playerID = curPlayer.GetPlayerID()
+    gameFB = GameWorld.GetGameFB()
+    
+    mapID = FBCommon.GetRecordMapID(curPlayer.GetMapID())
+    funcLineID = FBCommon.GetFBPropertyMark()
+    ipyData = IpyGameDataPY.GetIpyGameData("FBHelpBattle", mapID, funcLineID)
+    if not ipyData:
+        return
+    
+    helpBattlePlayerDict = PyGameData.g_fbHelpBattlePlayerDict.get(playerID, {})
+    maxHelpPlayerCount = IpyGameDataPY.GetFuncCfg("HelpBattleCall", 2) # 最大助战人数
+    calledCount = 0
+    freePlayerIDList = []
+    helpNPCRobotIDList = []
+    for helpPlayerID, helpPlayerInfoDict in helpBattlePlayerDict.items():
+        objID = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_HelpBattleFBObjID % helpPlayerID)
+        if objID:
+            calledCount += 1
+        # 统计免费的未召唤的
+        elif not helpPlayerInfoDict.get("NeedGoldCall", 0):
+            if "Name" in helpPlayerInfoDict:
+                freePlayerIDList.append(helpPlayerID)
+            else:
+                helpNPCRobotIDList.append(helpPlayerID)
+    if calledCount >= maxHelpPlayerCount:
+        GameWorld.Log("已经召唤满人数!无法再召唤!calledCount=%s" % calledCount, playerID)
+        return
+    
+    calledPlayerDict = {} # {被召唤的玩家ID:关系, ...}
+    # 优先召唤免费玩家镜像,不足才招NPC,暂不做战力排序
+    if isOneKeyCall:
+        lackCount = maxHelpPlayerCount - calledCount
+        if freePlayerIDList:
+            randIDList = random.sample(freePlayerIDList, min(lackCount, len(freePlayerIDList)))
+            lackCount -= len(randIDList)
+            for randID in randIDList:
+                helpPlayerInfoDict = helpBattlePlayerDict[randID]
+                calledPlayerDict[randID] = helpPlayerInfoDict.get("Relation", 0)
+        if lackCount > 0 and helpNPCRobotIDList:
+            for robotID in helpNPCRobotIDList[:lackCount]:
+                calledPlayerDict[robotID] = 0     
+                
+        if calledCount + len(calledPlayerDict) < maxHelpPlayerCount:
+            GameWorld.ErrLog("无法一键召唤!人数不足! calledCount=%s,calledPlayerDict=%s,maxHelpPlayerCount=%s" 
+                             % (calledCount, calledPlayerDict, maxHelpPlayerCount), playerID)
+            return
+    else:
+        if calledPlayerID not in helpBattlePlayerDict:
+            GameWorld.ErrLog("不存在该助战玩家,无法召唤!calledPlayerID=%s" % (calledPlayerID), playerID)
+            return
+        helpPlayerInfoDict = helpBattlePlayerDict[calledPlayerID]
+        if helpPlayerInfoDict.get("NeedGoldCall", 0):
+            goldCallCost = IpyGameDataPY.GetFuncCfg("HelpBattleCall", 5) # 付费召唤消耗仙玉,优先消耗绑玉
+            costMoneyList = PlayerControl.HaveMoneyEx(curPlayer, ShareDefine.TYPE_Price_Gold_Paper_Money, goldCallCost)
+            if not costMoneyList:
+                return
+            
+            infoDict = {"MapID":mapID, 'lineID':funcLineID, "Type":"Call"}
+            for moneyType, moneyNum in costMoneyList:
+                PlayerControl.PayMoney(curPlayer, moneyType, moneyNum, ChConfig.Def_Cost_FBHelpBattle, infoDict)
+        calledPlayerDict[calledPlayerID] = helpPlayerInfoDict.get("Relation", 0)
+        
+    # 直接在地图召唤出来
+    robotNPCID = IpyGameDataPY.GetFuncCfg("HelpBattleRobot", 1)
+    posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
+    for calledPlayerID in calledPlayerDict.keys():
+        if calledPlayerID not in helpBattlePlayerDict:
+            continue
+        helpPlayerInfoDict = helpBattlePlayerDict[calledPlayerID]
+        job = helpPlayerInfoDict.get("Job", 1)
+        if "FightPower" in helpPlayerInfoDict:
+            fightPower = helpPlayerInfoDict["FightPower"]
+            npcLV = helpPlayerInfoDict["LV"]
+        else:
+            fightPower = ipyData.GetRobotFightPower()
+            npcLV = ipyData.GetRobotLV()
+            calledPlayerDict.pop(calledPlayerID) # 招NPC不通知GameServer
+            
+        jobSkillDict = ipyData.GetRobotSkillsDict()
+        
+        position = GameMap.GetEmptyPlaceInAreaEx(posX, posY, 2, 6)
+        npcPosX, npcPosY = position.GetPosX(), position.GetPosY()
+        helpBattleNPC = NPCCommon.SummonMapNpc(robotNPCID, npcPosX, npcPosY)
+        if not helpBattleNPC:
+            continue
+        objID = helpBattleNPC.GetID()
+        GameWorld.Log("召唤助战成功: calledPlayerID=%s,objID=%s,fightPower=%s" % (calledPlayerID, objID, fightPower), playerID)
+        gameFB.SetGameFBDict(ChConfig.Def_FB_HelpBattleFBObjID % calledPlayerID, objID)
+        calledCount += 1
+        __DoGiveHelpBattleRobotSkill(helpBattleNPC, jobSkillDict, job, npcLV)
+        
+        # 通知前端召唤成功
+        callResultPack = ChPyNetSendPack.tagMCHelpBattleCallResult()
+        callResultPack.ObjID = objID
+        callResultPack.PlayerID = calledPlayerID
+        NetPackCommon.SendFakePack(curPlayer, callResultPack)
+        
+    # 召唤满后
+    if calledCount >= maxHelpPlayerCount:
+        FBLogic.OnCallHelpBattleOK(curPlayer, tick)
+        
+    # 通知GameServer
+    if calledPlayerDict:
+        msgInfo = str(["Call", mapID, funcLineID, calledPlayerDict])
+        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(curPlayer.GetID(), 0, 0, "FBHelpBattle", msgInfo, len(msgInfo))
+        GameWorld.Log("GameServer_QueryPlayerResult: %s" % msgInfo, curPlayer.GetID())
+    return
+
+def __DoGiveHelpBattleRobotSkill(curNPC, jobSkillDict, job, npcLV):
+    skillManager = curNPC.GetSkillManager()
+    if job not in jobSkillDict:
+        return
+    skillInfoDict = jobSkillDict[job]
+    #{职业:{(技能ID,...):所需等级, 技能ID:所需等级, ...}, ...}
+    #{1:{(12000, 12001, 12002, 12003):1, 12011:7, 12012:15, 12013:30, 12014:80, 12015:140}, 2:{(12100, 12101, 12102, 12103):1, 12111:7, 12112:15, 12114:80, 12115:140}}
+    skillIDList = []
+    for skillInfo, needLV in skillInfoDict.items():
+        if npcLV < needLV:
+            continue
+        if isinstance(skillInfo, int):
+            skillIDList.append(skillInfo)
+        else:
+            skillIDList += list(skillInfo)
+    GameWorld.DebugLog("    给助战机器人技能: job=%s,npcLV=%s, %s" % (job, npcLV, skillIDList))
+    for skillID in skillIDList:
+        skillManager.LearnSkillByID(skillID)
+    return
+
+#// B1 07 助战刷新 #tagCMHelpBattleRefresh
+#
+#struct    tagCMHelpBattleRefresh
+#{
+#    tagHead         Head;
+#};
+def OnHelpBattleRefresh(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    SendGameServer_RefreshHelpBattlePlayer(curPlayer, isClientRefresh=True)
+    return
+
+def SendGameServer_RefreshHelpBattlePlayer(curPlayer, mapID=0, funcLineID=0, isClientRefresh=True):
+    ## 发送GameServer请求刷新助战玩家列表
+    if not mapID:
+        mapID = FBCommon.GetRecordMapID(curPlayer.GetMapID())
+    if not funcLineID:
+        funcLineID = FBCommon.GetFBPropertyMark()
+    ipyData = IpyGameDataPY.GetIpyGameData("FBHelpBattle", mapID, funcLineID)
+    if not ipyData:
+        return
+    playerID = curPlayer.GetPlayerID()
+    gameFB = GameWorld.GetGameFB()
+    costMoneyList = []
+    calledPlayerIDDict = {} # 已经召唤的玩家ID字典 {playerID:[是否付费召唤, 职业], ...}
+    
+    if isClientRefresh:
+        refreshCount = gameFB.GetPlayerGameFBDictByKey(playerID, ChConfig.FBPD_HelpBattleRefreshCount)
+        freeRefreshCount = IpyGameDataPY.GetFuncCfg("HelpBattleRefresh", 1)
+        goldRefreshCount = IpyGameDataPY.GetFuncCfg("HelpBattleRefresh", 2)
+        if refreshCount >= (freeRefreshCount + goldRefreshCount):
+            GameWorld.DebugLog("超过刷新助战列表次数!无法刷新!refreshCount=%s" % (refreshCount), playerID)
+            return
+        
+        if refreshCount >= freeRefreshCount:
+            costGold = IpyGameDataPY.GetFuncCfg("HelpBattleRefresh", 3)
+            costMoneyList = PlayerControl.HaveMoneyEx(curPlayer, ShareDefine.TYPE_Price_Gold_Paper_Money, costGold)
+            if not costMoneyList:
+                GameWorld.DebugLog("货币不足!无法刷新助战!")
+                return
+            
+        # 获取已经召唤的玩家镜像ID
+        helpBattlePlayerDict = PyGameData.g_fbHelpBattlePlayerDict.get(playerID, {})
+        for helpPlayerID, helpPlayerInfoDict in helpBattlePlayerDict.items():
+            objID = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_HelpBattleFBObjID % helpPlayerID)
+            if not objID:
+                continue
+            needGoldCall = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_HelpBattleGoldCall % helpPlayerID)
+            job = helpPlayerInfoDict.get("Job", 1)
+            relation = helpPlayerInfoDict.get("Relation", 0) # 关系需以刷新列表时的为准,如果已经召唤了,则已当时的为准
+            calledPlayerIDDict[helpPlayerID] = [needGoldCall, job, relation]
+            
+    # 非客户端刷新的视为重新开始,重置刷新次数 
+    else:
+        gameFB.SetPlayerGameFBDict(playerID, ChConfig.FBPD_HelpBattleRefreshCount, 0)
+        
+    msgInfo = str(["Refresh", mapID, funcLineID, isClientRefresh, costMoneyList, calledPlayerIDDict])
+    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(curPlayer.GetID(), 0, 0, "FBHelpBattle", msgInfo, len(msgInfo))
+    GameWorld.Log("SendGameServer_RefreshHelpBattlePlayer %s" % (msgInfo), playerID)
+    return
+
+def __OnHelpBattleRefreshResult(curPlayer, msgList, tick):
+    ## 刷新返回
+    cmd, mapID, funcLineID, isClientRefresh, costMoneyList, calledPlayerIDDict, helpBattlePlayerDict = msgList
+    
+    playerID = curPlayer.GetPlayerID()
+    gameFB = GameWorld.GetGameFB()
+    
+    # 扣钱,加刷新次数
+    infoDict = {"MapID":mapID, 'lineID':funcLineID, "Type":"Refresh"}
+    for moneyType, moneyNum in costMoneyList:
+        PlayerControl.PayMoney(curPlayer, moneyType, moneyNum, ChConfig.Def_Cost_FBHelpBattle, infoDict)
+    refreshCount = gameFB.GetPlayerGameFBDictByKey(playerID, ChConfig.FBPD_HelpBattleRefreshCount)
+    if isClientRefresh:
+        refreshCount += 1
+        gameFB.SetPlayerGameFBDict(playerID, ChConfig.FBPD_HelpBattleRefreshCount, refreshCount)
+        
+    GameWorld.Log("FBHelpBattleResult: %s,mapID=%s,funcLineID=%s,isClientRefresh=%s,costMoneyList=%s,calledPlayerIDDict=%s,refreshCount=%s" 
+                  % (cmd, mapID, funcLineID, isClientRefresh, costMoneyList, calledPlayerIDDict, refreshCount), playerID)
+    GameWorld.Log("    helpBattlePlayerDict=%s" % (helpBattlePlayerDict), playerID)
+    PyGameData.g_fbHelpBattlePlayerDict[playerID] = helpBattlePlayerDict
+    
+    # 通知前端
+    helpPlayerListPack = ChPyNetSendPack.tagMCHelpBattleList()
+    helpPlayerListPack.RefreshCount = refreshCount
+    helpPlayerListPack.HelpPlayerList = []
+    for helpPlayerID, helpPlayerInfoDict in helpBattlePlayerDict.items():
+        helpPlayer = ChPyNetSendPack.tagMCHelpBattlePlayer()
+        helpPlayer.ObjID = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_HelpBattleFBObjID % helpPlayerID) # 获取已经召唤的对应objID
+        helpPlayer.PlayerID = helpPlayerID
+        helpPlayer.Job = helpPlayerInfoDict.get("Job", 1)
+        # 玩家镜像才有名字,助战NPC名字前端处理
+        if "Name" in helpPlayerInfoDict:
+            helpPlayer.Name =  helpPlayerInfoDict["Name"]
+            helpPlayer.NameLen =  len(helpPlayer.Name)
+            helpPlayer.LV =  helpPlayerInfoDict["LV"]
+            helpPlayer.RealmLV =  helpPlayerInfoDict["RealmLV"]
+            helpPlayer.FightPower =  helpPlayerInfoDict["FightPower"]
+            helpPlayer.Relation = helpPlayerInfoDict.get("Relation", 0)
+            helpPlayer.IsNeedGold =  helpPlayerInfoDict.get("NeedGoldCall", 0)
+        helpPlayerListPack.HelpPlayerList.append(helpPlayer)
+    helpPlayerListPack.HelpCount = len(helpPlayerListPack.HelpPlayerList)
+    NetPackCommon.SendFakePack(curPlayer, helpPlayerListPack)
+    return
+
+def __OnHelpBattleRecord(curPlayer, msgList, tick):
+    cmd, helpRecordList = msgList
+    playerID = curPlayer.GetPlayerID()
+    GameWorld.DebugLog("__OnHelpBattleRecord %s,helpRecordList=%s" % (cmd, helpRecordList), playerID)
+    
+    addXianyuanCoinTotal = 0 # 累计需要增加的仙缘币
+    addXianyuanCoinTotalTotay = 0 # 累计需要增加的今日仙缘币
+    drList = []
+    
+    recordPack = ChPyNetSendPack.tagMCHelpBattleRecordList()
+    recordPack.RecordList = []
+    for recordInfo in helpRecordList:
+        callPlayerID, callPlayerName, mapID, funcLineID, xianyuanCoinAdd, relation, vipLV, recordTime = recordInfo
+        timeStr = GameWorld.ChangeTimeNumToStr(recordTime)
+        isSameDay = GameWorld.CheckTimeIsSameServerDayEx(recordTime)
+        
+        record = ChPyNetSendPack.tagMCHelpBattleRecord()
+        record.CallPlayerID = callPlayerID
+        record.CallPlayerName = callPlayerName
+        record.NameLen = len(record.CallPlayerName)
+        record.MapID = mapID
+        record.FuncLineID = funcLineID
+        record.XianyuanCoinAdd = xianyuanCoinAdd
+        record.Relation = relation
+        record.VIPLV = vipLV
+        record.HelpTime = timeStr
+        recordPack.RecordList.append(record)
+        drList.append({"CallPlayerID":callPlayerID, "MapID":mapID, "FuncLineID":funcLineID, "XianyuanCoinAdd":xianyuanCoinAdd, 
+                       "Relation":relation, "VIPLV":vipLV, "HelpTime":timeStr, "IsSameDay":isSameDay})
+        addXianyuanCoinTotal += xianyuanCoinAdd
+        if isSameDay:
+            addXianyuanCoinTotalTotay += xianyuanCoinAdd
+            
+    recordPack.RecordCount = len(recordPack.RecordList)
+    NetPackCommon.SendFakePack(curPlayer, recordPack)
+    
+    addDataDict = {"HelpList":drList}
+    GameWorld.DebugLog("    addXianyuanCoinTotal=%s,addXianyuanCoinTotalTotay=%s" % (addXianyuanCoinTotal, addXianyuanCoinTotalTotay), playerID)
+    PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_XianyuanCoin, addXianyuanCoinTotal, addDataDict=addDataDict, isSysHint=False)
+    if addXianyuanCoinTotalTotay:
+        PlayerControl.AddTodayXianyuanCoin(curPlayer, addXianyuanCoinTotalTotay)
+        
+    return
+
+def DoSingleFBAddXianyuanCoin(curPlayer, mapID, lineID):
+    ## 挑战单人副本增加仙缘币,仅适用于召唤镜像助战挑战的副本
+    playerID = curPlayer.GetPlayerID()
+    fbFuncIpyData = IpyGameDataPY.GetIpyGameData("FBFunc", mapID)
+    fbHelpIpyData = IpyGameDataPY.GetIpyGameData("FBHelpBattle", mapID, lineID)
+    if not fbFuncIpyData or not fbHelpIpyData:
+        return
+    
+    addCoinRate = 10000 # 基础倍率
+    baseFBPoint = fbFuncIpyData.GetFBPoint() # 过关 - 基础仙缘币
+    xianyuanCoinUpper = IpyGameDataPY.GetFuncCfg("HelpBattlePoint", 1) # 每日仙缘币上限
+    relationCoinAddDict = IpyGameDataPY.GetFuncEvalCfg("HelpBattlePoint", 2, {}) # 社交关系加成 {"社交关系":[过关加成, 助战加成], ...} 
+    todayXianyuanCoin = PlayerControl.GetTodayXianyuanCoin(curPlayer) # 今日已获得仙缘币
+    playerXianyuanCoinUpper = xianyuanCoinUpper
+    if curPlayer.GetVIPLv():
+        playerXianyuanCoinUpper += PlayerVip.GetPrivilegeValue(curPlayer, ChConfig.VIPPrivilege_XianyuanCoinUpperAdd)
+        addCoinRate += PlayerVip.GetPrivilegeValue(curPlayer, ChConfig.VIPPrivilege_XianyuanCoinAddPer)
+        
+    if todayXianyuanCoin >= playerXianyuanCoinUpper:
+        GameWorld.DebugLog("玩家今日仙缘币已达上限!todayXianyuanCoin=%s,playerXianyuanCoinUpper=%s" 
+                           % (todayXianyuanCoin, playerXianyuanCoinUpper), playerID)
+        return
+    
+    relationAdd = 0
+    helpBattlePlayerDict = PyGameData.g_fbHelpBattlePlayerDict.get(playerID, {})
+    for helpPlayerInfoDict in helpBattlePlayerDict.values():
+        relation = helpPlayerInfoDict.get("Relation", 0)
+        if not relation:
+            continue
+        relationAddList = relationCoinAddDict.get(str(relation), [])
+        relationAdd += relationAddList[1] if len(relationAddList) == 2 else 0
+        
+    coinAdd = int((baseFBPoint + relationAdd) * addCoinRate / 10000.0)
+    canAddMax = max(playerXianyuanCoinUpper - todayXianyuanCoin, 0)
+    coinAddReal = min(coinAdd, canAddMax) # 实际加仙缘币
+    
+    GameWorld.DebugLog("挑战单人副本增加仙缘币: coinAdd=%s,canAddMax=%s,coinAddReal=%s" % (coinAdd, canAddMax, coinAddReal), playerID)
+    addDataDict = {"MapID":mapID, "FuncLineID":lineID}
+    PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_XianyuanCoin, coinAddReal, addDataDict=addDataDict)
+    PlayerControl.AddTodayXianyuanCoin(curPlayer, coinAddReal)
+    return
+
+def DoTeamFBAddXianyuanCoin(curPlayer, mapID, lineID, callPlayerID, callPlayerName, relation):
+    ## 挑战组队副本增加仙缘币
+    return
+
+
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_QueenRelics.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_QueenRelics.py
index 27d3874..d816e94 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_QueenRelics.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_QueenRelics.py
@@ -31,6 +31,7 @@
 import EventShell
 import EventReport
 import ChPlayer
+import FBHelpBattle
 
 
 #阶段时间
@@ -42,7 +43,7 @@
 
 #当前副本地图的状态
 (
-FB_Step_Open, # 副本开启
+FB_Step_CallHelp, # 助战召唤
 FB_Step_Prepare, # 副本等待
 FB_Step_Fighting, # 副本进行中
 FB_Step_Over, # 副本结束
@@ -122,6 +123,12 @@
     curPlayer.ResetPos(enterX, enterY)
     return
 
+## 召唤助战完成
+def OnCallHelpBattleOK(curPlayer, tick):
+    lineID = FBCommon.GetFBPropertyMark()
+    DoQueenRelicsLinePrepare(curPlayer, lineID, tick)
+    return
+
 ## 进副本
 #  @param curPlayer
 #  @param tick
@@ -130,7 +137,6 @@
     playerID = curPlayer.GetPlayerID()
     GameWorld.DebugLog("DoEnterFB...", playerID)
     
-    isHelpFight = FBCommon.GetIsHelpFight(curPlayer)
     gameFB = GameWorld.GetGameFB()
     
     if not FBCommon.GetHadDelTicket(curPlayer):
@@ -139,33 +145,21 @@
             GameWorld.ErrLog("进入副本扣除门票失败!", curPlayer.GetPlayerID())
             return 0
         FBCommon.SetHadDelTicket(curPlayer)
-        isHelpFight = FBCommon.SetIsHelpFight(curPlayer)
-        GameWorld.DebugLog("    是否助战: %s" % isHelpFight, playerID)
         
     mapID = ChConfig.Def_FBMapID_QueenRelics
     lineID = FBCommon.GetFBPropertyMark()
     reqLineID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ReqFBFuncLine)
-    joinType = FBCommon.GetFBJoinType(curPlayer, isHelpFight)
     
-    if not FBCommon.GetHadSetFBPropertyMark() or reqLineID > lineID:
-        FBCommon.SetFBPropertyMark(reqLineID, curPlayer)
-        FBCommon.SetFBStep(FB_Step_Open, tick)
-        lineID = reqLineID
-        EventReport.WriteEvent_FB(curPlayer, ChConfig.Def_FBMapID_QueenRelics, lineID, ChConfig.CME_Log_Start, joinType)
-        
-    # 最后一次请求的功能线路ID比副本当前的还小,代表掉线期间队友已经打到其他层了,这时候,需要直接切换过去
-    elif reqLineID < lineID:
-        GameWorld.DebugLog("掉线期间队友已经打到其他层了,直接切换到目标功能线路ID!", playerID)
-        toPosX, toPosY = __GetQueenRelicsLinePos(mapID, lineID)
-        PlayerControl.PlayerResetWorldPosFBLineID(curPlayer, mapID, toPosX, toPosY, lineID)
-        EventReport.WriteEvent_FB(curPlayer, ChConfig.Def_FBMapID_QueenRelics, lineID, ChConfig.CME_Log_Start, joinType)
-        return
-    else:
-        EventReport.WriteEvent_FB(curPlayer, ChConfig.Def_FBMapID_QueenRelics, lineID, ChConfig.CME_Log_Start, joinType)
-        
-    #因为是组队副本,队员轮流进,所以只能设置一次
     fbStep = gameFB.GetFBStep()
-    if fbStep < FB_Step_Prepare:
+    if fbStep == FB_Step_CallHelp:
+        FBCommon.SetFBPropertyMark(reqLineID, curPlayer)
+        lineID = reqLineID
+        FBHelpBattle.SendGameServer_RefreshHelpBattlePlayer(curPlayer, mapID, lineID)
+        return
+    
+    if reqLineID > lineID:
+        FBCommon.SetFBPropertyMark(reqLineID, curPlayer)
+        lineID = reqLineID
         DoQueenRelicsLinePrepare(curPlayer, lineID, tick)
     else:
         fbLineTime = FBCommon.GetFBLineStepTime(ChConfig.Def_FBMapID_QueenRelics, lineID)
@@ -195,6 +189,7 @@
     __RefreshQueenRelicsNPC(True, False, tick, curPlayer)
     fbLineTime = FBCommon.GetFBLineStepTime(ChConfig.Def_FBMapID_QueenRelics, lineID)
     curPlayer.Sync_TimeTick(IPY_GameWorld.tttWaitStart, 0, fbLineTime[Def_PrepareTime] * 1000, True)
+    EventReport.WriteEvent_FB(curPlayer, ChConfig.Def_FBMapID_QueenRelics, lineID, ChConfig.CME_Log_Start)
     return
 
 ##副本总逻辑计时器
@@ -473,30 +468,17 @@
             continue
         DoFBHelp(curPlayer, tick)
         
-        isHelpFight = FBCommon.GetIsHelpFight(curPlayer)
         playerID = curPlayer.GetPlayerID()
         rewardLine = gameFB.GetPlayerGameFBDictByKey(playerID, FBPKey_RewardLine)
         needSyncFBData = False
         overDict = {}
         if isPass:
             overDict = {FBCommon.Over_costTime:costTime, FBCommon.Over_grade:grade}
-            # 助战只算单次过关
-            if isHelpFight:
-                helpPoint = FBCommon.AddFBHelpPoint(curPlayer, dataMapID)
-                overDict.update({FBCommon.Over_money:FBCommon.GetJsonMoneyList({ShareDefine.TYPE_Price_FBHelpPoint:helpPoint})})
-                
-                curGrade = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_Player_Dict_PlayerFBStar_MapId, lineID, False, [dataMapID])
-                if curGrade < grade:
-                    GameWorld.DebugLog("    助战更新过关评级: dataMapID=%s,lineID=%s,curGrade=%s,grade=%s" % (dataMapID, lineID, curGrade, grade), curPlayer.GetPlayerID())
-                    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_Player_Dict_PlayerFBStar_MapId, lineID, grade, False, [dataMapID])
-                    needSyncFBData = True
-                    
-            else:
-                rewardRet = __GivePlayerQueenRelicsReward(curPlayer, dataMapID, rewardLine - 1, lineID, grade, maxGrade, rewardRateList)
-                if rewardRet:
-                    needSyncFBData, startRewardLineID, totalSP, rewardItemList = rewardRet
-                    overDict.update({FBCommon.Over_sp:totalSP, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(rewardItemList), 
-                                     "startRewardLineID":startRewardLineID})
+            rewardRet = __GivePlayerQueenRelicsReward(curPlayer, dataMapID, rewardLine - 1, lineID, grade, maxGrade, rewardRateList)
+            if rewardRet:
+                needSyncFBData, startRewardLineID, totalSP, rewardItemList = rewardRet
+                overDict.update({FBCommon.Over_sp:totalSP, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(rewardItemList), 
+                                 "startRewardLineID":startRewardLineID})
             if lineID+1 > curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FBHistoryMaxLine % dataMapID):
                 PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FBHistoryMaxLine % dataMapID, lineID+1)
             #成就
@@ -512,12 +494,13 @@
         updRewardLine = lineID + 1
         gameFB.SetPlayerGameFBDict(playerID, FBPKey_RewardLine, updRewardLine)
         
-        if isPass and not rewardLine and not isHelpFight:
+        if isPass and not rewardLine:
             isInFBOnDay = gameFB.GetPlayerGameFBDictByKey(playerID, FBPKey_IsInFBOnDay)
             if not isInFBOnDay:
                 GameWorld.DebugLog("首次结算奖励,增加挑战次数!", playerID)
                 needSyncFBData = True
                 FBCommon.AddEnterFBCount(curPlayer, dataMapID)
+                FBHelpBattle.DoSingleFBAddXianyuanCoin(curPlayer, mapID, lineID)
             else:
                 GameWorld.DebugLog("副本中过天,不增加挑战次数!", playerID)
             PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_QueenRelicsEx, 1)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
index 5426941..11eb234 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
@@ -102,6 +102,11 @@
     if fbType == 0:
         return
     
+    if fbType == IPY_GameWorld.fbtSingle:
+        ownerID = gameFBMgr.GetGameFBDictByKey(ChConfig.Def_FB_SingleFBPlayerID)
+        if ownerID in PyGameData.g_fbHelpBattlePlayerDict:
+            PyGameData.g_fbHelpBattlePlayerDict.pop(ownerID)
+            
     #副本关闭时统一清怪
     FBCommon.ClearFBNPC()
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py
index 5965d6f..0520331 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py
@@ -388,6 +388,7 @@
         return True
     tagObj = __RefreshDropOwner(curNPC, tick, 0)
     if not atkObj or not tagObj:
+        GameObj.SetHP(curNPC, 1)
         GameWorld.ErrLog("Boss当前状态下不可以死亡!npcID=%s" % curNPC.GetNPCID())
         return False
     return True
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_20.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_20.py
new file mode 100644
index 0000000..546881c
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_20.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package NPCAI.AIType_20
+#
+# @todo:助战机器人
+# @author hxp
+# @date 2018-11-24
+# @version 1.0
+#
+# 详细描述: 助战机器人
+#     玩家正在攻击的目标(变相跟随玩家), 玩家无攻击目标时,机器人攻击自身视野中可攻击目标
+#     如果都没有目标,跟随玩家
+#     玩家掉线,机器人不再攻击
+#     助战机器人永远不死,血量低于50%自动回血,后端自定
+#-------------------------------------------------------------------------------
+#"""Version = 2018-11-24 22:30"""
+#-------------------------------------------------------------------------------
+
+import NPCCommon
+import ChConfig
+import GameWorld
+import GameObj
+
+
+## 初始化
+#  @param curNPC 当前npc
+#  @return None
+#  @remarks 函数详细说明.
+def DoInit(curNPC):
+    curNPC.GetNPCAngry().Init(ChConfig.Def_NormalNPCAngryCount)
+    return
+
+## 执行AI
+#  @param curNPC 当前npc
+#  @param tick 当前时间
+#  @return None
+#  @remarks 函数详细说明.
+def ProcessAI(curNPC, tick):
+    npcControl = NPCCommon.NPCControl(curNPC)
+    
+    return
+
+def OnCheckCanDie(atkObj, curNPC, skill, tick):
+    ## 检查NPC是否可死亡
+    GameObj.SetHP(curNPC, GameObj.GetMaxHP(curNPC) / 2) # 回一半血
+    GameWorld.ErrLog("该NPC不可以死亡!npcID=%s,curHP=%s" % (curNPC.GetNPCID(), GameObj.GetHP(curNPC)))
+    return False
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
index ef41edc..e5d8b03 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -5678,6 +5678,10 @@
         curPlayer.SetExAttr6(silverPoint)
     return
 
+## 玩家今日已获得仙缘币
+def GetTodayXianyuanCoin(curPlayer): return curPlayer.GetExAttr11()
+def AddTodayXianyuanCoin(curPlayer, addValue): return curPlayer.SetExAttr11(curPlayer.GetExAttr11() + addValue, False, True)
+
 ##VIP到期时间, 需要同步GameServer
 def GetVIPExpireTime(curPlayer): return curPlayer.GetExAttr9()
 def SetVIPExpireTime(curPlayer, expireTime): return curPlayer.SetExAttr9(expireTime, False, True)
@@ -5889,23 +5893,6 @@
 #  @return: 威望值
 def GetMergeWarRank(curPlayer):
     return 0
-
-
-## 设置玩家官爵星级
-#  @param curPlayer: 玩家实例
-#  @param value: 星级
-#  @return: 
-def SetOfficeStar(curPlayer, value):
-    curPlayer.SetExAttr11(value)
-    return
-
-    
-## 获取玩家玩家官爵星级
-#  @param curPlayer: 玩家实例
-#  @return: 星级
-def GetOfficeStar(curPlayer):
-    return curPlayer.GetExAttr11()
-
 
 ##获取可免费开启的格子数
 # @param curPlayer 玩家对象
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
index 2d465c4..f38e738 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
@@ -98,6 +98,7 @@
 import PlayerDiceEx
 import IpyGameDataPY
 import FamilyRobBoss
+import FBHelpBattle
 import QuestManager
 import PyGameData
 import PlayerTJG
@@ -545,6 +546,8 @@
         PlayerFreeGoods.OnDay(curPlayer)
         #采集次数重置
         NPCCommon.CollNPCTimeOnDay(curPlayer)
+        #副本助战
+        FBHelpBattle.DoPlayerOnDay(curPlayer)
         
     PlayerTJG.TJGOnDay(curPlayer, onEventType)
     # 以下为支持两种重置模式切换配置的
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_FBHelpBattle.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_FBHelpBattle.py
new file mode 100644
index 0000000..d9d2404
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_FBHelpBattle.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.RemoteQuery.GY_Query_FBHelpBattle
+#
+# @todo:副本助战
+# @author hxp
+# @date 2018-11-24
+# @version 1.0
+#
+# 详细描述: 副本助战
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2018-11-24 22:30"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import FBHelpBattle
+
+#---------------------------------------------------------------------
+#逻辑实现
+## 请求逻辑
+#  @param query_Type 请求类型
+#  @param query_ID 请求的玩家ID
+#  @param packCMDList 发包命令 [ ]
+#  @param tick 当前时间
+#  @return "True" or "False" or ""
+#  @remarks 函数详细说明.
+def DoLogic(query_Type, query_ID, packCMDList, tick):
+    return ""
+
+#---------------------------------------------------------------------
+#执行结果
+## 执行结果
+#  @param curPlayer 发出请求的玩家
+#  @param callFunName 功能名称
+#  @param funResult 查询的结果
+#  @param tick 当前时间
+#  @return None
+#  @remarks 函数详细说明.
+def DoResult(curPlayer, callFunName, funResult, tick):
+    
+    GameWorld.DebugLog("GY_Query_FBHelpBattle funResult=%s" % str(funResult))
+    if funResult == "":
+        return
+    FBHelpBattle.GameServer_FBHelpBattleResult(curPlayer, eval(funResult), tick)
+    return
+
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
index a374fd5..ef5989f 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -67,4 +67,7 @@
 
 g_familyPartyInfo = {} #[ [[familyID, familyName, 答题数量]], top名字,top答题数量]
 
-g_elderBattleRobotDieDict = {} #上古战场机器人死亡时间{lineid:[]}
\ No newline at end of file
+g_elderBattleRobotDieDict = {} #上古战场机器人死亡时间{lineid:[]}
+
+g_fbHelpBattlePlayerDict = {} # 助战玩家信息 {playerID:{助战玩家ID:{助战玩家简要信息字典} ...}, ...}
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
index 37ba082..bf7a904 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -759,7 +759,7 @@
 CDBPlayerRefresh_RealmPoint,            # 境界修炼点
 CDBPlayerRefresh_Ysog,                  # 魔精
 CDBPlayerRefresh_FamilyActivity,        # 仙盟活跃令
-CDBPlayerRefresh_FBHelpPoint,           # 副本助战积分
+CDBPlayerRefresh_Xianyuancoin,          # 仙缘币
 CDBPlayerRefresh_PKState,               # 战斗状态 175
 CDBPlayerRefresh_BossState,             # boss状态
 CDBPlayerRefresh_BaseAtkMin,            # 基础最小攻击
@@ -789,7 +789,8 @@
 TYPE_Price_RealmPoint = 13    # 境界修炼点
 TYPE_Price_Ysog = 14    # 魔精
 TYPE_Price_FamilyActivity = 15    # 仙盟活跃令
-TYPE_Price_FBHelpPoint = 16    # 副本助战积分
+TYPE_Price_FBHelpPoint = 16    # 副本助战积分, 废弃
+TYPE_Price_XianyuanCoin = 17    # 仙缘币
 TYPE_Price_Rune = 23    # 符印精华点
 TYPE_Price_RuneSplinters = 24    # 符印碎片
 TYPE_Price_TreasureScore = 25    # 寻宝积分
@@ -820,7 +821,7 @@
                            TYPE_Price_RealmPoint:CDBPlayerRefresh_RealmPoint,
                            TYPE_Price_Ysog:CDBPlayerRefresh_Ysog,
                            TYPE_Price_FamilyActivity:CDBPlayerRefresh_FamilyActivity,
-                           TYPE_Price_FBHelpPoint:CDBPlayerRefresh_FBHelpPoint,
+                           TYPE_Price_XianyuanCoin:CDBPlayerRefresh_Xianyuancoin,
                            TYPE_Price_Danjing:CDBPlayerRefresh_Danjing,
                            }
 

--
Gitblit v1.8.0