From 41003de2a34a117d9aa286009742493e2aa9ff4b Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期四, 23 五月 2019 17:18:56 +0800
Subject: [PATCH] 6843 【后端】【2.0】缥缈仙域优化 6897 【后端】【2.0】缥缈仙域产出类型修改 6805 【后端】【2.0】副本前端化(缥缈宝藏、妖王、草园、VIPboss)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py                                   |   69 --
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py                            |   42 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py |   81 +-
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_SummonNPC.py        |   13 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossGrassland.py |  119 ++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py                                        |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyDomain.py                          |    6 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_FairyTreasure.py  |  358 -----------
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_PersonalBoss.py   |  330 ++---------
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py                   |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py                 |   32 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py                                         |    9 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/SummonNPC.py                             |   17 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_PriWood.py                          |    8 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                                               |   12 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py                                     |    8 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py                                 |   20 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py                                   |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_196.py                              |   71 --
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py                                     |  309 +++++++++-
 PySysDB/PySysDBPY.h                                                                                                      |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                                          |   10 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py                                     |  208 +++++++
 23 files changed, 860 insertions(+), 873 deletions(-)

diff --git a/PySysDB/PySysDBPY.h b/PySysDB/PySysDBPY.h
index b292843..7c518c2 100644
--- a/PySysDB/PySysDBPY.h
+++ b/PySysDB/PySysDBPY.h
@@ -896,8 +896,8 @@
 
 struct tagPersonalBoss
 {
-	DWORD		_NPCID;	//ID
-	DWORD		ChanllengeLv;	//可挑战等级
+	DWORD		NPCID;	//ID
+	DWORD		_FuncLineID;	//可挑战等级
 };
 
 //仙盟活跃表
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index 727192d..dbca99f 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -349,13 +349,13 @@
 PacketSubCMD_8=0x04
 PacketCallFunc_8=OnClientStartFB
 
-PacketCMD_9=0xB1
-PacketSubCMD_9=0x08
-PacketCallFunc_9=OnRefreshCustomFBPrize
+PacketCMD_9=
+PacketSubCMD_9=
+PacketCallFunc_9=
 
-PacketCMD_10=0xB1
-PacketSubCMD_10=0x09
-PacketCallFunc_10=OnGiveCustomFBPrize
+PacketCMD_10=
+PacketSubCMD_10=
+PacketCallFunc_10=
 
 PacketCMD_11=0xB1
 PacketSubCMD_11=0x0A
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 234a406..b90f427 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
@@ -1033,8 +1033,8 @@
         #对象已经死亡
         return False
     
-    #添加(系统)隐身者不可攻击,证明家族战非隐身
-    if not attacker.GetVisible():
+    #添加(系统)隐身者不可攻击,证明家族战非隐身;自定义场景中是隐身的不做限制
+    if not attacker.GetVisible() and not attacker.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomScene):
         return False
     
     if not defender.GetVisible():
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_SummonNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_SummonNPC.py
index e58b91c..3873af4 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_SummonNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_SummonNPC.py
@@ -21,18 +21,16 @@
 # @change: "2015-04-11 15:30" hxp 增加最终伤害逻辑
 # @change: "2016-02-26 17:00" hxp 增加PVP伤害统计
 #------------------------------------------------------------------------------ 
-"""Version = 2016-02-26 17:00"""
+#"""Version = 2016-02-26 17:00"""
 #---------------------------------------------------------------------
 import NPCCommon
 import GameWorld
 import AttackCommon
 import IPY_GameWorld
-import PlayerControl
-import SkillCommon
 import ChConfig
-import ChEquip
-import SkillShell
+import FBLogic
 import GameObj
+import ChNPC
 #---------------------------------------------------------------------
 
 #---------------------------------------------------------------------
@@ -146,8 +144,11 @@
     #召唤兽死亡
     if GameObj.GetHP(curTagSummon) <=  0:
         NPCCommon.OnPlayerAttackNPCDie(curTagSummon, curPlayer, skill)
+        FBLogic.DoFB_Player_KillNPC(curPlayer , curTagSummon , tick)
         #获得控制器
         curTagNormalNPCControl = NPCCommon.NPCControl(curTagSummon)
         curTagNormalNPCControl.SetKilled()
-    
+    else:
+        ChNPC.OnNPCAttacked(curPlayer, curTagSummon, skill, tick)
+        
     return True
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py
index 3d5775a..54e471b 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py
@@ -974,16 +974,18 @@
         if not curTag:
             continue
         
-        if curSkillUseTag == ChConfig.Def_UseSkillTag_CanAttackNPC:
-            if NPCCommon.GetNpcObjOwnerIsPlayer(curTag):
-                #npc主人是玩家不能攻击
+        #非自定义场景才需要判断
+        if not attacker.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomScene):
+            if curSkillUseTag == ChConfig.Def_UseSkillTag_CanAttackNPC:
+                if NPCCommon.GetNpcObjOwnerIsPlayer(curTag):
+                    #npc主人是玩家不能攻击
+                    continue
+            
+            if GameWorld.GetDist(curTag.GetPosX(), curTag.GetPosY(), attacker.GetPosX(), attacker.GetPosY()) > attacker.GetSight():
+                # 最远距离防范
+                GameWorld.DebugLog("#--- 最远距离防范[%s-%s]"%(attacker.GetID(), curTag.GetID()))
                 continue
-    
-        if GameWorld.GetDist(curTag.GetPosX(), curTag.GetPosY(), attacker.GetPosX(), attacker.GetPosY()) > attacker.GetSight():
-            # 最远距离防范
-            GameWorld.DebugLog("#--- 最远距离防范[%s-%s]"%(attacker.GetID(), curTag.GetID()))
-            continue
-    
+            
         if CheckFunc != None:
             #检查是否受影响
             if not CheckFunc(attacker, curTag, curSkill, tick):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index 85db2dd..5d19629 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -3377,6 +3377,7 @@
 Def_Player_Dict_ReqFBMissionID = "ReqFBMissionID" # 请求进入副本的任务ID
 Def_Player_Dict_ReqFBMissionType = "ReqFBMissionType" # 请求进入副本的任务类型
 Def_Player_Dict_PlayerFBStar_MapId = "FBStar_%s_%s"  # 副本星级星级信息, 参数为[mapID, key编号], 按位存储每个lineID对应的星级
+Def_Player_Dict_CustomSceneStartTime = "ClientFBStartTime_%s_%s"  # 前端自定义场景开始time, 参数(mapID, lineID), 0-无,2-已结束,其他-time值
 Def_Player_Dict_EnterFbCntDay = "EnterFbCntDay_%s"  # 今日进入副本次数, 参数为副本ID
 Def_Player_Dict_BuyFbCntDay = "BuyFbCntDay_%s" # 今日购买副本进入次数, 参数为副本ID
 Def_Player_Dict_RecoverFbCnt = "RecoverFbCnt_%s"  # 今日找回的副本次数, 参数为副本ID
@@ -3932,6 +3933,10 @@
 Def_PDict_FairyDomainVisitCnt = "FairyDomainVisitCnt" #寻访总次数
 Def_PDict_FairyAdventuresData = "FairyAdventuresData_%s" #奇遇数值 唯一ID*100+档位 参数事件ID
 Def_PDict_FairyDomainEventAppearCnt = "FDEventAppearCnt%s" #事件出现次数 参数事件ID  AAABBB BBB:小时段出现次数 AAA:今日出现次数
+
+#草园
+Def_PDict_GrasslandNPCCount = "GrasslandNPCCount_%s" #草园NPCID个数,参数NPCID
+Def_PDict_GrasslandDropCount = "GrasslandDropCount_%s" #草园掉落统计,参数编号,记录格式 itemID*100+dropCount
 
 #五行专精
 Def_PDict_SkillElementID = "SkillElementID%s" #主技能选择的专精技能 参数主技能ID
@@ -4901,6 +4906,11 @@
 
 ##==================================================================================================
 
+# 前端自定义场景状态
+CustomSceneState_None = 0 # 无
+CustomSceneState_Fight = 1 # 战斗进行中
+CustomSceneState_Over = 2 # 已结束
+
 # 副本参与类型
 FB_JoinType = (
 FB_JoinType_None, # 默认无
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py
index dbc46c5..071ac05 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py
@@ -828,3 +828,211 @@
 
 m_NAtagObjInfoRefresh=tagObjInfoRefresh()
 ChNetPackDict[eval("0x%02x%02x"%(m_NAtagObjInfoRefresh.Cmd,m_NAtagObjInfoRefresh.SubCmd))] = m_NAtagObjInfoRefresh
+
+#04 08 玩家召唤NPC出现#tagPlayerSummonNPCAppear
+
+class  tagPlayerSummonNPCAppear(Structure):
+    Head = tagHead()
+    PlayerID = 0    #(DWORD PlayerID)
+    Country = 0    #(BYTE Country)
+    ObjID = 0    #(DWORD ObjID)//召唤出来的NPCID
+    NPCID = 0    #(DWORD NPCID)
+    PosX = 0    #(WORD PosX)
+    PosY = 0    #(WORD PosY)
+    HP = 0    #(DWORD HP)
+    HPEx = 0    #(DWORD HPEx)
+    MaxHP = 0    #(DWORD MaxHP)
+    MaxHPEx = 0    #(DWORD MaxHPEx)
+    Speed = 0    #(WORD Speed)
+    LV = 0    #(BYTE LV)
+    OwnerNameLen = 0    #(BYTE OwnerNameLen)
+    OwnerName = ""    #(String OwnerName)//size = OwnerNameLen
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0x04
+        self.Head.SubCmd = 0x08
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Country,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ObjID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.NPCID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.PosX,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.PosY,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.HP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.MaxHP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.MaxHPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Speed,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.LV,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.OwnerNameLen,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.OwnerName,_pos = CommFunc.ReadString(_lpData, _pos,self.OwnerNameLen)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0x04
+        self.Head.SubCmd = 0x08
+        self.PlayerID = 0
+        self.Country = 0
+        self.ObjID = 0
+        self.NPCID = 0
+        self.PosX = 0
+        self.PosY = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.MaxHP = 0
+        self.MaxHPEx = 0
+        self.Speed = 0
+        self.LV = 0
+        self.OwnerNameLen = 0
+        self.OwnerName = ""
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 1
+        length += 4
+        length += 4
+        length += 2
+        length += 2
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 2
+        length += 1
+        length += 1
+        length += len(self.OwnerName)
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteBYTE(data, self.Country)
+        data = CommFunc.WriteDWORD(data, self.ObjID)
+        data = CommFunc.WriteDWORD(data, self.NPCID)
+        data = CommFunc.WriteWORD(data, self.PosX)
+        data = CommFunc.WriteWORD(data, self.PosY)
+        data = CommFunc.WriteDWORD(data, self.HP)
+        data = CommFunc.WriteDWORD(data, self.HPEx)
+        data = CommFunc.WriteDWORD(data, self.MaxHP)
+        data = CommFunc.WriteDWORD(data, self.MaxHPEx)
+        data = CommFunc.WriteWORD(data, self.Speed)
+        data = CommFunc.WriteBYTE(data, self.LV)
+        data = CommFunc.WriteBYTE(data, self.OwnerNameLen)
+        data = CommFunc.WriteString(data, self.OwnerNameLen, self.OwnerName)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                PlayerID:%d,
+                                Country:%d,
+                                ObjID:%d,
+                                NPCID:%d,
+                                PosX:%d,
+                                PosY:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                MaxHP:%d,
+                                MaxHPEx:%d,
+                                Speed:%d,
+                                LV:%d,
+                                OwnerNameLen:%d,
+                                OwnerName:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.PlayerID,
+                                self.Country,
+                                self.ObjID,
+                                self.NPCID,
+                                self.PosX,
+                                self.PosY,
+                                self.HP,
+                                self.HPEx,
+                                self.MaxHP,
+                                self.MaxHPEx,
+                                self.Speed,
+                                self.LV,
+                                self.OwnerNameLen,
+                                self.OwnerName
+                                )
+        return DumpString
+
+
+m_NAtagPlayerSummonNPCAppear=tagPlayerSummonNPCAppear()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagPlayerSummonNPCAppear.Head.Cmd,m_NAtagPlayerSummonNPCAppear.Head.SubCmd))] = m_NAtagPlayerSummonNPCAppear
+
+#06 08 NPC死亡#tagNPCDie
+
+class  tagNPCDie(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ObjID", c_int),    
+                  ("Reason", c_int),    
+                  ("KillerType", c_ubyte),    
+                  ("KillerID", c_int),    
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0x06
+        self.SubCmd = 0x08
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0x06
+        self.SubCmd = 0x08
+        self.ObjID = 0
+        self.Reason = 0
+        self.KillerType = 0
+        self.KillerID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagNPCDie)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''//06 08 NPC死亡//tagNPCDie:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ObjID:%d,
+                                Reason:%d,
+                                KillerType:%d,
+                                KillerID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ObjID,
+                                self.Reason,
+                                self.KillerType,
+                                self.KillerID
+                                )
+        return DumpString
+
+
+m_NAtagNPCDie=tagNPCDie()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagNPCDie.Cmd,m_NAtagNPCDie.SubCmd))] = m_NAtagNPCDie
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/SummonNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/SummonNPC.py
index 6d274af..d37ba58 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/SummonNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/SummonNPC.py
@@ -16,6 +16,7 @@
 import ChConfig
 import GameWorld
 import NPCCommon
+import PlayerFB
 
 #
 #逻辑实现
@@ -26,8 +27,9 @@
 #  @remarks 函数详细说明.
 def OnExec(curPlayer, paramList):
     #输入命令格式错误
-    if not paramList or len(paramList) > 2:
+    if not paramList:
         GameWorld.DebugAnswer(curPlayer, "SummonNPC npcID 个数")
+        GameWorld.DebugAnswer(curPlayer, "SummonNPC npcID 个数 前端场景ID lineID")
         return
     
     #NPC对象ID
@@ -40,6 +42,19 @@
     
     npcType = npcData.GetType()
     if npcType in [ChConfig.ntPriWoodPilePVE, ChConfig.ntPriWoodPilePVP]:
+        mapID = paramList[2] if len(paramList) > 2 else 0
+        lineID = paramList[3] if len(paramList) > 3 else 0
+        if not mapID:
+            GameWorld.DebugAnswer(curPlayer, "木桩怪必须指定地图ID才能召唤!")
+            return
+        
+        sceneMapID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneMapID)
+        sceneLineID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneLineID)
+        if mapID != sceneMapID or lineID != sceneLineID:
+            if sceneMapID:
+                PlayerFB.DoExitCustomScene(curPlayer)
+            tick = GameWorld.GetGameWorld().GetTick()
+            PlayerFB.DoEnterCustomScene(curPlayer, mapID, lineID, tick)
         NPCCommon.SummonPriWoodPile(curPlayer, npcID, npcCount)
         return
     
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 ee6d852..876a00b 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -272,6 +272,12 @@
 #  @return None
 #  @remarks 函数详细说明.
 def DoFB_Player_KillNPC(curPlayer , curNPC , tick):
+    mapID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneMapID)
+    lineID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneLineID)
+    if mapID:
+        DoCustomScene_Player_KillNPC(curPlayer, curNPC, mapID, lineID)
+        return
+    
     do_FBLogic_ID = __GetFBLogic_MapID(GameWorld.GetMap().GetMapID())
     
     callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "DoFB_Player_KillNPC"))
@@ -1564,7 +1570,7 @@
     callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnEnterFBEvent"))
     
     if callFunc == None:
-        return False
+        return True
     
     return callFunc(curPlayer, mapID, lineID, tick)
 
@@ -2180,28 +2186,38 @@
     
     return callFunc(curPlayer, mapID, lineID)
 
-## 客户端发送刷新自定义副本奖励
-def OnRefreshCustomFBPrize(curPlayer, mapID, lineID):
+## 判断可否召唤木桩怪
+def OnCanSummonPriWoodPile(curPlayer, mapID, lineID, npcID, count):
     do_FBLogic_ID = __GetFBLogic_MapID(mapID)
     
-    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnRefreshCustomFBPrize"))
+    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnCanSummonPriWoodPile"))
     
     if callFunc == None:
-        return []
+        return True
     
-    return callFunc(curPlayer, mapID, lineID)
+    return callFunc(curPlayer, mapID, lineID, npcID, count)
 
-## 给自定义副本奖励后续处理
-## @return: 返回结算副本over信息字典,不含jsonItem信息
-def OnGiveCustomFBPrizeOK(curPlayer, mapID, lineID):
+## 自定义场景副本击杀NPC
+def DoCustomScene_Player_KillNPC(curPlayer, curNPC, mapID, lineID):
     do_FBLogic_ID = __GetFBLogic_MapID(mapID)
     
-    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnGiveCustomFBPrizeOK"))
+    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "DoCustomScene_Player_KillNPC"))
     
-    if callFunc == None:
-        return {}
+    if callFunc:
+        callFunc(curPlayer, curNPC, mapID, lineID)
+        
+    return
+
+## 自定义场景采集OK
+def OnCustomSceneCollectOK(curPlayer, mapID, lineID, npcID):
+    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
     
-    return callFunc(curPlayer, mapID, lineID)
+    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnCustomSceneCollectOK"))
+    
+    if callFunc:
+        callFunc(curPlayer, mapID, lineID, npcID)
+        
+    return
 
 ## 进入跨服副本注册数据前逻辑
 ## @return: 是否可以注册前往跨服副本,次函数中可以写一些扣除消耗逻辑等
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
index d007905..0afee8e 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
@@ -473,6 +473,38 @@
     GameWorld.GetGameFB().SetPlayerGameFBDict(curPlayer.GetID(), ChConfig.FBPlayerDict_IsDelTicket, delSign)
     return
 
+def SetCustomSceneStart(curPlayer, mapID, lineID):
+    ''' 设置前端自定义场景副本开始时间
+            某些自定义场景进入可能需要处理某些逻辑,比如扣门票、设置某些状态、完成成就等,这些逻辑在单次进入只能触发一次
+            需要记录一个开始状态,只在设置成功的时候进行处理这些逻辑,防止断线重连或重登重新进入自定义场景时,重复执行逻辑
+            该状态设置了一个有效期,暂时设定为15分钟,未完成场景有效期内重复进入不会重复触发
+            做超时是为了防止玩家没有正常结束场景导致未来的某个时间再次进入场景时无法正常触发进入需要额外处理的逻辑
+            需根据副本结束时机配置函数 SetCustomSceneOver 设置结束状态
+    @return: 设置成功返回True
+    '''
+    if GetCustomSceneState(curPlayer, mapID, lineID) == ChConfig.CustomSceneState_Fight:
+        return
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_CustomSceneStartTime % (mapID, lineID), int(time.time()))
+    return True
+
+def GetCustomSceneState(curPlayer, mapID, lineID):
+    '''获取前端自定义场景状态
+    @return: 0-无,1-进行中,2-已结束
+    '''
+    startTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_CustomSceneStartTime % (mapID, lineID))
+    if not startTime:
+        return ChConfig.CustomSceneState_None
+    if startTime == ChConfig.CustomSceneState_Over:
+        return ChConfig.CustomSceneState_Over
+    # 保留15分钟
+    curTime = int(time.time())
+    return ChConfig.CustomSceneState_Fight if curTime - startTime < 15 * 60 else ChConfig.CustomSceneState_None
+
+def SetCustomSceneOver(curPlayer, mapID, lineID):
+    ## 设置前端自定义场景副本结束
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_CustomSceneStartTime % (mapID, lineID), ChConfig.CustomSceneState_Over)
+    return
+
 def GetCurSingleFBPlayer():
     ''' 获取当前单人副本玩家 '''
     curPlayer = None
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py
index abc8c73..365f0bd 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py
@@ -25,6 +25,7 @@
 import PlayerControl
 import ShareDefine
 import PyGameData
+import NPCCommon
 import ChConfig
 import ChItem
 
@@ -273,31 +274,41 @@
     PlayerFairyDomain.SetFairyDomainFBEventState(curPlayer, mapID, lineID, PlayerFairyDomain.FDEventState_Visiting)
     return
 
-## 客户端发送刷新自定义副本奖励
-def OnRefreshCustomFBPrize(curPlayer, mapID, lineID):
-    visitCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FairyDomainVisitCnt)
-    fakeImmortalCount = IpyGameDataPY.GetFuncCfg("FakeImmortalCount", 1)
-    if visitCount > fakeImmortalCount:
-        GameWorld.DebugLog("当前寻访次数不能获取自定义副本奖励!visitCount=%s" % visitCount)
-        return []
-    if not PlayerFairyDomain.SetFairyDomainFBEventState(curPlayer, mapID, lineID, PlayerFairyDomain.FDEventState_Visiting):
-        GameWorld.DebugLog("寻访状态异常不能获取自定义副本奖励!")
-        return []
-    ipyData = IpyGameDataPY.GetIpyGameDataByCondition("FairyDomain", {"MapID":mapID, "LineID":lineID})
+## 自定义场景副本击杀NPC
+def DoCustomScene_Player_KillNPC(curPlayer, curNPC, mapID, lineID):
+    
+    bossID = curNPC.GetNPCID()
+    funcLineID = lineID
+    curBossID = GetCurFBLineBOSSID(lineID=funcLineID)
+    curState = PlayerFairyDomain.GetFairyDomainFBEventState(curPlayer, mapID, lineID)
+    GameWorld.DebugLog("自定义场景击杀NPC: mapID=%s,lineID=%s,bossID=%s,curBossID=%s,eventState=%s" 
+                       % (mapID, lineID, bossID, curBossID, curState))
+    if bossID != curBossID:
+        return
+    
+    playerID = curPlayer.GetPlayerID()
+    ipyData = IpyGameDataPY.GetIpyGameDataByCondition("FairyDomain", {"MapID":mapID, "LineID":funcLineID})
     if not ipyData:
-        return []
+        return
     eventID = ipyData.GetID()
+    eventFBType = ipyData.GetEventFBType()
+    if eventFBType != PlayerFairyDomain.FDEventFBType_Client:
+        GameWorld.DebugLog("    非前端本,不能掉落!", playerID)
+        return
+    if curState != PlayerFairyDomain.FDEventState_Visiting:
+        GameWorld.DebugLog("    非寻访中,不能掉落!", playerID)
+        return
+    PlayerFairyDomain.SetFairyDomainFBEventState(curPlayer, mapID, funcLineID, PlayerFairyDomain.FDEventState_Visited)
+    
     isOwner = True
-    giveItemList = __GetDemonKingPrizeItemList(curPlayer, mapID, lineID, eventID, isOwner)
-    return giveItemList
-
-## 给自定义副本奖励后续处理
-## @return: 返回结算副本over信息字典,不含jsonItem信息
-def OnGiveCustomFBPrizeOK(curPlayer, mapID, lineID):
-    PlayerFairyDomain.SetFairyDomainFBEventState(curPlayer, mapID, lineID, PlayerFairyDomain.FDEventState_Visited)
-    ownerID, ownerName = curPlayer.GetPlayerID(), curPlayer.GetPlayerName()
-    overDict = {FBCommon.Over_ownerID:ownerID, FBCommon.Over_ownerName:ownerName}
-    return overDict
+    giveItemList = __GetDemonKingPrizeItemList(curPlayer, mapID, funcLineID, eventID, isOwner)
+    NPCCommon.DoGiveItemByVirtualDrop(curPlayer, giveItemList, bossID)
+    
+    isPass = 1
+    overDict = {FBCommon.Over_ownerID:playerID, FBCommon.Over_ownerName:curPlayer.GetPlayerName(), 
+                FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(giveItemList)}
+    FBCommon.NotifyFBOver(curPlayer, mapID, lineID, isPass, overDict)
+    return
 
 def __GetDemonKingPrizeItemList(curPlayer, mapID, lineID, eventID, isOwner):
     giveItemList = PlayerFairyDomain.GetFairyAppointAward(curPlayer, eventID)
@@ -305,19 +316,29 @@
         return giveItemList
     
     # 没有定制奖励则取常规奖励
-    # {物品ID:[归属者获得个数饼图[[概率, 个数], ...], 非归属者获得个数饼图[[概率, 个数], ...], 是否拍品], ...}
+    # [[归属者随机次数, 非归属随机次数, [[权重,[物品ID,个数,是否拍品]], ...]], ...]
+    
     giveItemList = []
-    awardDict = FBCommon.GetFBLineReward(mapID, lineID)
-    for itemID, itemInfo in awardDict.items():
-        ownerCountRateList, notOwnerCountRateList, isAuctionItem = itemInfo
+    awardList = FBCommon.GetFBLineReward(mapID, lineID)
+    for awardInfo in awardList:
+        ownerCount, otherCount, itemWeightList = awardInfo
         if isOwner:
-            itemCount = GameWorld.GetResultByRandomList(ownerCountRateList)
+            if not ownerCount:
+                continue
+            randCount = ownerCount
         else:
-            itemCount = GameWorld.GetResultByRandomList(notOwnerCountRateList)
-        if not itemCount:
+            if not otherCount:
+                continue
+            randCount = otherCount
+        realWeightList = ItemCommon.GetWeightItemListByAlchemyDiffLV(curPlayer, itemWeightList, 1)
+        if not realWeightList:
             continue
-        giveItemList.append([itemID, itemCount, isAuctionItem])
         
+        for _ in xrange(randCount):
+            itemInfo = GameWorld.GetResultByWeightList(realWeightList)
+            itemID, itemCount, isAuctionItem = itemInfo
+            giveItemList.append([itemID, itemCount, isAuctionItem])
+            
     return giveItemList
 
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossGrassland.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossGrassland.py
index e4fe704..9e8b39f 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossGrassland.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossGrassland.py
@@ -39,7 +39,8 @@
         npcID = IpyGameDataPY.GetFuncCfg("CrossGrasslandCfg", 1)
         if npcID:
             NPCCommon.UpdateNPCAttackCount(curPlayer, npcID, 0)
-            
+          
+    ResetGrasslandAwardRecord(curPlayer)  
     return
 
 ## 是否需要做进入副本通用检查条件逻辑,默认需要检查
@@ -127,12 +128,100 @@
 
 ## 客户端进入自定义场景
 def OnEnterCustomScene(curPlayer, mapID, lineID):
+    curState = PlayerFairyDomain.GetFairyDomainFBEventState(curPlayer, mapID, lineID)
     PlayerFairyDomain.SetFairyDomainFBEventState(curPlayer, mapID, lineID, PlayerFairyDomain.FDEventState_Visiting)
+    
+    boxNPCID = IpyGameDataPY.GetFuncCfg("CrossGrasslandCfg", 1)
+    refreshMapNPCDict = IpyGameDataPY.GetFuncEvalCfg("CrossGrasslandCfg", 2)
+    refreshCount, npcIDRateList = refreshMapNPCDict.get(mapID, [0, []])
+    npcIDList = [rate[1] for rate in npcIDRateList]
+    if curState == PlayerFairyDomain.FDEventState_CanVisit:
+        # 随机采集物数量
+        for npcID in npcIDList:
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GrasslandNPCCount % npcID, 0)
+        npcCountDict = {}
+        for _ in xrange(refreshCount):
+            npcID = GameWorld.GetResultByRandomList(npcIDRateList)
+            if npcID:
+                npcCount = npcCountDict.get(npcID, 0) + 1
+                npcCountDict[npcID] = npcCount
+                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GrasslandNPCCount % npcID, npcCount)
+                
+        if mapID == ChConfig.Def_FBMapID_CrossGrasslandXian:
+            FBCommon.DelFBEnterTicket(curPlayer, mapID, lineID)
+            
+    SyncCustomSceneNPCCount(curPlayer, mapID)
+    if mapID == ChConfig.Def_FBMapID_CrossGrasslandXian:
+        boxNPCID = IpyGameDataPY.GetFuncCfg("CrossGrasslandCfg", 1)
+        if boxNPCID:
+            NPCCommon.SyncNPCAttackCount(curPlayer, [boxNPCID])
+            
     return
 
-def DoCheckUpdateGrasslandEnd(curPlayer):
-    ## 检查更新草园已拜访完成
+def DecCustomSceneNPCCount(curPlayer, npcID):
+    ## 减少草园自定义场景NPC,默认减少一个
+    mapID = GetGrasslandMapID(curPlayer)[0]
+    if not mapID:
+        return
+    curCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_GrasslandNPCCount % npcID)
+    updCount = max(0, curCount - 1)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GrasslandNPCCount % npcID, updCount)
+    SyncCustomSceneNPCCount(curPlayer, mapID)
+    return
+
+def SyncCustomSceneNPCCount(curPlayer, mapID):
+    ## 通知自定义场景NPC数
+    refreshMapNPCDict = IpyGameDataPY.GetFuncEvalCfg("CrossGrasslandCfg", 2)
+    npcIDRateList = refreshMapNPCDict.get(mapID, [0, []])[1]
+    npcCountDict = {}
+    for npcRate in npcIDRateList:
+        npcID = npcRate[1]
+        npcCountDict[npcID] = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_GrasslandNPCCount % npcID)
+    if mapID == ChConfig.Def_FBMapID_CrossGrasslandXian:
+        boxNPCID = IpyGameDataPY.GetFuncCfg("CrossGrasslandCfg", 1)
+        npcCountDict[boxNPCID] = 1
+    NPCCommon.SyncNPCCntInfo(curPlayer, mapID, npcCountDict)
+    return
+
+def RecordGrasslandAward(curPlayer, addItemList):
+    ## 记录草园奖励信息
+    mapID = GetGrasslandMapID(curPlayer)[0]
+    if not mapID:
+        return
     
+    for itemInfo in addItemList:
+        if not isinstance(itemInfo, list):
+            continue
+        itemID, itemCount = itemInfo[:2]
+        
+        newIndex = None
+        for i in xrange(20):
+            itemCountInfo = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_GrasslandDropCount % i)
+            if not itemCountInfo:
+                newIndex = i
+                break
+            recItemID = itemCountInfo/100
+            if recItemID == itemID:
+                updRecValue = itemCountInfo + itemCount
+                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GrasslandDropCount % i, updRecValue)
+                break
+        if newIndex != None:
+            newRecValue = itemID*100 + itemCount
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GrasslandDropCount % newIndex, newRecValue)
+            
+    return
+
+def ResetGrasslandAwardRecord(curPlayer):
+    ## 重置草园奖励信息记录
+    for i in xrange(20):
+        itemCountInfo = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_GrasslandDropCount % i)
+        if not itemCountInfo:
+            break
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GrasslandDropCount % i, 0)
+    return
+
+    
+def GetGrasslandMapID(curPlayer):
     grasslandMapIDList = [ChConfig.Def_FBMapID_CrossGrasslandLing, ChConfig.Def_FBMapID_CrossGrasslandXian]
     crossMapID = PlayerControl.GetCrossMapID(curPlayer)
     clientCustomSceneMapID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneMapID)
@@ -143,6 +232,14 @@
         mapID = clientCustomSceneMapID
         lineID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneLineID)
     else:
+        return 0, 0
+    return mapID, lineID
+
+def DoCheckUpdateGrasslandEnd(curPlayer):
+    ## 检查更新草园已拜访完成
+    
+    mapID, lineID = GetGrasslandMapID(curPlayer)
+    if not mapID:
         return
     
     # 采集次数是否已用完
@@ -179,6 +276,19 @@
         
     PlayerFairyDomain.SetFairyDomainFBEventState(curPlayer, mapID, lineID, PlayerFairyDomain.FDEventState_Visited)
     GameWorld.DebugLog("设置草园已完成!mapID=%s, lineID=%s" % (mapID, lineID))
+    
+    # 通知结算
+    awardItemList = []
+    for i in xrange(20):
+        itemCountInfo = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_GrasslandDropCount % i)
+        if not itemCountInfo:
+            break
+        isAuctionItem = 0
+        itemID, itemCount = itemCountInfo/100, itemCountInfo%100
+        awardItemList.append([itemID, itemCount, isAuctionItem])
+    overDict = {FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(awardItemList)}
+    FBCommon.NotifyFBOver(curPlayer, mapID, lineID, 1, overDict)
+    ResetGrasslandAwardRecord(curPlayer)
     return
 
 def DoFB_NPCDead(curNPC):
@@ -191,3 +301,6 @@
     GameWorld.GetGameFB().SetGameFBDict(FBDict_SyncFBNPC, 1)
     return
 
+
+
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_FairyTreasure.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_FairyTreasure.py
index f7c1968..db3194e 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_FairyTreasure.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_FairyTreasure.py
@@ -11,6 +11,7 @@
 # @date 2019-04-09
 # @version 1.0
 # 详细描述: 缥缈宝藏
+# 本副本做前端自定义场景处理,后端无实际地图
 #
 #---------------------------------------------------------------------
 #"""Version = 2019-04-09 11:00"""
@@ -18,56 +19,14 @@
 
 import FBCommon
 import GameWorld
-import IPY_GameWorld
-import NPCCustomRefresh
 import IpyGameDataPY
-import PlayerControl
 import PlayerFairyDomain
-import GameWorldProcess
-import ItemControler
+import ItemCommon
 import NPCCommon
 import ChConfig
-import ChPlayer
-import EventReport
-import ChNPC
-import ItemCommon
 
-
-FBPlayerDict_CurStep = 'FBPlayerDict_CurStep'   # 当前阶段
-FBPlayerDict_StepState = 'FBPlayerDict_StepState'   # 阶段状态
-# 副本通用配置
-(
-Def_PrepareTime, # 准备时间,秒
-Def_FightTime, # 战斗时间,秒
-Def_LeaveTime, # 退出时间, 秒
-Def_CollectTime, # 采集时间, 秒
-Def_RefreshBossMark, # 刷怪标识点
-Def_RefreshBossMark1, # 刷怪标识点
-) = range(6)
-
-
-#当前副本地图的状态
-(
-FB_Step_Open,  # 副本开启
-FB_Step_Prepare,  # 副本等待
-FB_Step_Fighting,  # 副本进行中
-#FB_Step_Collect,  # 副本采集中
-#FB_Step_PickItem, # 拾取物品中
-FB_Step_Over,  # 副本结束
-FB_Step_Close,  # 副本关闭
-) = range(5)
-
-def __GetFBTimeCfg(lineID=0):
-    if not lineID:
-        lineID = FBCommon.GetFBPropertyMark()
-    return FBCommon.GetFBLineStepTime(ChConfig.Def_FBMapID_FairyTreasure, lineID)
 
 ## 是否可进入
-#  @param curPlayer
-#  @param mapID 地图ID
-#  @param lineId 分线ID
-#  @param tick
-#  @return 是否可进入
 def OnEnterFBEvent(curPlayer, mapID, lineId, tick):
     ipyData = IpyGameDataPY.GetIpyGameDataByCondition('FairyDomain', {'MapID':mapID, 'LineID':lineId})
     if not ipyData:
@@ -77,284 +36,18 @@
         return
     return True
 
-
-
-##副本玩家进入点
-# @param curPlayer 玩家实例
-# @param mapID 地图ID
-# @param lineId 分线ID
-# @param ipyEnterPosInfo 功能线路IPY配置坐标信息
-# @param tick 时间戳
-# @return posX, posY, 随机半径(可选)
-def OnGetFBEnterPos(curPlayer, mapID, lineId, ipyEnterPosInfo, tick):
-    return ipyEnterPosInfo
-
-
-## 是否可以进入
-#  @param ask 请求信息
-#  @param tick
-#  @return 回复是否通过请求
-def OnChangeMapAsk(ask, tick):
-    return IPY_GameWorld.cmeAccept
-
-## 进副本
-#  @param curPlayer
-#  @param tick
-#  @return None
-def DoEnterFB(curPlayer, tick):
-    PlayerControl.SetSight(curPlayer, ChConfig.Def_PlayerSight_Default * 3)
-    mapID = GameWorld.GetMap().GetMapID()
-    mapID = FBCommon.GetRecordMapID(mapID)
-    gameFB = GameWorld.GetGameFB()
-    lineID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ReqFBFuncLine)
-    playerID = curPlayer.GetID()
-    hadDelTicket = FBCommon.GetHadDelTicket(curPlayer)
-    if not hadDelTicket:
-        FBCommon.SetHadDelTicket(curPlayer)
-        FBCommon.SetFBPropertyMark(lineID)
-        boxIDList = FBCommon.GetFBLineRefreshNPC(mapID, lineID)[1:]
-        npcCnt = len(boxIDList)
-        NPCCustomRefresh.SetNPCRefresh(__GetFBTimeCfg()[Def_RefreshBossMark1], [[npcID,1] for npcID in boxIDList], npcCnt, npcCnt)
-    if gameFB.GetGameFBDictByKey(FBPlayerDict_CurStep) == 0:
-        FBCommon.SyncDynamicBarrierState(IpyGameDataPY.GetFuncEvalCfg('HazyTreasure'), 1, curPlayer) # 准备期间有动态障碍点
-    if not hadDelTicket \
-        and IpyGameDataPY.GetIpyGameDataByCondition("NPCShow", {"MapID":mapID, "LineID":lineID}, isLogNone=False):
-        GameWorld.DebugLog("刚进入时不直接开始,需等前端通知开始才开始!", playerID)  # 掉线重上强制开始
+## 自定义场景采集OK
+def OnCustomSceneCollectOK(curPlayer, mapID, lineID, npcID):
+    
+    curState = PlayerFairyDomain.GetFairyDomainFBEventState(curPlayer, mapID, lineID)
+    if curState != PlayerFairyDomain.FDEventState_Visiting:
+        GameWorld.DebugLog("非寻访中,不能结算缥缈宝藏采集!", curPlayer.GetPlayerID())
         return
-    
-    fbStep = gameFB.GetFBStep()
-
-    if fbStep < FB_Step_Prepare:
-        FBCommon.SetFBStep(FB_Step_Prepare, tick)
-    
-    if fbStep <= FB_Step_Prepare:
-        notify_tick = __GetFBTimeCfg(lineID)[Def_PrepareTime] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
-        curPlayer.Sync_TimeTick(IPY_GameWorld.tttWaitStart, 0, max(notify_tick, 0), True)
-        
-    elif fbStep == FB_Step_Fighting:
-        notify_tick = __GetFBTimeCfg(lineID)[Def_FightTime] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
-        curPlayer.Sync_TimeTick(IPY_GameWorld.tttTowerTake, 0, max(notify_tick, 0), True)
-    else:
-        PlayerControl.PlayerLeaveFB(curPlayer)
-        return
-    DoFBHelp(curPlayer, tick)
-    return
-
-
-
-## 客户端发送开始副本
-def OnClientStartFB(curPlayer, tick):
-    gameFB = GameWorld.GetGameFB()
-    fbStep = gameFB.GetFBStep()
-    if fbStep >= FB_Step_Prepare:
-        GameWorld.ErrLog("前端请求正式开始副本, 但副本已经开始了,不可重复开始!", curPlayer.GetPlayerID())
-        return
-    
-    lineID = FBCommon.GetFBPropertyMark()
-    GameWorld.DebugLog("前端场景秀已播放完毕,请求正式开始副本!lineID=%s" % lineID, curPlayer.GetPlayerID())
-    FBCommon.SetFBStep(FB_Step_Prepare, tick)
-    notify_tick = __GetFBTimeCfg(lineID)[Def_PrepareTime] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
-    curPlayer.Sync_TimeTick(IPY_GameWorld.tttWaitStart, 0, max(notify_tick, 0), True)
-    DoFBHelp(curPlayer, tick)
-    return
-
-
-## 副本时间到关闭
-#  @param tick 当前时间
-#  @return None
-#  @remarks 函数详细说明.
-def OnCloseFB(tick):
-    return
-
-
-## 获得副本帮助信息
-#  @param curPlayer 当前玩家(被通知对象)
-#  @param tick 当前时间
-#  @return None
-def DoFBHelp(curPlayer, tick):
-    gameFB = GameWorld.GetGameFB()
-    
-    curStep = gameFB.GetGameFBDictByKey(FBPlayerDict_CurStep) + 1
-    helpDict = {FBCommon.Help_step:curStep, FBCommon.Help_npcTotal:gameFB.GetGameFBDictByKey(FBPlayerDict_StepState)}
-    GameWorld.DebugLog("DoFBHelp %s" % str(helpDict))
-    FBCommon.Notify_FBHelp(curPlayer, helpDict)
-    return
-
-##玩家退出副本.
-# @param curPlayer 玩家实例
-# @param tick 时间戳
-# @return 返回值无意义
-# @remarks 玩家主动离开副本.
-def DoExitFB(curPlayer, tick):
-    PlayerControl.SetSight(curPlayer, ChConfig.Def_PlayerSight_Default)
-    # 玩家退出默认关闭副本
-    #GameWorldProcess.CloseFB(tick)
-    return
-
-
-##副本总逻辑计时器
-# @param tick 时间戳
-# @return 无意义
-# @remarks 副本总逻辑计时器
-def OnProcess(tick):
-    gameFB = GameWorld.GetGameFB()
-    fbStep = gameFB.GetFBStep()
-    # 副本准备
-    if fbStep == FB_Step_Prepare:
-        __DoLogic_FB_Prepare(tick)    
-    # 副本进行中
-    elif fbStep == FB_Step_Fighting:
-        __DoLogic_FB_Fighting(tick)
-#    # 副本拾取中
-#    elif fbStep == FB_Step_PickItem:
-#        __DoLogic_FB_PickItem(tick)
-    # 副本结束
-    elif fbStep == FB_Step_Over:
-        __DoLogic_FB_Over(tick)
-    
-    return
-
-
-##战斗准备时间
-# @param tick  时钟
-# @return 无意义
-def __DoLogic_FB_Prepare(tick):
-    gameFB = GameWorld.GetGameFB()
-    mapID = GameWorld.GetMap().GetMapID()
-    lineID = FBCommon.GetFBPropertyMark()
-    trialCfg = __GetFBTimeCfg(lineID)
-    if tick - gameFB.GetFBStepTick() < trialCfg[Def_PrepareTime] * 1000:
-        return
-    FBCommon.Sync_Player_TimeTick(IPY_GameWorld.tttTowerTake, trialCfg[Def_FightTime] * 1000)
-    bossID = FBCommon.GetFBLineRefreshNPC(mapID, lineID)[0]
-    NPCCustomRefresh.SetNPCRefresh(trialCfg[Def_RefreshBossMark], [bossID])
-    #转入战斗
-    FBCommon.SetFBStep(FB_Step_Fighting, tick)
-    return
-
-
-##战斗时间
-# @param tick  时钟
-# @return 无意义
-def __DoLogic_FB_Fighting(tick):
-    gameFB = GameWorld.GetGameFB()
-    #判断时间结束
-    if tick - gameFB.GetFBStepTick() < __GetFBTimeCfg()[Def_FightTime] * 1000:
-        return
-    
-    #游戏结束
-    FBCommon.SetFBStep(FB_Step_Over, tick)
-    return
-
-
-        
-##副本关闭中
-# @param tick:时间戳
-# @return 无意义
-# @remarks 副本关闭中
-def __DoLogic_FB_Over(tick):
-    # 间隔未到
-    if tick - GameWorld.GetGameFB().GetFBStepTick() < __GetFBTimeCfg()[Def_LeaveTime] * 1000:
-        return
-    
-    #副本关闭
-    GameWorldProcess.CloseFB(tick)
-    FBCommon.SetFBStep(FB_Step_Close, tick)
-    return
-
-
-## 杀怪
-#  @param curPlayer
-#  @param curNPC 被杀的怪
-#  @param tick
-#  @return None
-def DoFB_Player_KillNPC(curPlayer, curNPC, tick):
-    mapID = GameWorld.GetMap().GetMapID()
-    lineID = FBCommon.GetFBPropertyMark()
-    bossID = FBCommon.GetFBLineRefreshNPC(mapID, lineID)[0]
-    if bossID != curNPC.GetNPCID():
-        return
-    #刷宝箱进入采集阶段
-    GameWorld.GetGameFB().SetGameFBDict(FBPlayerDict_CurStep, 1)
-    
-    DoFBHelp(curPlayer, tick)
-    FBCommon.SyncDynamicBarrierState(IpyGameDataPY.GetFuncEvalCfg('HazyTreasure'), 0, curPlayer)
-    #特效NPC消失
-    FBCommon.ClearFBNPCEx(FBCommon.GetFBLineRefreshNPC(mapID, lineID)[2:])
-    
-    #FBCommon.SetFBStep(FB_Step_Collect, tick)
-    #NPCCustomRefresh.SetNPCRefresh(__GetFBTimeCfg()[Def_RefreshBossMark], [boxID])
-    return
-
-## 检查是否可攻击, 主判定不可攻击的情况,其他逻辑由外层决定
-#  @param attacker 攻击方
-#  @param defender 防守方
-#  @return bool
-def CheckCanAttackTagObjInFB(attacker, defender):
-    gameFB = GameWorld.GetGameFB()
-    if gameFB.GetFBStep() != FB_Step_Fighting:
-        return False
-    return True
-
-
-##是否可以采集
-# @param curPlayer 玩家实例
-# @param curNPC NPC实例
-# @param tick 时间戳
-# @return 无意义
-# @remarks
-def OnCanCollect(curPlayer, curNPC, tick):
-    gameFB = GameWorld.GetGameFB()
-    curStep = gameFB.GetGameFBDictByKey(FBPlayerDict_CurStep)
-    return curStep == 1
-
-
-##副本中,采集物需要Loading时间.
-# @param curPlayer 玩家实例
-# @param curNPC NPC实例
-# @return 返回值, Loading时间
-# @remarks 副本中,采集物需要Loading时间
-def GetFBPrepareTime(curPlayer, curNPC):
-    return __GetFBTimeCfg()[Def_CollectTime] * 1000
-
-
-##玩家收集成功(塔, 旗)
-# @param curPlayer 玩家实例
-# @param tick 时间戳
-# @return 无意义
-# @remarks
-def OnCollectOK(curPlayer, npcID, tick):
-    playerID = curPlayer.GetID()
-    tagObj = curPlayer.GetActionObj()
-    if not tagObj:
-        return
-    if tagObj.GetGameObjType() != IPY_GameWorld.gotNPC:
-        return
-    
-    curNPC = GameWorld.GetNPCManager().GetNPCByIndex(tagObj.GetIndex())
-    if not curNPC:
-        return
-    dropPosX, dropPosY = curNPC.GetPosX(), curNPC.GetPosY()
-    
-    ChNPC.OnCollectEnd(curPlayer, curNPC)
-    GameWorld.DebugLog('    采集成功!', playerID)
-    GameWorld.GetGameFB().SetGameFBDict(FBPlayerDict_StepState, 1)
-    DoFBHelp(curPlayer, 0)
-    #掉落给奖励
-    DoFairyTreasureOver(curPlayer, tick, dropPosX, dropPosY)
-    
-    
-    return
-
-def DoFairyTreasureOver(curPlayer, tick, dropPosX, dropPosY):
-    #[[(4000,[141,1,1])],[(4000,[141,1,1])]]
-    FBCommon.SetFBStep(FB_Step_Over, tick)
-    
-    lineID = FBCommon.GetFBPropertyMark()
     ipyData = IpyGameDataPY.GetIpyGameDataByCondition('FairyDomain', {'MapID':ChConfig.Def_FBMapID_FairyTreasure, 'LineID':lineID})
     fdeventID = ipyData.GetID()
+    PlayerFairyDomain.SetFairyDomainEventState(curPlayer, fdeventID, PlayerFairyDomain.FDEventState_Visited)
+    
     giveItemList = PlayerFairyDomain.GetFairyAppointAward(curPlayer, fdeventID)
-    dropItemList = []
     if not giveItemList:
         awardCfg = FBCommon.GetFBLineReward(ChConfig.Def_FBMapID_FairyTreasure, lineID)
         curAlchemyLV = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_AlchemyLV)
@@ -376,34 +69,11 @@
             randomitem = GameWorld.GetResultByWeightList(newItemInfoList)
             giveItemList.append(randomitem)
             
-        
     if giveItemList:
-        for randomitem in giveItemList:
-            for _ in xrange(randomitem[1]):
-                dropItemList.append([randomitem[0],1,randomitem[2]])
-        NPCCommon.DoVirtualItemDrop(curPlayer, dropItemList, dropPosX, dropPosY)
-        ItemControler.GivePlayerItemOrMail(curPlayer, giveItemList)
-        leaveTick = __GetFBTimeCfg(lineID)[Def_LeaveTime] * 1000
-        curPlayer.Sync_TimeTick(IPY_GameWorld.tttLeaveMap, 0, leaveTick, True)
-        overDict = {FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(giveItemList)}
-        FBCommon.NotifyFBOver(curPlayer, ChConfig.Def_FBMapID_FairyTreasure, lineID, 1, overDict)
-    
-    PlayerFairyDomain.SetFairyDomainEventState(curPlayer, fdeventID, PlayerFairyDomain.FDEventState_Visited)
+        NPCCommon.DoGiveItemByVirtualDrop(curPlayer, giveItemList, npcID)
+        
+    overDict = {FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(giveItemList)}
+    FBCommon.NotifyFBOver(curPlayer, ChConfig.Def_FBMapID_FairyTreasure, lineID, 1, overDict)
     return
 
 
-## 是否副本复活
-#  @param None
-#  @return 是否副本复活
-def OnPlayerReborn():
-    return True
-
-## 副本行为
-#  @param curPlayer 玩家
-#  @param actionType 行为类型
-#  @param actionInfo 行为信息
-#  @param tick 当前时间
-#  @return None
-def DoFBAction(curPlayer, actionType, actionInfo, tick):
-    
-    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_PersonalBoss.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_PersonalBoss.py
index 4346259..2af46e3 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_PersonalBoss.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_PersonalBoss.py
@@ -2,287 +2,79 @@
 # -*- coding: GBK -*-
 #-------------------------------------------------------------------------------
 #
-#-------------------------------------------------------------------------------
-#
 ##@package GameWorldLogic.FBProcess.GameLogic_PersonalBoss
 #
-# @todo:个人BOSS
-# @author xdh
-# @date 2017-05-05
+# @todo:个人Boss、VIPBoss
+# @author hxp
+# @date 2019-05-23
 # @version 1.0
-# 详细描述: 个人BOSS
 #
-#---------------------------------------------------------------------
-#"""Version = 2017-05-05 11:00"""
-#---------------------------------------------------------------------
+# 详细描述: 个人Boss、VIPBoss
+# 本副本做前端自定义场景处理,后端无实际地图
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2019-05-23 17:30"""
+#-------------------------------------------------------------------------------
 
 import FBCommon
 import GameWorld
-import IPY_GameWorld
-import NPCCustomRefresh
-import IpyGameDataPY
-import PlayerBossReborn
-import PlayerFairyCeremony
-import PlayerNewFairyCeremony
-import ChConfig
-import ChPlayer
 import EventReport
+import PlayerBossReborn
+import PlayerNewFairyCeremony
+import PlayerFairyCeremony
+import IpyGameDataPY
+import NPCCommon
+import ChConfig
 
 
-
-# 副本通用配置
-(
-Def_PrepareTime, # 准备时间,秒
-Def_FightTime, # 战斗时间,秒
-Def_ExitTime, # 退出时间, 秒
-Def_RefreshBossMark, # 刷怪标识点
-) = range(4)
-
-
-
-
-# 副本状态
-(
-FB_State_Open, # 副本开启
-FB_State_FightPrepare, # 战斗准备时间
-FB_State_Fighting, # 战斗
-FB_State_FreeTime, # 活动结束准备(胜利/失败)
-FB_State_Close, # 关闭副本
-) = range(5)
-
-## 是否可进入
-#  @param curPlayer
-#  @param mapID 地图ID
-#  @param lineId 分线ID
-#  @param tick
-#  @return 是否可进入
-def OnEnterFBEvent(curPlayer, mapID, lineId, tick):
+## 是否需要做进入副本通用检查条件逻辑,默认需要检查
+def OnNeedCheckCanEnterFBComm(curPlayer, mapID, lineID):
+    ## 进行中的不需要重复检查,防止断线重连被禁止进入
+    if FBCommon.GetCustomSceneState(curPlayer, mapID, lineID) == ChConfig.CustomSceneState_Fight:
+        GameWorld.DebugLog("VIPBoss已经在进行中,本次进入不需要重新检查!")
+        return False
     return True
 
-
-## 检查可否进行挑战
-def __CheckCanChallenge(curPlayer, bossID):
-    ipyData = IpyGameDataPY.GetIpyGameData('PersonalBoss', bossID)
+## 客户端进入自定义场景
+def OnEnterCustomScene(curPlayer, mapID, lineID):
     
+    if FBCommon.SetCustomSceneStart(curPlayer, mapID, lineID):
+        FBCommon.DelFBEnterTicket(curPlayer, ChConfig.Def_FBMapID_PersonalBoss)
+        #增加进入次数
+        FBCommon.AddEnterFBCount(curPlayer, ChConfig.Def_FBMapID_PersonalBoss)
+        EventReport.WriteEvent_FB(curPlayer, ChConfig.Def_FBMapID_PersonalBoss, 0, ChConfig.CME_Log_Start)
+        PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_VIPBOSS, 1)
+        PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_VIPBoss, 1)
+        PlayerNewFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_VIPBoss, 1)
+        
+    return
+
+## 自定义场景副本击杀NPC
+def DoCustomScene_Player_KillNPC(curPlayer, curNPC, mapID, lineID):
+    
+    npcID = curNPC.GetNPCID()
+    bossID = __GetPersonalBossID(lineID)
+    GameWorld.DebugLog("个人boss场景击杀NPC: npcID=%s,bossID=%s" % (npcID, bossID), curPlayer.GetPlayerID())
+    if npcID != bossID:
+        return
+    
+    if FBCommon.GetCustomSceneState(curPlayer, mapID, lineID) != ChConfig.CustomSceneState_Fight:
+        return
+    FBCommon.SetCustomSceneOver(curPlayer, mapID, lineID)
+    
+    npcCountDict = {bossID:1}
+    dropItemMapInfo = [0, 0]
+    jsonItemList = NPCCommon.GiveKillNPCDropPrize(curPlayer, mapID, npcCountDict, dropItemMapInfo=dropItemMapInfo, isVirtualDrop=True)[0]
+    
+    isPass = 1
+    overDict = {FBCommon.Over_itemInfo:jsonItemList}
+    FBCommon.NotifyFBOver(curPlayer, mapID, lineID, isPass, overDict)
+    return
+
+def __GetPersonalBossID(lineID):
+    ## VIPbossID
+    ipyData = IpyGameDataPY.GetIpyGameData('PersonalBoss', lineID)
     if not ipyData:
-        return False
-    needLV = ipyData.GetChanllengeLv()
-    if curPlayer.GetLV() < needLV:
-        GameWorld.DebugLog('    检查可否进行挑战 bossID-%s  等级不足 %s'%(bossID, needLV))
-        return False
-    delResult = FBCommon.DelFBEnterTicket(curPlayer, ChConfig.Def_FBMapID_PersonalBoss)
-    isOK = delResult[0]
-    if not isOK:
-        return False
-    return True
+        return 0
+    return ipyData.GetNPCID()
 
-
-##副本玩家进入点
-# @param curPlayer 玩家实例
-# @param mapID 地图ID
-# @param lineId 分线ID
-# @param ipyEnterPosInfo 功能线路IPY配置坐标信息
-# @param tick 时间戳
-# @return posX, posY, 随机半径(可选)
-def OnGetFBEnterPos(curPlayer, mapID, lineId, ipyEnterPosInfo, tick):
-    return ipyEnterPosInfo
-
-
-## 是否可以进入
-#  @param ask 请求信息
-#  @param tick
-#  @return 回复是否通过请求
-def OnChangeMapAsk(ask, tick):
-    return IPY_GameWorld.cmeAccept
-
-## 进副本
-#  @param curPlayer
-#  @param tick
-#  @return None
-def DoEnterFB(curPlayer, tick):
-    # 不做处理,有副本行为客户端发包选择挑战关卡
-    return
-
-
-## 副本时间到关闭
-#  @param tick 当前时间
-#  @return None
-#  @remarks 函数详细说明.
-def OnCloseFB(tick):
-    return
-
-
-##玩家退出副本.
-# @param curPlayer 玩家实例
-# @param tick 时间戳
-# @return 返回值无意义
-# @remarks 玩家主动离开副本.
-def DoExitFB(curPlayer, tick):
-    # 玩家退出默认关闭副本
-    #GameWorldProcess.CloseFB(tick)
-    return
-
-
-##副本总逻辑计时器
-# @param tick 时间戳
-# @return 无意义
-# @remarks 副本总逻辑计时器
-def OnProcess(tick):
-    gameFB = GameWorld.GetGameFB()
-    fbStep = gameFB.GetFBStep()
-    
-    if fbStep == FB_State_FightPrepare:
-        __DoLogic_FightPrepare(tick)
-    elif fbStep == FB_State_Fighting:
-        __DoLogic_Fighting(tick)
-    elif fbStep == FB_State_FreeTime:
-        __DoLogic_FreeTime(tick)
-    elif fbStep == FB_State_Close:
-        pass
-    
-    return
-
-
-##战斗准备时间
-# @param tick  时钟
-# @return 无意义
-def __DoLogic_FightPrepare(tick):
-    gameFB = GameWorld.GetGameFB()
-    mapID = GameWorld.GetMap().GetMapID()
-    trialCfg = FBCommon.GetFBLineStepTime(mapID)
-    if tick - gameFB.GetFBStepTick() < trialCfg[Def_PrepareTime] * 1000:
-        return
-    
-    bossID = FBCommon.GetFBPropertyMark()
-    if not bossID:
-        FBCommon.DoLogic_FBKickAllPlayer()
-        return
-    
-    FBCommon.Sync_Player_TimeTick(IPY_GameWorld.tttTowerTake, trialCfg[Def_FightTime] * 1000)
-    NPCCustomRefresh.SetNPCRefresh(FBCommon.GetFBLineStepTime(mapID)[Def_RefreshBossMark], [bossID])
-    
-    #转入战斗
-    FBCommon.SetFBStep(FB_State_Fighting, tick)
-    return
-
-## 开始副本关卡
-def StartFBLevel(curPlayer, bossID, tick):
-    FBCommon.SetFBPropertyMark(bossID)
-    if curPlayer.GetPlayerAction() == IPY_GameWorld.paDie:
-        GameWorld.DebugLog("复活玩家...", curPlayer.GetPlayerID())
-        ChPlayer.PlayerRebornByType(curPlayer, ChConfig.rebornType_City, tick)
-    #增加进入次数
-    FBCommon.AddEnterFBCount(curPlayer, ChConfig.Def_FBMapID_PersonalBoss)
-    EventReport.WriteEvent_FB(curPlayer, ChConfig.Def_FBMapID_PersonalBoss, 0, ChConfig.CME_Log_Start)
-    PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_VIPBOSS, 1)
-    PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_VIPBoss, 1)
-    PlayerNewFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_VIPBoss, 1)
-    
-    FBCommon.ClearFBNPC()
-    
-    #gameFB = GameWorld.GetGameFB()
-    mapID = GameWorld.GetMap().GetMapID()
-    prepareTick = FBCommon.GetFBLineStepTime(mapID)[Def_PrepareTime] * 1000
-    FBCommon.Sync_Player_TimeTick(IPY_GameWorld.tttAddUpTime, prepareTick)
-    FBCommon.Sync_Player_TimeTick(IPY_GameWorld.tttWaitStart, prepareTick)
-    FBCommon.SetFBStep(FB_State_FightPrepare, tick)
-    helpDict = {FBCommon.Help_npcTotal:0}
-    FBCommon.Notify_FBHelp(curPlayer, helpDict)
-    GameWorld.DebugLog("StartFBLevel, fbLevel=%s, helpDict=%s" 
-                       % (bossID, str(helpDict)), curPlayer.GetPlayerID())
-    return
-
-
-##战斗时间
-# @param tick  时钟
-# @return 无意义
-def __DoLogic_Fighting(tick):
-    gameFB = GameWorld.GetGameFB()
-    mapID = GameWorld.GetMap().GetMapID()
-    #判断时间结束
-    if tick - gameFB.GetFBStepTick() < FBCommon.GetFBLineStepTime(mapID)[Def_FightTime] * 1000:
-        return
-    
-    #游戏结束
-    __SetFBToFreeTime(tick)
-    return
-
-##设置副本进入离开状态
-# @param tick  时钟
-# @return 无意义
-def __SetFBToFreeTime(tick):
-    mapID = GameWorld.GetMap().GetMapID()
-    FBCommon.Sync_Player_TimeTick(IPY_GameWorld.tttLeaveMap, FBCommon.GetFBLineStepTime(mapID)[Def_ExitTime] * 1000)
-    FBCommon.SetFBStep(FB_State_FreeTime, tick)
-    return
-
-##比赛结束的空闲时间
-# @param tick  时钟
-# @return 无意义
-# @remarks 比赛结束的空闲时间
-def __DoLogic_FreeTime(tick):
-    mapID = GameWorld.GetMap().GetMapID()
-    if tick - GameWorld.GetGameFB().GetFBStepTick() < FBCommon.GetFBLineStepTime(mapID)[Def_ExitTime] * 1000:
-        return
-    
-    FBCommon.DoLogic_FBKickAllPlayer()
-    return
-
-## 杀怪
-#  @param curPlayer
-#  @param curNPC 被杀的怪
-#  @param tick
-#  @return None
-def DoFB_Player_KillNPC(curPlayer, curNPC, tick):
-    
-    bossID = FBCommon.GetFBPropertyMark()
-    if bossID != curNPC.GetNPCID():
-        return
-    helpDict = {FBCommon.Help_npcTotal:1}
-    FBCommon.Notify_FBHelp(curPlayer, helpDict)
-
-    __SetFBToFreeTime(tick)
-    return
-
-## 检查是否可攻击, 主判定不可攻击的情况,其他逻辑由外层决定
-#  @param attacker 攻击方
-#  @param defender 防守方
-#  @return bool
-def CheckCanAttackTagObjInFB(attacker, defender):
-    gameFB = GameWorld.GetGameFB()
-    if gameFB.GetFBStep() != FB_State_Fighting:
-        return False
-    return True
-
-
-## 是否副本复活
-#  @param None
-#  @return 是否副本复活
-def OnPlayerReborn():
-    return True
-
-## 副本行为
-#  @param curPlayer 玩家
-#  @param actionType 行为类型
-#  @param actionInfo 行为信息
-#  @param tick 当前时间
-#  @return None
-def DoFBAction(curPlayer, actionType, actionInfo, tick):
-    # 默认为选择关卡,由客户端决定,进场及副本选关通用此行为
-    if actionInfo <= 0:
-        return
-    
-    gameFB = GameWorld.GetGameFB()
-    fbStep = gameFB.GetFBStep()
-    
-    if fbStep in [FB_State_FightPrepare, FB_State_Fighting]:
-        GameWorld.DebugLog("准备或战斗中, 无法变更关卡!")
-        return
-    
-    bossid = actionInfo
-    if not __CheckCanChallenge(curPlayer, bossid):
-        FBCommon.DoLogic_FBKickAllPlayer()
-        return
-    
-    StartFBLevel(curPlayer, bossid, tick)
-    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
index 7dbb6a7..80004a6 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -731,8 +731,8 @@
                         ),
 
                 "PersonalBoss":(
-                        ("DWORD", "NPCID", 1),
-                        ("DWORD", "ChanllengeLv", 0),
+                        ("DWORD", "NPCID", 0),
+                        ("DWORD", "FuncLineID", 1),
                         ),
 
                 "FamilyActivity":(
@@ -2923,11 +2923,11 @@
     
     def __init__(self):
         self.NPCID = 0
-        self.ChanllengeLv = 0
+        self.FuncLineID = 0
         return
         
     def GetNPCID(self): return self.NPCID # ID
-    def GetChanllengeLv(self): return self.ChanllengeLv # 可挑战等级
+    def GetFuncLineID(self): return self.FuncLineID # 可挑战等级
 
 # 仙盟活跃表
 class IPY_FamilyActivity():
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py
index 2c2aee9..dbaf5d3 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py
@@ -127,6 +127,7 @@
 #  @return None
 #  @remarks 函数详细说明.
 def OnNPCAttacked(atkObj, curNPC, skill, tick):
+    NPCCommon.OnNPCAttacked(atkObj, curNPC, skill, tick)
     callFunc = GameWorld.GetExecFunc(NPCAI, "AIType_%d.%s"%(curNPC.GetAIType(), "OnAttacked"))
     if callFunc == None:
         return None
@@ -134,6 +135,7 @@
     callFunc(atkObj, curNPC, skill, tick)
     
     PlayerActivity.OnAttackNPCActivity(atkObj, curNPC)
+    return
 
 def OnCheckCanDie(atkObj, curNPC, skill, tick):
     callFunc = GameWorld.GetExecFunc(NPCAI, "AIType_%d.%s"%(curNPC.GetAIType(), "OnCheckCanDie"))
@@ -437,7 +439,7 @@
         GameWorld.GetGameFB().SetGameFBDict(ChConfig.Def_FBDict_NPCShowEndTick % npcID, 0)
         GameWorld.DebugLog("NPC秀结束,开始处理AI!npcID=%s,tick=%s,endTick=%s" % (npcID, tick, endTick))
         
-    callFunc = GameWorld.GetExecFunc(NPCAI, "AIType_%d.%s"%(curNPC.GetAIType(), "ProcessAI"))
+    callFunc = GameWorld.GetExecFunc(NPCAI, "AIType_%s.%s"%(GetAIName(curNPC), "ProcessAI"))
     if callFunc == None:
         #NPCAI不可使用
 #        #默认call类型1的AI
@@ -447,7 +449,12 @@
         return
     
     callFunc(curNPC, tick)
+    return
 
+def GetAIName(curNPC):
+    if curNPC.GetType() in [ChConfig.ntPriWoodPilePVE, ChConfig.ntPriWoodPilePVP]:
+        return "PriWood"
+    return curNPC.GetAIType()
 
 ##################################################logic
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_196.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_196.py
index d77b4ee..3242144 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_196.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_196.py
@@ -20,12 +20,8 @@
 import GameWorld
 import NPCCommon
 import IPY_GameWorld
-import PlayerControl
-import IpyGameDataPY
-import AttackCommon
-import ItemCommon
+import AICommon
 import GameObj
-import ChItem
 
 import random
 
@@ -246,14 +242,10 @@
 #  @param tick 
 #  @return 具体伤害值
 def OnAttacked(atkObj, curNPC, skill, tick):
-    npcControl = NPCCommon.NPCControl(curNPC)
     if GameObj.GetHP(curNPC) < GameObj.GetMaxHP(curNPC) / 2:
         GameObj.SetHP(curNPC, GameObj.GetMaxHP(curNPC))
         GameWorld.DebugLog("半血回满血!")
     curNPC.SetDict(Def_NPCKey_Goblin_AttackedTick, tick) # 设置被攻击时间
-    
-    # 每次被攻击掉落物品
-    __OnAttackedDropItem(atkObj, curNPC, npcControl)
     return
 
 def OnCheckCanDie(atkObj, curNPC, skill, tick):
@@ -261,65 +253,4 @@
     GameObj.SetHP(curNPC, GameObj.GetMaxHP(curNPC))
     GameWorld.DebugLog("死亡回满血!")
     return False
-
-
-## 每次被攻击掉落物品
-#  @param atkObj 攻击发起者
-#  @param curNPC 被攻击NPC
-#  @return None
-def __OnAttackedDropItem(atkObj, curNPC, npcControl):
-    attackPlayer, npcObjType = AttackCommon.GetAttackPlayer(atkObj)
-    if npcObjType:
-        return
-    if not attackPlayer:
-        return
-    npcID = curNPC.GetNPCID()
-    ipyData = IpyGameDataPY.GetIpyGameDataNotLog("TreasureNPC", npcID)
-    if not ipyData:
-        return
-    attackCountDropWeightInfo = ipyData.GetAttackCountDropWeightInfo()
-    attackDropWeightList = ipyData.GetAttackDropWeightList()
-    attackDropWeightListEx = ipyData.GetAttackDropWeightListEx()
-    dropCountEx = ipyData.GetDropCountEx()
-    alchemyDiffLV = ipyData.GetAlchemyDiffLV()
-    
-    mainItemWeightList = []
-    if attackCountDropWeightInfo:
-        maxCount = max(attackCountDropWeightInfo)
-        attackCount = attackPlayer.NomalDictGetProperty(ChConfig.Def_PDict_NPCAttackCount % npcID) + 1
-        if attackCount <= maxCount:
-            if attackCount in attackCountDropWeightInfo:
-                mainItemWeightList = attackCountDropWeightInfo[attackCount]
-            NPCCommon.UpdateNPCAttackCount(attackPlayer, npcID, attackCount, maxCount)
-    
-    if mainItemWeightList:
-        mainItemWeightList = ItemCommon.GetWeightItemListByAlchemyDiffLV(attackPlayer, mainItemWeightList, alchemyDiffLV)
-    elif attackDropWeightList:
-        mainItemWeightList = ItemCommon.GetWeightItemListByAlchemyDiffLV(attackPlayer, attackDropWeightList, alchemyDiffLV)
-        
-    mainItemInfo = GameWorld.GetResultByWeightList(mainItemWeightList)
-    
-    if not mainItemInfo:
-        notDropNotify = ipyData.GetNotDropNotify()
-        if notDropNotify:
-            PlayerControl.NotifyCode(attackPlayer, notDropNotify)
-        return
-    
-    dropItemList = []
-    if mainItemInfo:
-        dropItemList.append(mainItemInfo)
-        
-    if attackDropWeightListEx and dropCountEx:
-        weightListEx = ItemCommon.GetWeightItemListByAlchemyDiffLV(attackPlayer, attackDropWeightListEx, alchemyDiffLV)
-        for _ in xrange(dropCountEx):
-            itemInfo = GameWorld.GetResultByWeightList(weightListEx)
-            if itemInfo:
-                dropItemList.append(itemInfo)
-                
-    if not dropItemList:
-        return
-    
-    dropPosX, dropPosY = curNPC.GetPosX(), curNPC.GetPosY()
-    ChItem.DoMapDropItem(attackPlayer, dropItemList, npcID, dropPosX, dropPosY, isOnlySelfSee=False)
-    return
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_202.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_PriWood.py
similarity index 89%
rename from ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_202.py
rename to ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_PriWood.py
index fd58232..8dcbc23 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_202.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_PriWood.py
@@ -2,17 +2,17 @@
 # -*- coding: GBK -*-
 #-------------------------------------------------------------------------------
 #
-##@package NPCAI.AIType_202
+##@package NPCAI.AIType_PriWood
 #
 # @todo:专属木桩
 # @author hxp
-# @date 2019-04-29
+# @date 2019-05-23
 # @version 1.0
 #
 # 详细描述: 跟随主人
 #
 #-------------------------------------------------------------------------------
-#"""Version = 2019-04-29 17:00"""
+#"""Version = 2019-05-23 17:30"""
 #-------------------------------------------------------------------------------
 
 import IPY_GameWorld
@@ -37,7 +37,7 @@
     npcControl.RefreshBuffState(tick)
     
     owner = NPCCommon.GetNpcObjOwnerDetail(curNPC)
-    if GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(), owner.GetPosX(), owner.GetPosY()) > 5:
+    if GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(), owner.GetPosX(), owner.GetPosY()) > 3:
         resetPos = GameMap.GetEmptyPlaceInArea(owner.GetPosX(), owner.GetPosY(), 2)
         curNPC.ResetPos(resetPos.GetPosX(), resetPos.GetPosY())
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
index 382d5a4..2442525 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
@@ -397,7 +397,7 @@
     return attrDict
 
 def GiveKillNPCDropPrize(curPlayer, mapID, npcCountDict, exp_rate=None, mailTypeKey=None, isMail=False, 
-                         extraItemList=[], prizeMultiple=1, dropItemMapInfo=[], curGrade=0):
+                         extraItemList=[], prizeMultiple=1, dropItemMapInfo=[], curGrade=0, isVirtualDrop=False):
     '''给玩家击杀NPC掉落奖励
     @param mapID: 击杀的NPC所在地图ID,注意次地图并不一定是玩家当前地图
     @param npcCountDict: 执行单次时所击杀的npc数量字典 {npcID:count, ...}
@@ -406,6 +406,9 @@
     @param isMail: 是否强制发送邮件,若是则不考虑背包空间,否的话只在背包空间不足时才发送邮件
     @param extraItemList: 固定附加物品列表,如果需执行多次,则此固定产出列表需在外层处理好,内层不做多次执行处理。[[itemID, itemCount, isAuctionItem], ...]
     @param prizeMultiple: 奖励倍值, 对所有奖励有效,等于击杀多次NPC,多倍附加物品
+    @param dropItemMapInfo: 掉落地板信息 [dropPosX, dropPosY, 是否仅自己可见, 堆叠物品是否散开]
+    @param curGrade: 评级
+    @param isVirtualDrop: 是否给物品虚拟掉落表现
     '''
     if not exp_rate:
         exp_rate = PlayerControl.GetPlayerExpRate(curPlayer)
@@ -508,45 +511,15 @@
         
     ## 直接掉地板上
     if dropItemMapInfo:
-        dropPosX, dropPosY, isOnlySelfSee = dropItemMapInfo[:3]
+        dropPosX, dropPosY = dropItemMapInfo[:2]
+        isOnlySelfSee = dropItemMapInfo[2] if len(dropItemMapInfo) > 2 else False # 是否仅自己可见
         isDropDisperse = dropItemMapInfo[3] if len(dropItemMapInfo) > 3 else False # 堆叠的物品是否散开掉落
-        if isDropDisperse:
-            dropItemList = []
-            for itemInfo in prizeItemList:
-                if isinstance(itemInfo, list):
-                    itemID, itemCount, isAuctionItem = itemInfo
-                    for _ in xrange(itemCount):
-                        dropItemList.append([itemID, 1, isAuctionItem])
-                else:
-                    dropItemList.append(itemInfo)
+        ## 虚拟掉落表现    
+        if isVirtualDrop:
+            DoGiveItemByVirtualDrop(curPlayer, prizeItemList, npcID, dropPosX, dropPosY, isDropDisperse, mailTypeKey)
         else:
-            dropItemList = prizeItemList
-        index = 0
-        playerID = curPlayer.GetPlayerID()
-        gameMap = GameWorld.GetMap()
-        for posX, posY in ChConfig.Def_DropItemAreaMatrix:
-            resultX = dropPosX + posX
-            resultY = dropPosY + posY
+            DoMapDropPrizeItem(curPlayer, prizeItemList, npcID, dropPosX, dropPosY, isDropDisperse, isOnlySelfSee)
             
-            if not gameMap.CanMove(resultX, resultY):
-                #玩家不可移动这个点
-                continue
-            
-            if index > len(dropItemList) - 1:
-                break
-            
-            curItem = dropItemList[index]
-            index += 1
-            if isinstance(curItem, list):
-                itemID, itemCount, isAuctionItem = curItem
-                curItem = ItemControler.GetOutPutItemObj(itemID, itemCount, isAuctionItem)
-                
-            if not curItem:
-                continue
-            
-            ChItem.AddMapDropItem(resultX, resultY, curItem, ownerInfo=[ChConfig.Def_NPCHurtTypePlayer, playerID], 
-                                  dropNPCID=npcID, isOnlySelfSee=isOnlySelfSee)
-    
     ## 发邮件 或 背包空间不足
     elif isMail or needSpace > ItemCommon.GetItemPackSpace(curPlayer, IPY_GameWorld.rptItem, needSpace):
         mailItemList = []
@@ -602,6 +575,110 @@
         dropItemDataStr = ChItem.GetMapDropItemDataStr(curItem)
         SendVirtualItemDrop(curPlayer, itemID, resultX, resultY, dropItemDataStr)
         curItem.Clear()
+    return
+
+def DoMapDropPrizeItem(curPlayer, prizeItemList, npcID, dropPosX, dropPosY, isDropDisperse=True, isOnlySelfSee=True):
+    ## 奖励物品真实掉落地图,先拆开分散再掉落
+    
+    if isDropDisperse:
+        dropItemList = []
+        for itemInfo in prizeItemList:
+            if isinstance(itemInfo, list):
+                itemID, itemCount, isAuctionItem = itemInfo
+                for _ in xrange(itemCount):
+                    dropItemList.append([itemID, 1, isAuctionItem])
+            else:
+                dropItemList.append(itemInfo)
+    else:
+        dropItemList = prizeItemList
+    index = 0
+    playerID = curPlayer.GetPlayerID()
+    gameMap = GameWorld.GetMap()
+    for posX, posY in ChConfig.Def_DropItemAreaMatrix:
+        resultX = dropPosX + posX
+        resultY = dropPosY + posY
+        
+        if not gameMap.CanMove(resultX, resultY):
+            #玩家不可移动这个点
+            continue
+        
+        if index > len(dropItemList) - 1:
+            break
+        
+        curItem = dropItemList[index]
+        index += 1
+        if isinstance(curItem, list):
+            itemID, itemCount, isAuctionItem = curItem
+            curItem = ItemControler.GetOutPutItemObj(itemID, itemCount, isAuctionItem)
+            
+        if not curItem:
+            continue
+        
+        ChItem.AddMapDropItem(resultX, resultY, curItem, ownerInfo=[ChConfig.Def_NPCHurtTypePlayer, playerID], 
+                              dropNPCID=npcID, isOnlySelfSee=isOnlySelfSee)
+    return
+
+def DoGiveItemByVirtualDrop(curPlayer, giveItemList, npcID, dropPosX=0, dropPosY=0, isDropDisperse=True, mailTypeKey="ItemNoPickUp"):
+    ## 给物品并且做假掉落表现,直接先堆叠给物品,再拆开做虚假掉落表现
+    
+    mapID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneMapID)
+    #lineID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneLineID)
+    if not mapID:
+        mapID = GameWorld.GetGameWorld().GetMapID()
+        
+    playerID = curPlayer.GetPlayerID()
+    mailItemList = []
+    virtualItemDropList = []
+    itemControl = ItemControler.PlayerItemControler(curPlayer)
+    for itemInfo in giveItemList:
+        if isinstance(itemInfo, list):
+            itemID, itemCount, isAuctionItem = itemInfo
+            curItem = ItemControler.GetOutPutItemObj(itemID, itemCount, isAuctionItem, curPlayer=curPlayer)
+            if not curItem:
+                continue
+        else:
+            curItem = itemInfo
+            itemID = curItem.GetItemTypeID()
+            itemCount = curItem.GetCount()
+            isAuctionItem = ItemControler.GetIsAuctionItem(curItem)
+        jsonItem = ItemCommon.GetJsonItem(curItem)
+        dropItemDataStr = ChItem.GetMapDropItemDataStr(curItem)
+        equipInfo = [curItem.GetEquipPlace(), ItemCommon.GetItemClassLV(curItem), curItem.GetItemColor(), 
+                     curItem.GetItemQuality(), curItem.GetUserData()]
+        packIndex = ChConfig.GetItemPackType(curItem.GetType())
+        if not itemControl.PutInItem(packIndex, curItem, event=[ChConfig.ItemGive_Pickup, False, {"NPCID":npcID}]):
+            mailItemList.append(jsonItem)
+            
+        if npcID:
+            serverGroupID = PlayerControl.GetPlayerServerGroupID(curPlayer)
+            SendGameServerGoodItemRecord(mapID, npcID, curPlayer.GetName(), playerID, itemID, equipInfo, serverGroupID)
+            
+        # 散开掉落
+        if isDropDisperse:
+            for _ in xrange(itemCount):
+                virtualItemDropList.append([itemID, dropItemDataStr])
+        else:
+            virtualItemDropList.append([itemID, dropItemDataStr])
+            
+    if mailItemList:
+        PlayerControl.SendMailByKey(mailTypeKey, [playerID], mailItemList, [mapID])
+        
+    gameMap = GameWorld.GetMap()
+    index = 0
+    for posX, posY in ChConfig.Def_DropItemAreaMatrix:
+        if dropPosX or dropPosY:
+            resultX = dropPosX + posX
+            resultY = dropPosY + posY
+            if not gameMap.CanMove(resultX, resultY):
+                #玩家不可移动这个点
+                continue
+        else:
+            resultX, resultY = 0, 0
+        if index > len(virtualItemDropList) - 1:
+            break
+        itemID, dropItemDataStr = virtualItemDropList[index]
+        index += 1
+        SendVirtualItemDrop(curPlayer, itemID, resultX, resultY, dropItemDataStr)
     return
 
 ################################### NPC掉落 ###################################
@@ -1959,6 +2036,17 @@
     ''' 召唤私有专属木桩怪
     '''
     
+    if not curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomScene):
+        GameWorld.DebugLog("玩家当前不是在自定义场景中,不允许招木桩!")
+        return
+    
+    mapID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneMapID)
+    lineID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneLineID)
+    if mapID:
+        if not FBLogic.OnCanSummonPriWoodPile(curPlayer, mapID, lineID, npcID, count):
+            GameWorld.ErrLog("无法召唤木桩怪!mapID=%s,lineID=%s,npcID=%s,count=%s" % (mapID, lineID, npcID, count))
+            return
+        
     maxCount = 10
     nowCount = 0
     # 只允许存在一个私有木桩
@@ -1999,6 +2087,37 @@
         summonPos = GameMap.GetEmptyPlaceInArea(curPlayer.GetPosX(), curPlayer.GetPosY(), 3)
         summonNPC.Reborn(summonPos.GetPosX(), summonPos.GetPosY())
         
+        if not curPlayer.GetSight():
+            summonNPCAppear = ChNetSendPack.tagPlayerSummonNPCAppear()
+            summonNPCAppear.Clear()
+            summonNPCAppear.PlayerID = curPlayer.GetPlayerID()
+            summonNPCAppear.ObjID = summonNPC.GetID()
+            summonNPCAppear.NPCID = summonNPC.GetNPCID()
+            summonNPCAppear.PosX = summonNPC.GetPosX()
+            summonNPCAppear.PosY = summonNPC.GetPosY()
+            summonNPCAppear.HP = summonNPC.GetHP()
+            summonNPCAppear.HPEx = summonNPC.GetHPEx()
+            summonNPCAppear.MaxHP = summonNPC.GetMaxHP()
+            summonNPCAppear.MaxHPEx = summonNPC.GetMaxHPEx()
+            summonNPCAppear.Speed = summonNPC.GetSpeed()
+            summonNPCAppear.LV = GetNPCLV(summonNPC)
+            summonNPCAppear.OwnerName = curPlayer.GetPlayerName()
+            summonNPCAppear.OwnerNameLen = len(summonNPCAppear.OwnerName)
+            NetPackCommon.SendFakePack(curPlayer, summonNPCAppear)
+            
+    return
+
+def ClearPriWoodPile(curPlayer):
+    ## 清除私有木桩
+    indexList = range(curPlayer.GetSummonCount())
+    for index in indexList[::-1]:
+        summonNPC = curPlayer.GetSummonNPCAt(index)
+        if not summonNPC:
+            continue
+        npcType = summonNPC.GetType()
+        if npcType not in [ChConfig.ntPriWoodPilePVE, ChConfig.ntPriWoodPilePVP]:
+            continue
+        SetDeadEx(summonNPC)
     return
 
 ## 设置npc死亡及自身处理(请不要将游戏逻辑加在此函数中)
@@ -2019,8 +2138,10 @@
     if curNPC.GetGameObjType() == IPY_GameWorld.gotNPC:
         FBLogic.DoFB_NPCDead(curNPC)
     
+    ownerPlayer = None
     summonPlayerID = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_SummonMapNPCPlayerID)
     if summonPlayerID > 0:
+        ownerPlayer = GameWorld.GetObj(summonPlayerID, IPY_GameWorld.gotPlayer)
         curNPC.SetDict(ChConfig.Def_NPC_Dict_SummonMapNPCPlayerID, 0)
     
     # 暗金boss
@@ -2047,6 +2168,14 @@
         lineRobotJobDict = PyGameData.g_fbRobotJobDict.get(lineID, {})
         lineRobotJobDict.pop(curNPC.GetID(), 0)
         PyGameData.g_fbRobotJobDict[lineID] = lineRobotJobDict
+        
+    if ownerPlayer and not ownerPlayer.GetSight():
+        npcDie = ChNetSendPack.tagNPCDie()
+        npcDie.ObjID = curNPC.GetID()
+        npcDie.Reason = curNPC.GetDictByKey(ChConfig.Def_NPCDead_Reason)
+        npcDie.KillerType = curNPC.GetDictByKey(ChConfig.Def_NPCDead_KillerType)
+        npcDie.KillerID = curNPC.GetDictByKey(ChConfig.Def_NPCDead_KillerID)
+        NetPackCommon.SendFakePack(ownerPlayer, npcDie)
         
     # C++设置npc死亡
     curNPC.SetDead(curNPC.GetDictByKey(ChConfig.Def_NPCDead_Reason),
@@ -3344,7 +3473,8 @@
         
         #得到范围内随机一个点, 普通小怪走法
         PosMap = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
-
+        if not PosMap:
+            return
         moveArea = min(curNPC.GetMoveArea(), 2)
 
         posX = curNPC.GetPosX()
@@ -4152,6 +4282,9 @@
     #  @return 返回值无意义
     def __NPCDropItem(self, dropPlayer, hurtType, hurtID, ownerPlayerList=[]):
         if not dropPlayer:
+            return
+        if dropPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomScene):
+            GameWorld.DebugLog("前端自定义场景中,不掉落物品!")
             return
         curNPC = self.__Instance
         npcID = curNPC.GetNPCID()
@@ -5729,6 +5862,15 @@
     if not curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomScene):
         GameWorld.DebugLog("非自定义场景中,无法获取定义采集奖励!")
         return
+    mapID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneMapID)
+    lineID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneLineID)
+    GameWorld.DebugLog("前端场景采集: mapID=%s,lineID=%s,npcID=%s" % (mapID, lineID, npcID))
+    
+    if mapID:
+        #if FBCommon.GetCustomSceneState(curPlayer, mapID, lineID) != ChConfig.CustomSceneState_Fight:
+        #    return
+        FBLogic.OnCustomSceneCollectOK(curPlayer, mapID, lineID, npcID)
+                
     collectNPCIpyData = IpyGameDataPY.GetIpyGameData("CollectNPC", npcID)
     if collectNPCIpyData:
         DoGiveCollectNPCAward(curPlayer, npcID, collectNPCIpyData)
@@ -5764,11 +5906,20 @@
     collectAwardCfg = collectNPCIpyData.GetCollectAward()
     collectAppointAwardCfg = collectNPCIpyData.GetCollectAppointAward()
     if collectAppointAwardCfg:
-        collTotalTime = min(curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcIDCollTimeTotal % npcID) + 1, ChConfig.Def_UpperLimit_DWord)
-        if collTotalTime in collectAppointAwardCfg:
-            awardItemList.append(collectAppointAwardCfg[collTotalTime])
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcIDCollTimeTotal % npcID, collTotalTime)
-        GameWorld.DebugLog("    采集次数定制奖励: collTotalTime=%s,awardItemList=%s" % (collTotalTime, awardItemList))
+        #缥缈草园的采集定制由缥缈寻访次数决定
+        if collectNPCIpyData.GetCollectResetType() in [12, 14]:
+            fairyDomainVisitCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FairyDomainVisitCnt)
+            grasslandCollectAppointCfg = collectAppointAwardCfg.get(fairyDomainVisitCnt, {})
+            curCollTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcIDCollTime % npcID)
+            if curCollTime in grasslandCollectAppointCfg:
+                awardItemList.append(grasslandCollectAppointCfg[curCollTime])
+            GameWorld.DebugLog("    草园采集定制奖励: fairyDomainVisitCnt=%s,curCollTime=%s,awardItemList=%s" % (fairyDomainVisitCnt, curCollTime, awardItemList))
+        else:
+            collTotalTime = min(curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CollNpcIDCollTimeTotal % npcID) + 1, ChConfig.Def_UpperLimit_DWord)
+            if collTotalTime in collectAppointAwardCfg:
+                awardItemList.append(collectAppointAwardCfg[collTotalTime])
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CollNpcIDCollTimeTotal % npcID, collTotalTime)
+            GameWorld.DebugLog("    采集次数定制奖励: collTotalTime=%s,awardItemList=%s" % (collTotalTime, awardItemList))
         
     if not awardItemList:
         alchemyDiffLV = collectNPCIpyData.GetAlchemyDiffLV()
@@ -5806,6 +5957,7 @@
                 awardPack.AwardItemList.append(awardItem)
             awardPack.Count = len(awardPack.AwardItemList)
             NetPackCommon.SendFakePack(curPlayer, awardPack)
+        GameLogic_CrossGrassland.RecordGrasslandAward(curPlayer, awardItemList)
     else:
         GameWorld.ErrLog("采集物品没有奖励!npcID=%s" % (npcID))
         
@@ -5813,6 +5965,7 @@
     PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_Collect, collectCnt, [npcID])
     #SyncCollectionItemInfo(curPlayer, addExp, addMoney, addZhenQi, giveItemInfoList, npcID)
     
+    GameLogic_CrossGrassland.DecCustomSceneNPCCount(curPlayer, npcID)
     if isMaxTime:
         GameLogic_CrossGrassland.DoCheckUpdateGrasslandEnd(curPlayer)
         
@@ -6218,4 +6371,74 @@
     return
 
 
+def OnNPCAttacked(atkObj, curNPC, skill, tick):
+    ## NPC被攻击
+    __OnAttackedDropItem(atkObj, curNPC)
+    return
+
+## 每次被攻击掉落物品
+#  @param atkObj 攻击发起者
+#  @param curNPC 被攻击NPC
+#  @return None
+def __OnAttackedDropItem(atkObj, curNPC):
+    attackPlayer, npcObjType = AttackCommon.GetAttackPlayer(atkObj)
+    if npcObjType:
+        return
+    if not attackPlayer:
+        return
+    npcID = curNPC.GetNPCID()
+    ipyData = IpyGameDataPY.GetIpyGameDataNotLog("TreasureNPC", npcID)
+    if not ipyData:
+        return
+    attackCountDropWeightInfo = ipyData.GetAttackCountDropWeightInfo()
+    attackDropWeightList = ipyData.GetAttackDropWeightList()
+    attackDropWeightListEx = ipyData.GetAttackDropWeightListEx()
+    dropCountEx = ipyData.GetDropCountEx()
+    alchemyDiffLV = ipyData.GetAlchemyDiffLV()
+    
+    mainItemWeightList = []
+    if attackCountDropWeightInfo:
+        maxCount = max(attackCountDropWeightInfo)
+        attackCount = attackPlayer.NomalDictGetProperty(ChConfig.Def_PDict_NPCAttackCount % npcID) + 1
+        if attackCount <= maxCount:
+            if attackCount in attackCountDropWeightInfo:
+                mainItemWeightList = attackCountDropWeightInfo[attackCount]
+            UpdateNPCAttackCount(attackPlayer, npcID, attackCount, maxCount)
+            
+    if mainItemWeightList:
+        mainItemWeightList = ItemCommon.GetWeightItemListByAlchemyDiffLV(attackPlayer, mainItemWeightList, alchemyDiffLV)
+    elif attackDropWeightList:
+        mainItemWeightList = ItemCommon.GetWeightItemListByAlchemyDiffLV(attackPlayer, attackDropWeightList, alchemyDiffLV)
+        
+    mainItemInfo = GameWorld.GetResultByWeightList(mainItemWeightList)
+    
+    if not mainItemInfo:
+        notDropNotify = ipyData.GetNotDropNotify()
+        if notDropNotify:
+            PlayerControl.NotifyCode(attackPlayer, notDropNotify)
+        return
+    
+    dropItemList = []
+    if mainItemInfo:
+        dropItemList.append(mainItemInfo)
+        
+    if attackDropWeightListEx and dropCountEx:
+        weightListEx = ItemCommon.GetWeightItemListByAlchemyDiffLV(attackPlayer, attackDropWeightListEx, alchemyDiffLV)
+        for _ in xrange(dropCountEx):
+            itemInfo = GameWorld.GetResultByWeightList(weightListEx)
+            if itemInfo:
+                dropItemList.append(itemInfo)
+                
+    if not dropItemList:
+        return
+    
+    mapID = attackPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneMapID)
+    if mapID:
+        DoGiveItemByVirtualDrop(attackPlayer, dropItemList, npcID)
+        GameLogic_CrossGrassland.RecordGrasslandAward(attackPlayer, dropItemList)
+    else:
+        dropPosX, dropPosY = curNPC.GetPosX(), curNPC.GetPosY()
+        ChItem.DoMapDropItem(attackPlayer, dropItemList, npcID, dropPosX, dropPosY, isOnlySelfSee=False)
+    return
+
                 
\ No newline at end of file
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
index 9882efd..30bb9e3 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -439,7 +439,6 @@
         # 离线过久恢复为非跨服状态
         if PlayerControl.GetCrossMapID(curPlayer):
             PlayerControl.SetCrossMapID(curPlayer, 0)
-        PyGameData.g_customFBPrizeInfo.pop(curPlayer.GetPlayerID(), None)
         
     SyncGuideState(curPlayer)
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
index 4f74374..bbcdc76 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
@@ -38,9 +38,9 @@
 import ShareDefine
 import GameFuncComm
 import FBHelpBattle
-import ItemControler
 import SkillShell
 import PyGameData
+import NPCCommon
 
 import time
 import math
@@ -525,73 +525,16 @@
     curPet = curPlayer.GetPetMgr().GetFightPet()
     if curPet:
         curPet.SetVisible(True)
+    mapID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneMapID)
+    lineID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomSceneLineID)
     curPlayer.SetDict(ChConfig.Def_PlayerKey_ClientCustomScene, 0)
     curPlayer.SetDict(ChConfig.Def_PlayerKey_ClientCustomSceneMapID, 0)
     curPlayer.SetDict(ChConfig.Def_PlayerKey_ClientCustomSceneLineID, 0)
+    if mapID and curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_CustomSceneStartTime % (mapID, lineID)) == ChConfig.CustomSceneState_Over:
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_CustomSceneStartTime % (mapID, lineID), ChConfig.CustomSceneState_None)
+    NPCCommon.ClearPriWoodPile(curPlayer)
     GameWorld.Log("玩家退出自定义场景!", curPlayer.GetPlayerID())
     return
-
-#// B1 08 刷新自定义副本奖励 #tagCMRefreshCustomFBPrize
-#
-#struct    tagCMRefreshCustomFBPrize
-#{
-#    tagHead         Head;
-#    DWORD        MapID;
-#    WORD        FuncLineID;
-#};
-def OnRefreshCustomFBPrize(playerIndex, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(playerIndex)
-    playerID = curPlayer.GetPlayerID()
-    mapID = clientData.MapID
-    funcLineID = clientData.FuncLineID
-    prizeItemList = FBLogic.OnRefreshCustomFBPrize(curPlayer, mapID, funcLineID)
-    PyGameData.g_customFBPrizeInfo[playerID] = prizeItemList
-    prizePack = ChPyNetSendPack.tagMCCuntomFBPrizeInfo()
-    prizePack.MapID = mapID
-    prizePack.FuncLineID = funcLineID
-    prizePack.PrizeItemIDList = [prizeItemInfo[0] for prizeItemInfo in prizeItemList]
-    prizePack.PrizeItemCount = len(prizePack.PrizeItemIDList)
-    NetPackCommon.SendFakePack(curPlayer, prizePack)
-    return
-
-
-#// B1 09 结算自定义副本奖励 #tagCMGiveCustomFBPrize
-#
-#struct    tagCMGiveCustomFBPrize
-#{
-#    tagHead         Head;
-#    DWORD        MapID;
-#    WORD        FuncLineID;
-#};
-def OnGiveCustomFBPrize(playerIndex, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(playerIndex)
-    playerID = curPlayer.GetPlayerID()
-    mapID = clientData.MapID
-    lineID = clientData.FuncLineID
-    prizeItemList = PyGameData.g_customFBPrizeInfo.pop(playerID, [])
-    
-    mailItemList = []
-    jsonItemList = []
-    playerItemControl = ItemControler.PlayerItemControler(curPlayer)
-    for itemID, itemCount, isAuctionItem in prizeItemList:
-        curItem = ItemControler.GetOutPutItemObj(itemID, itemCount, isAuctionItem, curPlayer=curPlayer)
-        if not curItem:
-            continue
-        jsonItem = ItemCommon.GetJsonItem(curItem)
-        jsonItemList.append(jsonItem)
-        #放入玩家背包
-        if not playerItemControl.PutInItem(IPY_GameWorld.rptItem, curItem):
-            mailItemList.append(jsonItem)
-            
-    if mailItemList:
-        PlayerControl.SendMailByKey("ItemNoPickUp", [playerID], mailItemList, [mapID])
-        
-    overDict = FBLogic.OnGiveCustomFBPrizeOK(curPlayer, mapID, lineID)
-    isPass = 1
-    overDict.update({FBCommon.Over_itemInfo:jsonItemList})
-    FBCommon.NotifyFBOver(curPlayer, mapID, lineID, isPass, overDict)
-    return
-
 
 #// B1 0A 副本购买buff #tagCMFBBuyBuff
 #struct    tagCMFBBuyBuff
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyDomain.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyDomain.py
index 603713b..947833a 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyDomain.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyDomain.py
@@ -59,6 +59,12 @@
 AdventuresType4,
 ) = range(1, 5)
 
+#事件副本类型
+(
+FDEventFBType_Client, #前端本0
+FDEventFBType_Server, #本服本1
+FDEventFBType_CrossServer, #跨服本2
+) = range(3)
 
 def OnLogin(curPlayer):
     NotifyVisitFairyDomainInfo(curPlayer)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
index 98c193b..78f9875 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -101,8 +101,6 @@
 g_allfamilyBossDict = {} # 多仙盟boss信息 {familyID:[familyName, 伤害, [playerID], ...}
 g_horsePetBossPlayerHurtDict = {} #骑宠boss信息 {lineID:{playerID:[playerName,hurt]}}
 
-g_customFBPrizeInfo = {} #自定义副本奖励 {playerID:[mapID, funcLineID, [奖励物品列表], ...}
-
 g_crossFuncLineDataCache = {} # 动态分配的跨服虚拟分线数据缓存 {(mapID, copyMapID):funcLineDataCache, ...}
 g_crossPlayerServerGroupIDInfo = {} #跨服玩家服务器组ID缓存,副本线路关闭时才释放,所以支持离线跨服玩家 {copyMapID:{playerID:serverGroupID, ...}, ...}
 g_fbBuyBuffTimeDict = {} # 副本购买buff时间缓存{playerID:{moneyCnt:time}}

--
Gitblit v1.8.0