From 9ec66731c8a551035958aebe1fa974a140b99cf1 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期三, 02 七月 2025 17:34:10 +0800
Subject: [PATCH] 129 【战斗】战斗系统-服务端(初版战斗,支持基础的三维属性战斗,支持简单的普攻技能、怒气技能、回血技能;主线章节关卡过关支持;阵容保存支持多阵容;)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py                                   |  164 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py                               |  628 ++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py                                    |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py |    8 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py                             | 1905 ++++++++++++++++++++-
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCHurtManager.py                            |   40 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_SummonNPC.py |    3 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PassiveBuff/PassiveSkill_4075.py           |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py |    7 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py                                 |  149 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py                             |  142 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py                               |   59 
 PySysDB/PySysDBPY.h                                                                                                  |   60 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py                          |  119 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_SummonNPC.py |   10 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_18.py               |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py                            |   19 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py                                   |   22 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_8.py                |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py               |  419 +--
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py             |    6 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py                                     |    3 
 /dev/null                                                                                                            |   47 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/EffGetSet.py                               |   18 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py                         |   56 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py                             |  150 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py                           |  118 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                                           |   12 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldEvent.py                 |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py                                 |  133 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py                                     |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameObj.py                                       |  314 +++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py                  |  148 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                                      |   71 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py                                 |  337 +++
 35 files changed, 4,313 insertions(+), 870 deletions(-)

diff --git a/PySysDB/PySysDBPY.h b/PySysDB/PySysDBPY.h
index c083348..7c2cc68 100644
--- a/PySysDB/PySysDBPY.h
+++ b/PySysDB/PySysDBPY.h
@@ -33,7 +33,7 @@
 	BYTE		Country;	// 国家
 	BYTE		Quality;	// 品质
 	list		SkinNPCIDList;	// 皮肤NPCID列表
-	DWORD		AtkSkillID;	//普攻技能ID
+	DWORD		NormalSkillID;	//普攻技能ID
 	DWORD		AngerSkillID;	//怒气技能ID
 	WORD		AtkInheritPer;	//攻击继承
 	WORD		DefInheritPer;	//防御继承
@@ -125,6 +125,43 @@
 	DWORD		_Quality;	//品质
 	DWORD		_AwakeLV;	//觉醒等级
 	list		UPCostItem;	// 觉醒到下级消耗道具
+};
+
+//主线章节表
+struct	MainChapter
+{
+	BYTE		_ChapterID;	//章节ID
+	list		DailyBootyUpperList;	// 每日战利品掉落上限,[[物品ID,每日上限], ...]
+	list		BootyWeightList;	// 战利品掉落权重,[[权重,物品ID,掉落个数下限, 上限], ...]
+};
+
+//主线关卡表
+struct	MainLevel
+{
+	BYTE		_ChapterID;	//章节ID
+	BYTE		_LevelNum;	//章节关卡编号
+	list		WaveLineupIDList1;	// 波1阵容ID列表,小队1阵容ID|小队2阵容ID|...
+	list		WaveLineupIDList2;	// 波2阵容ID列表,小队1阵容ID|小队2阵容ID|...
+	list		WaveLineupIDList3;	// 波3阵容ID列表,小队1阵容ID|小队2阵容ID|...
+	list		WaveLineupIDList4;	// 波4阵容ID列表,小队1阵容ID|小队2阵容ID|...
+	list		WaveLineupIDList5;	// 波5阵容ID列表,小队1阵容ID|小队2阵容ID|...
+	list		WaveLineupIDList6;	// 波6阵容ID列表,小队1阵容ID|小队2阵容ID|...
+	list		BossLineupIDList;	// Boss波阵容ID列表,小队1阵容ID|小队2阵容ID|...
+	list		AwardItemList;	// 过关奖励列表,[[物品ID,个数], ...]
+};
+
+//NPC阵容表
+struct	NPCLineup
+{
+	DWORD		_LineupID;	//阵容ID
+	DWORD		PosNPCID1;	//1号位NPCID
+	DWORD		PosNPCID2;	//2号位NPCID
+	DWORD		PosNPCID3;	//3号位NPCID
+	DWORD		PosNPCID4;	//4号位NPCID
+	DWORD		PosNPCID5;	//5号位NPCID
+	DWORD		PosNPCID6;	//6号位NPCID
+	DWORD		PosNPCID7;	//7号位NPCID
+	DWORD		BossID;	// 本阵容的BossID,没有boss时为0
 };
 
 //称号表 #tagDienstgrad
@@ -851,6 +888,27 @@
 	DWORD		_NPCID;	//NPCID
 	BYTE		FightPowerLackAtkLimit;	//战力不足限制攻击
 	DWORD		SuppressFightPower;	//推荐/压制战力
+	BYTE		AtkDictType;	//远近类型;1-近战;2-远程
+	DWORD		Atk;	//攻击力
+	DWORD		Def;	//防御值
+	DWORD		MaxHP;	//最大生命值,可超过20E
+	list		SkillIDList;	//技能ID列表
+	DWORD		FinalHurtPer;	//最终增伤
+	DWORD		FinalHurtReducePer;	//最终减伤
+	DWORD		MissRate;	//闪避概率
+	DWORD		MissDefRate;	//抗闪避概率
+	DWORD		SuperHitRate;	//暴击概率
+	DWORD		SuperHitRateReduce;	//抗暴击概率
+	DWORD		FaintRate;	//击晕概率
+	DWORD		FaintDefRate;	//抗击晕概率
+	DWORD		ComboRate;	//连击概率
+	DWORD		ComboDefRate;	//抗连击概率
+	DWORD		ParryRate;	//格挡概率
+	DWORD		ParryDefRate;	//抗格挡概率
+	DWORD		ParryDamPer;	//格挡减伤比率
+	DWORD		SuckHPPer;	//吸血比率
+	DWORD		SuckHPDefPer;	//抗吸血比率
+	dict		SpecAttrInfo;	//特殊属性信息 {"属性ID":值, ...}
 };
 
 //成长型境界怪物表
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index 5b58cd3..96f308c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -1818,11 +1818,19 @@
 Writer = hxp
 Releaser = hxp
 RegType = 0
-RegisterPackCount = 1
+RegisterPackCount = 3
 
 PacketCMD_1=0xB4
 PacketSubCMD_1=0x10
 PacketCallFunc_1=OnTurnFight
+
+PacketCMD_2=0xB4
+PacketSubCMD_2=0x13
+PacketCallFunc_2=OnMainFightReq
+
+PacketCMD_3=0xB4
+PacketSubCMD_3=0x14
+PacketCallFunc_3=OnTurnFightReportView
 
 ;镜像战斗
 [MirrorAttack]
@@ -1950,4 +1958,4 @@
 
 PacketCMD_10=0xB4
 PacketSubCMD_10=0x12
-PacketCallFunc_10=OnHeroBattlePosSave
+PacketCallFunc_10=OnHeroLineupSave
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 5dd3c73..389e148 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
@@ -55,6 +55,8 @@
 import ChNPC
 import BossHurtMng
 import NPCHurtMgr
+import ChNetSendPack
+import TurnAttack
 
 import datetime
 import math
@@ -429,20 +431,22 @@
 #  @return 攻击类型 如 IPY_GameWorld.ghtPhy
 #  @remarks 获取攻击类型
 def GetBattleType(attack, attackUseSkill):
-    # GetHurtType用法改成 pvp pve标识
-    return IPY_GameWorld.ghtPhy
-#    #---技能攻击, 读表获取攻击类型---
-#    if attackUseSkill != None:
-#        return attackUseSkill.GetHurtType()
-#
-#    #---普通攻击---
-#
-#    #玩家算普通攻击
-#    if attack.GetGameObjType() == IPY_GameWorld.gotPlayer:
-#        return IPY_GameWorld.ghtPhy
-#
-#    #NPC读表取
-#    return attack.GetHurtType()
+    # GetHurtType 个位数用法改成 pvp pve标识,十位数-物攻法攻 IPY_GameWorld.ghtPhy = 1
+    #---技能攻击, 读表获取攻击类型---
+    if attackUseSkill != None:
+        ght = attackUseSkill.GetHurtType() / 10
+        if ght == IPY_GameWorld.ghtMag: # 做配置兼容用,优先验证法伤,否则默认物伤
+            return IPY_GameWorld.ghtMag
+        return IPY_GameWorld.ghtPhy
+    
+    #---普通攻击---
+
+    #玩家算普通攻击
+    if attack.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        return IPY_GameWorld.ghtPhy
+    
+    #NPC读表取
+    return attack.GetHurtType()
 
 ## 输入基础数值,返回增强后的值 - 技能加强
 #  @param value 基础值
@@ -1145,18 +1149,20 @@
 #  @return True or False
 #  @remarks 函数详细说明.
 def CheckNPCAttackDist(curNPC, curTag, skill):
-    #获取距离
-    dist = GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(),
-                             curTag.GetPosX(), curTag.GetPosY())
-    #普通攻击
-    if skill == None:
-        if dist > curNPC.GetAtkDist():
-            return
-    #技能攻击
-    elif dist > skill.GetAtkDist():
-        return
-    
+    #卡牌暂不限制
     return True
+#    #获取距离
+#    dist = GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(),
+#                             curTag.GetPosX(), curTag.GetPosY())
+#    #普通攻击
+#    if skill == None:
+#        if dist > curNPC.GetAtkDist():
+#            return
+#    #技能攻击
+#    elif dist > skill.GetAtkDist():
+#        return
+#    
+#    return True
 
 ## 检查被攻击后,特殊Buff消失
 #  @param curTagPlayer 被攻击方
@@ -1285,34 +1291,35 @@
     hurtType = ChConfig.Def_HurtType_Normal
     # 伤害类型结果信息, 默认值{伤害类型:[是否触发, 伤害计算值, 触发时防守方的伤害减免值], ...}
     hurtTypeResultDict = {
-                          ChConfig.Def_HurtType_LuckyHit:[False, 0, 0],
+                          #ChConfig.Def_HurtType_LuckyHit:[False, 0, 0],
                           ChConfig.Def_HurtType_SuperHit:[False, 0, 0],
                           ChConfig.Def_HurtType_Parry:[False, 0, 0],
-                          ChConfig.Def_HurtType_Zhuxian:[False, 0, 0],
-                          ChConfig.Def_HurtType_DeadlyHit:[False, 0, 0],
+                          #ChConfig.Def_HurtType_Zhuxian:[False, 0, 0],
+                          #ChConfig.Def_HurtType_DeadlyHit:[False, 0, 0],
                           ChConfig.Def_HurtType_ThumpHit:[False, 0, 0],
                           }
     
-    calcTypeList =  []
-    if atkObjType == IPY_GameWorld.gotPlayer:
-        calcTypeList += [ChConfig.Def_HurtType_LuckyHit, ChConfig.Def_HurtType_SuperHit, 
-                         ChConfig.Def_HurtType_Zhuxian, ChConfig.Def_HurtType_DeadlyHit,
-                         ChConfig.Def_HurtType_ThumpHit]
-    if defObjType == IPY_GameWorld.gotPlayer:
-        calcTypeList += [ChConfig.Def_HurtType_Parry]
-    # 暂时只计算玩家
+    #calcTypeList =  []
+    #if atkObjType == IPY_GameWorld.gotPlayer:
+    #    calcTypeList += [ChConfig.Def_HurtType_LuckyHit, ChConfig.Def_HurtType_SuperHit, 
+    #                     ChConfig.Def_HurtType_Zhuxian, ChConfig.Def_HurtType_DeadlyHit,
+    #                     ChConfig.Def_HurtType_ThumpHit]
+    #if defObjType == IPY_GameWorld.gotPlayer:
+    #    calcTypeList += [ChConfig.Def_HurtType_Parry]
+    calcTypeList = [ChConfig.Def_HurtType_SuperHit, ChConfig.Def_HurtType_Parry]
     if not calcTypeList:
         return hurtType, hurtTypeResultDict
     
-    # 优先级列表, 互斥列表
-    priorityList, mutexList = ReadChConfig.GetEvalChConfig("CalcHurtTypeInfo")
+    # 优先级列表
+    priorityList = [ChConfig.Def_HurtType_Parry, ChConfig.Def_HurtType_SuperHit]
+    mutexList = [] # 互斥列表
     happenFunc = {
-                  ChConfig.Def_HurtType_LuckyHit:__HurtTypeHappen_LuckyHit,
+                  #ChConfig.Def_HurtType_LuckyHit:__HurtTypeHappen_LuckyHit,
                   ChConfig.Def_HurtType_SuperHit:__HurtTypeHappen_SuperHit,
                   ChConfig.Def_HurtType_Parry:__HurtTypeHappen_Parry,
                   #ChConfig.Def_HurtType_Zhuxian:__HurtTypeHappen_Zhuxian,
-                  ChConfig.Def_HurtType_DeadlyHit:__HurtTypeHappen_Deadly,
-                  ChConfig.Def_HurtType_ThumpHit:__HurtTypeHappen_ThumpHit,
+                  #ChConfig.Def_HurtType_DeadlyHit:__HurtTypeHappen_Deadly,
+                  #ChConfig.Def_HurtType_ThumpHit:__HurtTypeHappen_ThumpHit,
                   }
     
     hadCheckList = [] # 已经处理过的伤害类型列表
@@ -1375,10 +1382,10 @@
     '''
     
     if IsHappenStateByType(happenState, ChConfig.Def_Skill_HappenState_SuperHit):
-        return True, atkObj.GetSuperHit(), PlayerControl.GetSuperHitReduce(defObj)
+        return True, atkObj.GetSuperHit(), GameObj.GetSuperHitReduce(defObj)
     
     aSuperHitRate = atkObj.GetSuperHitRate()
-    dSuperHitRateReduce = PlayerControl.GetSuperHitRateReduce(defObj)
+    dSuperHitRateReduce = GameObj.GetSuperHitRateReduce(defObj)
     superHitRate = eval(ReadChConfig.GetChConfig("CalcSuperHitRate"))
     superHitRate += PassiveBuffEffMng.GetValueByPassiveBuffTriggerType(atkObj, defObj, curSkill, 
                                                                        ChConfig.TriggerType_Buff_AddSuperHitRate)
@@ -1387,7 +1394,7 @@
     if superHitRate <= 0:
         return
     if GameWorld.CanHappen(superHitRate):
-        return True, atkObj.GetSuperHit(), PlayerControl.GetSuperHitReduce(defObj)
+        return True, atkObj.GetSuperHit(), GameObj.GetSuperHitReduce(defObj)
     return
 
 def __HurtTypeHappen_Parry(atkObj, defObj, happenState, curSkill):
@@ -1422,7 +1429,7 @@
 def __HurtTypeHappen_ThumpHit(atkObj, defObj, happenState, curSkill):
     
     if IsHappenStateByType(happenState, ChConfig.Def_Skill_HappenState_ThumpHit):
-        return True, int(atkObj.GetSuperHit()*1.5), PlayerControl.GetSuperHitReduce(defObj)
+        return True, int(atkObj.GetSuperHit()*1.5), GameObj.GetSuperHitReduce(defObj)
     
     thumpHitRate = 0  
     thumpHitRate += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, 
@@ -1432,7 +1439,7 @@
     if thumpHitRate <= 0:
         return
     if GameWorld.CanHappen(thumpHitRate):
-        return True, atkObj.GetSuperHit()*2, PlayerControl.GetSuperHitReduce(defObj)
+        return True, atkObj.GetSuperHit()*2, GameObj.GetSuperHitReduce(defObj)
     return
 
 
@@ -1965,9 +1972,6 @@
 # 计算攻击伤害
 # maxHurt参数用于模拟计算最大伤害,防范客户端攻击伤害过高
 def CalcHurtHP(atkObj, defObj, curSkill, atkSkillValue, atkSkillPer, tick, happenState=None, **atkwargs):
-    # 翻滚闪避特殊处理
-    if tick - defObj.GetDictByKey(ChConfig.Def_PlayerKey_SomersaultTime) < 500:
-        return 0, ChConfig.Def_HurtType_Miss
     
     multiValue = 1 # 伤害倍值
     summonAtkPer = 1    # 召唤继承提高基础攻击力,取表
@@ -1985,106 +1989,61 @@
         
     atkObjType = atkObj.GetGameObjType()
     defObjType = defObj.GetGameObjType()
-
-    aRealmLV, dRealmLV = GetPVERealmLVs(atkObj, defObj, atkObjType, defObjType) # 获取境界
-    if defObjType == IPY_GameWorld.gotNPC and ChConfig.IsGameBoss(defObj) and dRealmLV > aRealmLV:
-        aRealmIpyData = IpyGameDataPY.GetIpyGameDataNotLog("Realm", aRealmLV)
-        dRealmIpyData = IpyGameDataPY.GetIpyGameDataNotLog("Realm", dRealmLV)
-        aRealmLVLarge = aRealmIpyData.GetLvLarge() if aRealmIpyData else 0
-        dRealmLVLarge = dRealmIpyData.GetLvLarge() if dRealmIpyData else 0
-        if dRealmLVLarge > aRealmLVLarge:
-            if atkObjType == IPY_GameWorld.gotPlayer:
-                GameWorld.DebugLog("BossRealmHint%s-%s"%(dRealmLV, aRealmLV))
-                PlayerControl.NotifyCode(atkObj, 'BossRealmHint', [dRealmLVLarge])
-                
-            # 攻击高境界的BOSS 伤害固定为1
-            return 1, ChConfig.Def_HurtType_Normal
-        
+    
     atkType = GetBattleType(atkObj, curSkill)
     happenState = happenState if happenState else SkillShell.GetHappenState(curSkill)
     happenState += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_HappenState)
-
-    aLV = atkObj.GetLV()                # 攻击方等级
-    dLV = defObj.GetLV()                # 防守方等级
-
-    aHit = atkObj.GetHit()
-    if curSkill and atkObjType == IPY_GameWorld.gotPlayer and curSkill.GetFuncType() != ChConfig.Def_SkillFuncType_NormalAttack:
-        aHit = aHit*IpyGameDataPY.GetFuncCfg("FightHappenRate", 2)
-        
-    aHitSuccessRate = PlayerControl.GetHitSucessRate(atkObj) if atkObjType == IPY_GameWorld.gotPlayer else ChConfig.Def_MaxRateValue
-    aHitSuccessRate += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_HitSuccess)
-    dMiss = defObj.GetMiss()
+    
+    atkID = atkObj.GetID()
+    defID = defObj.GetID()
+    #aLV = atkObj.GetLV()                # 攻击方等级
+    #dLV = defObj.GetLV()                # 防守方等级
+    
+    aHit = GameObj.GetMissDefRate(atkObj)#atkObj.GetHit() # 抗闪避率 - 命中
+    #aHitSuccessRate = PlayerControl.GetHitSucessRate(atkObj) if atkObjType == IPY_GameWorld.gotPlayer else ChConfig.Def_MaxRateValue
+    #aHitSuccessRate += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_HitSuccess)
+    dMiss = GameObj.GetMissRate(defObj)#defObj.GetMiss()
     dMiss += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(defObj, atkObj, None, ChConfig.TriggerType_MissPer)
-    dMissSuccessRate = PlayerControl.GetMissSucessRate(defObj) if defObjType == IPY_GameWorld.gotPlayer else 0
-    dMissSuccessRate += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(defObj, atkObj, None, ChConfig.TriggerType_MissSuccessPer)
+    #dMissSuccessRate = PlayerControl.GetMissSucessRate(defObj) if defObjType == IPY_GameWorld.gotPlayer else 0
+    #dMissSuccessRate += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(defObj, atkObj, None, ChConfig.TriggerType_MissSuccessPer)
     skillID = curSkill.GetSkillID() if curSkill else 0
     
-    
     atkIsBoss = 0 # 攻击方是否boss
-    suppressValueLV = 0 # 等级最终压制值, 由压制规则及相关参数计算得出,可作为伤害公式计算参数使用
-    suppressValueFP = 0 # 战力最终压制值, 由压制规则及相关参数计算得出,可作为伤害公式计算参数使用
-    suppressLV, suppressFightPower = 0, 0 # 压制等级差、战力差
-    suppressReMaxHP = 0 # NPC压制等级生命值, 等级表中NPC等级对应的数据, 压制等级差大于0时才有值
-    suppressNPCFightPower = 0 # 压制NPC战力
-    fbFightPower, fbBaseHurt = 0, 0 # 副本战力, 副本保底伤害
-    #当攻击方为NPC,防守方为玩家时,计算压制等级 及 压制战力
-    if atkObjType == IPY_GameWorld.gotNPC and defObjType == IPY_GameWorld.gotPlayer:
-        
-        if curSkill and curSkill.GetFuncType() == ChConfig.Def_SkillFuncType_RealmSuppress:
-            # 境界压制技能不对高等级境界玩家产生攻击
-            aRealmLV, dRealmLV = GetPVERealmLVs(atkObj, defObj, atkObjType, defObjType)
-            if aRealmLV <= dRealmLV:
-                return 0, ChConfig.Def_HurtType_Immune   # 免疫
-            
-        atkIsBoss = 1 if ChConfig.IsGameBoss(atkObj) else 0
-        if NPCCommon.GetIsLVSuppress(atkObj):
-            suppressLV = max(0, aLV - dLV)
-            if suppressLV:
-                suppressLVIpyData = PlayerControl.GetPlayerLVIpyData(aLV)
-                suppressReMaxHP = 0 if not suppressLVIpyData else suppressLVIpyData.GetReMaxHP()
-        suppressNPCFightPower = NPCCommon.GetSuppressFightPower(atkObj)
-        if suppressNPCFightPower:
-            suppressFightPower = max(0, suppressNPCFightPower - PlayerControl.GetFightPower(defObj))
-            
-    mustHit = False
-    helpBattleFormatKey = ""
-    if atkObjType == IPY_GameWorld.gotNPC and atkObj.GetType() == ChConfig.ntHelpBattleRobot:
-        mustHit = True
-        suppressNPCFightPower = NPCCommon.GetSuppressFightPower(atkObj)
-        fbFightPower = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.FBPD_HelpBattleFBFightPower)
-        fbBaseHurt = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.FBPD_HelpBattleFBBaseHurt)
-        helpBattleFormatKey = "HelpRobot_Atk"
-    if defObjType == IPY_GameWorld.gotNPC and defObj.GetType() == ChConfig.ntHelpBattleRobot:
-        mustHit = True
-        suppressNPCFightPower = NPCCommon.GetSuppressFightPower(defObj)
-        fbFightPower = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.FBPD_HelpBattleFBFightPower)
-        fbBaseHurt = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.FBPD_HelpBattleFBBaseHurt)
-        helpBattleFormatKey = "HelpRobot_Def"
-        
-    #命中公式 攻击方类型不同,公式不同
-    hitFormula = ReadChConfig.GetChConfig('CalcCanHit')
+    angerOverflow = 0 # 怒气溢出值
     
+    turnFightPosInfo = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo)
+    mustHit = False
+    if SkillCommon.isXPSkill(curSkill):
+        mustHit = True
+        GameWorld.DebugLog("        XP必命中")
+        angerOverflow = max(GameObj.GetXP(atkObj) - IpyGameDataPY.GetFuncCfg("AngerXP", 2), 0)
+        
+    if not mustHit:
+        if IsHappenStateByType(happenState, ChConfig.Def_Skill_HappenState_HitOn):
+            mustHit = True
+            GameWorld.DebugLog("        技能必命中: skillID=%s" % skillID)
+            
+    pow = math.pow
+    #命中公式 攻击方类型不同,公式不同
     if not mustHit and not PassiveBuffEffMng.GetValueByPassiveBuffTriggerType(atkObj, defObj, curSkill, 
                            ChConfig.TriggerType_Buff_MustBeHit):
         # 技能对指定BOSS无效果的返回MISS
-        if defObjType == IPY_GameWorld.gotNPC and defObj.GetIsBoss() not in ChConfig.Def_SkillAttack_NPCIsBoss \
-        and SkillCommon.GetSkillBattleType(curSkill) == ChConfig.Def_BattleRelationType_CommNoBoss and SkillShell.IsNPCSkillResist(defObj):
-            return 0, ChConfig.Def_HurtType_Miss
+        #if defObjType == IPY_GameWorld.gotNPC and defObj.GetIsBoss() not in ChConfig.Def_SkillAttack_NPCIsBoss \
+        #and SkillCommon.GetSkillBattleType(curSkill) == ChConfig.Def_BattleRelationType_CommNoBoss and SkillShell.IsNPCSkillResist(defObj):
+        #    return 0, ChConfig.Def_HurtType_Miss
         
         #攻击方处于嘲讽,防守方处于免疫嘲讽者攻击则miss
-        if GameObj.GetPyPlayerState(atkObj, ChConfig.Def_PlayerState_Sneer) and \
-        GameObj.GetPyPlayerState(defObj, ChConfig.Def_PlayerState_MissSneerAtk):
+        #if GameObj.GetPyPlayerState(atkObj, ChConfig.Def_PlayerState_Sneer) and \
+        #GameObj.GetPyPlayerState(defObj, ChConfig.Def_PlayerState_MissSneerAtk):
+        #    return 0, ChConfig.Def_HurtType_Miss
+        
+        missNum = defObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnMissNum)
+        missRate = eval(IpyGameDataPY.GetFuncCompileCfg("MissCfg", 1))
+        if GameWorld.CanHappen(missRate):
             return 0, ChConfig.Def_HurtType_Miss
         
-        #添加是否必命中
-        if not IsHappenStateByType(happenState, ChConfig.Def_Skill_HappenState_HitOn) \
-            and eval(hitFormula) < 0:
-            return 0, ChConfig.Def_HurtType_Miss
-    
-
     hurtType, hurtTypeResultDict = CalcHurtTypeResult(atkObj, defObj, atkObjType, defObjType, happenState, curSkill)
     #GameWorld.DebugLog("GetHurtHP hurtType=%s, hurtTypeResultDict=%s" % (hurtType, hurtTypeResultDict))
-    isLuckyHit, aLuckyHit, dLuckyHitReduce = hurtTypeResultDict[ChConfig.Def_HurtType_LuckyHit] # 幸运一击
     
     # 重击和暴击互斥,并且使用同一个参数
     isSuperHit, aSuperHit, dSuperHitReduce = hurtTypeResultDict[ChConfig.Def_HurtType_ThumpHit] 
@@ -2104,17 +2063,11 @@
         aSuperHit = aSuperHit*(thumpPer + ChConfig.Def_MaxRateValue)/ChConfig.Def_MaxRateValue
         
     dDamChanceDef = hurtTypeResultDict[ChConfig.Def_HurtType_Parry][2] # 抵御, 大于0代表触发抵御效果
-    isZhuxianHit, aZhuxianHurtPer, dZhuxianReducePer = hurtTypeResultDict[ChConfig.Def_HurtType_Zhuxian] # 诛仙一击
-    isDeadlyHit, deadlyHitMultiValue, _ = hurtTypeResultDict[ChConfig.Def_HurtType_DeadlyHit] # 致命一击
-
+    #isZhuxianHit, aZhuxianHurtPer, dZhuxianReducePer = hurtTypeResultDict[ChConfig.Def_HurtType_Zhuxian] # 诛仙一击
+    #isDeadlyHit, deadlyHitMultiValue, _ = hurtTypeResultDict[ChConfig.Def_HurtType_DeadlyHit] # 致命一击
+    
     if PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(defObj, atkObj, None, ChConfig.TriggerType_OneDamage):
         return 1, hurtType
-    
-    wReFightPower = 0
-    worldLV = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
-    if worldLV:
-        wLVIpyData = PlayerControl.GetPlayerLVIpyData(worldLV)
-        wReFightPower = 0 if not wLVIpyData else wLVIpyData.GetReFightPower() # 当前世界等级参考战力
     
     # 改变技能伤害
     atkSkillPer, atkSkillValue = ChangeSkillHurt(atkObj, defObj, curSkill, atkSkillPer, atkSkillValue)
@@ -2149,31 +2102,28 @@
         PassiveBuffEffMng.OnPassiveBuffTrigger(atkObj, defObj, curSkill, ChConfig.TriggerType_SuperHitSubLayer, tick)
     elif hurtType == ChConfig.Def_HurtType_ThumpHit:
         atkSkillValue += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_ThumpSkillValue)
-
-    if isLuckyHit:
-        # 会心一击时增加会心伤害固定值 
-        aLuckyHit += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_LuckyHit)
-        aLuckyHit -= PassiveBuffEffMng.GetValueByPassiveBuffTriggerType(defObj, atkObj, curSkill, ChConfig.TriggerType_BeLuckyHitSubPer)
-        aLuckyHit = max(aLuckyHit, 0)
-        
+    
     #参与运算的数值
     rand = random.random()                #种子数 0~1
     
     #------- 攻击方
-    aMinAtk = atkObj.GetMinAtk() * summonAtkPer        # 攻击方最小攻击
-    aMaxAtk = atkObj.GetMaxAtk() * summonAtkPer       # 攻击方最大攻击
+    #aMinAtk = atkObj.GetMinAtk() * summonAtkPer        # 攻击方最小攻击
+    aAtk = atkObj.GetMaxAtk() * summonAtkPer       # 攻击方最大攻击
     
     aIceAtk = atkObj.GetIceAtk()        # 冰攻, 元素真伤, 玩家及NPC通用
     aIceAtk += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_AddIceAtk)
     #------- 防守方
-    dMinAtk = defObj.GetMinAtk()        # 防守方最小攻击
-    dMaxAtk = defObj.GetMaxAtk()        # 防守方最大攻击
+    #dMinAtk = defObj.GetMinAtk()        # 防守方最小攻击
+    #dAtk = defObj.GetMaxAtk()        # 防守方最大攻击
     dDef = defObj.GetDef()              # 防守方防御力
-    dHP = GameObj.GetHP(defObj)                # 防守方当前血量
-    dMaxHP = GameObj.GetMaxHP(defObj)          # 防守方最大血量
+    #dHP = GameObj.GetHP(defObj)                # 防守方当前血量
+    #dMaxHP = GameObj.GetMaxHP(defObj)          # 防守方最大血量
     dIceDef = defObj.GetIceDef()        # 冰防, 元素真防, 玩家及NPC通用
     
     # 攻击方
+    aNormalAtkPer = 0
+    aFinalHurtPer = GameObj.GetFinalHurtPer(atkObj) # 最外层伤害加成, 可能为负值
+    aFinalHurtPer += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_AttackAddFinalPer)
     if atkObjType == IPY_GameWorld.gotPlayer:
         aIgnoreDefRate = atkObj.GetIgnoreDefRate()  # 无视防御比率
         aSkillAtkRate = atkObj.GetSkillAtkRate()    # 技能攻击力加成
@@ -2183,8 +2133,6 @@
         aNPCHurtAddPer = PlayerControl.GetNPCHurtAddPer(atkObj)     # PVE伤害加成
         aDamagePerPVP = PlayerControl.GetDamagePerPVP(atkObj)     # 外层PVP伤害加成
         aDamagePerPVP += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_AddPVPDamagePer)
-        aFinalHurtPer = PlayerControl.GetFinalHurtPer(atkObj) # 最外层伤害加成, 可能为负值
-        aFinalHurtPer += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(atkObj, defObj, curSkill, ChConfig.TriggerType_AttackAddFinalPer)
         
         aFinalHurt = PlayerControl.GetFinalHurt(atkObj)     # 最终固定伤害
         # 被动增加最终伤害
@@ -2193,12 +2141,8 @@
         
         aOnlyFinalHurt = PlayerControl.GetOnlyFinalHurt(atkObj) # 额外固定伤害
         aFightPower = PlayerControl.GetFightPower(atkObj)
-        
-        
-        
     else:
         aIgnoreDefRate = 0  # 无视防御比率
-        aFinalHurtPer = GameObj.GetPetDamPer(atkObj) # 最外层伤害加成, 可能为负值
         aSkillAtkRate = NPCCommon.GetSkillAtkRate(atkObj)   # 技能攻击力加成
         if atkObjType == IPY_GameWorld.gotNPC and atkObj.GetGameNPCObjType() == IPY_GameWorld.gnotPet:
             aSkillAtkRate += atkObj.GetSkillAtkRate()
@@ -2208,9 +2152,13 @@
         aDamagePVP = 0      # PVP固定伤害
         aDamagePVE = 0      # PVE固定伤害
         aFinalHurt = NPCCommon.GetFinalHurt(atkObj) # 最终固定伤害
+        aOnlyFinalHurt = 0 # 额外固定伤害
         aFightPower = NPCCommon.GetSuppressFightPower(atkObj)
         
     #防守方的类型
+    dNormalAtkDefPer = 0
+    dFinalHurtReducePer = GameObj.GetFinalHurtReducePer(defObj)
+    dFinalHurtReducePer += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(defObj, atkObj, curSkill, ChConfig.TriggerType_dFinalHurtReducePer)
     if defObjType == IPY_GameWorld.gotPlayer:
         dIgnoreDefRateReduce = PlayerControl.GetIgnoreDefRateReduce(defObj)  # 无视防御比率抗性
         dSkillAtkRateReduce = PlayerControl.GetSkillAtkRateReduce(defObj) # 技能攻击力减少
@@ -2221,8 +2169,6 @@
         dFinalHurtReduce = PlayerControl.GetFinalHurtReduce(defObj) # 最终固定伤害减少
         dBeHurtPer = PlayerControl.GetBeHurtPer(defObj)      # 加深受到伤害百分比
         dFightPower = PlayerControl.GetFightPower(defObj)
-        dFinalHurtReducePer = PlayerControl.GetFinalHurtReducePer(defObj)
-        dFinalHurtReducePer += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(defObj, atkObj, curSkill, ChConfig.TriggerType_dFinalHurtReducePer)
         
     else:
         dIgnoreDefRateReduce = 0    # 无视防御比率抗性
@@ -2232,108 +2178,55 @@
         dFinalHurtReduce = 0        # 最终固定伤害减少
         dBeHurtPer = 0
         dFightPower = NPCCommon.GetSuppressFightPower(defObj)
-        dFinalHurtReducePer = 0             # 最终伤害减少百分比 默认0
         
-    #攻击字典 { 攻击类型 : '公式' }
-    mapID = FBCommon.GetRecordMapID(GameWorld.GetMap().GetMapID())
-    hurtDist = ReadChConfig.GetEvalChConfig('CalcAttackValue')
-
-    if suppressLV:
-        suppressFormulaKeyLV = "SuppressValueLV_%s" % (atkIsBoss)
-        if suppressFormulaKeyLV in hurtDist:
-            suppressLVFormula = hurtDist[suppressFormulaKeyLV]
-            suppressValueLV = eval(FormulaControl.GetCompileFormula(suppressFormulaKeyLV, suppressLVFormula))
-            
-    if suppressFightPower:
-        suppressFormulaKeyFP = "SuppressValueFP_%s" % (atkIsBoss)
-        if suppressFormulaKeyFP in hurtDist:
-            suppressFPFormula = hurtDist[suppressFormulaKeyFP]
-            suppressValueFP = eval(FormulaControl.GetCompileFormula(suppressFormulaKeyFP, suppressFPFormula))
+    aPMHurtPer = 0 # 物法增伤
+    dPMHurtReduce = 0 # 物法减伤
+    if atkType == IPY_GameWorld.ghtMag: # 法伤
+        pass
+    else: # 物伤
+        pass
     
-    # 境界压制规则
-    # 1. 其中一方无境界等级则无效, 如普通NPC
-    # 2. 宠物和召唤兽(如水元素)有效, 取主人
-    # 3. 玩家地境界低于BOSS则伤害固定为1 (在函数入口处已处理)
-    # 4. 其他情况统一境界压制 境界差*2%
-    if aRealmLV == 0 or dRealmLV == 0:
-        SuppressValueRealmRate = 10000
-    else:
-        suppressRealmRateMapKey = "SuppressValueRealm_%s" % mapID
-        if suppressRealmRateMapKey not in hurtDist:
-            suppressRealmRateMapKey = "SuppressValueRealm"
-        SuppressValueRealmRate = int(eval(FormulaControl.GetCompileFormula(suppressRealmRateMapKey, hurtDist[suppressRealmRateMapKey])))
-        
-    # 骑宠争夺最终伤害衰减
-    if defObjType == IPY_GameWorld.gotNPC and FamilyRobBoss.IsHorsePetRobBoss(defObj.GetNPCID()):
-        ownerPlayer, npcObjType = GetAttackPlayer(atkObj)
-            
-        if ownerPlayer:
-            findBuff = SkillCommon.FindBuffByID(ownerPlayer, ChConfig.Def_SkillID_HorsePetRobBossKillCntBuff)[0]
-            if findBuff:
-                reduceFinalHurtPer = findBuff.GetSkill().GetEffect(0).GetEffectValue(0)
-                aFinalHurtPer -= reduceFinalHurtPer
+    # 所有万分率参数统一除10000.0
+    atkSkillPer /= 10000.0
+    aNormalAtkPer /= 10000.0
+    dNormalAtkDefPer /= 10000.0
+    aFinalHurtPer /= 10000.0
+    dFinalHurtReducePer /= 10000.0
     
-    # 仙盟boss最终伤害加成
-    if atkObjType == IPY_GameWorld.gotPlayer and mapID == ChConfig.Def_FBMapID_FamilyBossMap:
-        aFinalHurtPer += PlayerControl.GetFamilyBossHurtPer(atkObj)
-        
-    if atkObjType == IPY_GameWorld.gotPlayer and defObjType == IPY_GameWorld.gotNPC and ChConfig.IsGameBoss(defObj):
-        killBossCntLimitDict = IpyGameDataPY.GetFuncEvalCfg('KillBossCntLimit') 
-        limitIndex = GameWorld.GetDictValueByKey(killBossCntLimitDict, defObj.GetNPCID())
-        if limitIndex != None:
-            aFinalHurtPer += PlayerControl.GetBossFinalHurtPer(atkObj)
-            
-    atkStateMark = GetObjAtkStateMark(atkObj)
-    defStateMark = GetObjAtkStateMark(defObj)
-    hurtFormulaKey = "%sV%s_%s" % (atkStateMark, defStateMark, atkType)
+    #PVP PVE 之后再扩展
+    #atkStateMark = GetObjAtkStateMark(atkObj)
+    #defStateMark = GetObjAtkStateMark(defObj)
     
-    suppressLVGroup = 0 # NPC压制等级组编号
-    mapHurtKey = "%s_%s" % (hurtFormulaKey, mapID)
-    if mapHurtKey in hurtDist:
-        hurtFormulaKey = mapHurtKey
-    elif atkStateMark == "E" and defStateMark == "P":
-        suppressLVGroup = NPCCommon.GetIsLVSuppress(atkObj)
-    elif atkStateMark == "P" and defStateMark == "E":
-        suppressLVGroup = NPCCommon.GetIsLVSuppress(defObj)
-        
-    if suppressLVGroup:
-        suppressLVHurtKey = "%s_%s" % (hurtFormulaKey, suppressLVGroup)
-        if suppressLVHurtKey in hurtDist:
-            hurtFormulaKey = suppressLVHurtKey
+    GameWorld.DebugLog("伤血计算: atkID=%s,defID=%s,skillID=%s,atkSkillPer=%s,aAtk=%s,dDef=%s,dHP=%s" 
+                       % (atkID, defID, skillID, atkSkillPer, aAtk, dDef, GameObj.GetHP(defObj)))
     
-    # 助战机器人特殊伤血key
-    if helpBattleFormatKey:
-        hurtFormulaKey = helpBattleFormatKey
-        
-    if hurtFormulaKey not in hurtDist:
-        GameWorld.ErrLog("CalcAttackValue.txt 伤害公式未配置, key=%s" % (hurtFormulaKey))
-        return 0, ChConfig.Def_HurtType_Miss
-    
-    if atkwargs.get('hurtFormulaKey', None):
-        # 指定公式
+    if "hurtFormulaKey" in atkwargs:
         aBurnValue = atkwargs.get('burnValue', 0)
         aBurnPer = atkwargs.get('burnPer', 0)
         hurtFormulaKey = atkwargs.get('hurtFormulaKey', None)
-
-    hurtFormula = hurtDist[hurtFormulaKey]
-    
-    hurtValue = int(eval(FormulaControl.GetCompileFormula(hurtFormulaKey, hurtFormula)))
-    if isDeadlyHit:
-        hurtValue *= deadlyHitMultiValue
-        
-    if atkObjType == IPY_GameWorld.gotPlayer and defObjType == IPY_GameWorld.gotNPC and mapID == ChConfig.Def_FBMapID_CrossBattlefield:
-        multiValue = FBLogic.GetFBPlayerHurtNPCMultiValue(atkObj, defObj)
+        #if hurtFormulaKey == "Burn":
+        #    pass
+        #else:
+        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("DOTFormula", 1))
+    elif not curSkill:
+        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 3))
+        GameWorld.DebugLog("    普攻伤害=%s" % (hurtValue))
+    elif SkillCommon.isNormalAtkSkill(curSkill):
+        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 1))
+        GameWorld.DebugLog("    普攻技能伤害=%s" % (hurtValue))
+    elif SkillCommon.isXPSkill(curSkill):
+        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 2))
+        GameWorld.DebugLog("    怒气技能伤害=%s" % (hurtValue))
+    else:
+        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 4))
+        GameWorld.DebugLog("    其他伤害=%s" % (hurtValue))
         
     if multiValue != 1:
         hurtValue = int(hurtValue * multiValue)
-        
-    #hurtValue = min(max(hurtValue, 0), ChConfig.Def_UpperLimit_DWord)
+    hurtValue = int(hurtValue)
     
     if hurtType == ChConfig.Def_HurtType_Normal and atkSkillPerYinji > 0:
         return hurtValue, ChConfig.Def_HurtType_Yinji
-    elif hurtType == ChConfig.Def_HurtType_Normal and SuppressValueRealmRate > 10000:
-        # 存在压制
-        return hurtValue, ChConfig.Def_HurtType_RealmSupress
     
     return hurtValue, hurtType
 
@@ -2400,6 +2293,12 @@
             atkObj.SetDict(ChConfig.Def_PlayerKey_LastHurtNPCObjID, defObj.GetID())
         else:
             defObj.SetDict(ChConfig.Def_PlayerKey_LastAttackerObjID, atkObj.GetID())
+            
+    TurnAttack.AddTurnObjHurtValue(atkObj, defObj, resultHurtType.HurtType, resultHurtType.RealHurtHP, resultHurtType.LostHP, curSkill)
+    
+    #if resultHurtType.RealHurtHP:
+    #    PassiveBuffEffMng.OnPassiveSkillTrigger(defObj, atkObj, None, ChConfig.TriggerType_BeHurt, tick)
+        
     return
 
 
@@ -2410,6 +2309,8 @@
         return "P"
     
     if objType == IPY_GameWorld.gotNPC:
+        if obj.GetDictByKey(ChConfig.Def_Obj_Dict_LineupPlayerID):
+            return "P"
         if obj.GetType() == ChConfig.ntRobot:
             return "Robot"
         if obj.GetType() == ChConfig.ntHelpBattleRobot:
@@ -2894,6 +2795,9 @@
     if GameObj.GetHP(curObjDetel) > 0:
         return
     
+    if TurnAttack.SetKilled(curObjDetel):
+        return
+        
     #---玩家处理---
     if curObjDetel.GetGameObjType() == IPY_GameWorld.gotPlayer:
         playerControl = PlayerControl.PlayerControl(curObjDetel)
@@ -2970,6 +2874,21 @@
     srcID, srcType = 0, 0
     if srcObj:
         srcID, srcType = srcObj.GetID(), srcObj.GetGameObjType()
+    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(curObj.GetID())
+    if turnFight:
+        clientPack = ChNetSendPack.tagObjPropertyRefreshView()
+        clientPack.ObjID = curObj.GetID()
+        clientPack.ObjType = curObj.GetGameObjType()
+        clientPack.SkillID = skillID
+        clientPack.DiffValue = changeHP % ShareDefine.Def_PerPointValue
+        clientPack.DiffValueEx = changeHP / ShareDefine.Def_PerPointValue
+        clientPack.AttackType = changType
+        clientPack.SrcObjID = srcID
+        clientPack.SrcObjType = srcType
+        clientPack.HP = curObj.GetHP()
+        clientPack.HPEx = curObj.GetHPEx()
+        turnFight.addBatPack(clientPack)
+        return
     curObj.ChangeHPView(skillID, changeHP % ShareDefine.Def_PerPointValue, changeHP / ShareDefine.Def_PerPointValue, 
                         changType, srcID, srcType, curObj.GetHP(), curObj.GetHPEx())
     return
\ No newline at end of file
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py
index 02e8d34..2694d8d 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py
@@ -24,6 +24,7 @@
 import ChNPC
 import GameObj
 import GameWorld
+import TurnAttack
 #---------------------------------------------------------------------
 Def_FB_NPCAI_SideList = []
 #---------------------------------------------------------------------
@@ -52,8 +53,8 @@
     if atkLimit:
         return ChConfig.Type_Relation_Friend, ChConfig.Def_PASysMessage_None
     
-    attackerCampType = NPCCommon.GetFaction(attacker)
-    defenderCampType = NPCCommon.GetFaction(defender)
+    attackerCampType = GameObj.GetFaction(attacker)
+    defenderCampType = GameObj.GetFaction(defender)
     
     #不同阵营是敌人
     if attackerCampType != defenderCampType:
@@ -104,6 +105,8 @@
     
     #普通NPC
     if GameObj.GetHP(defender) <= 0:
+        if TurnAttack.SetKilled(defender):
+            return
         if not ChNPC.OnCheckCanDie(attacker, defender, skill, tick):
             return
         #副本
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_SummonNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_SummonNPC.py
index c8c1aee..a2f9d9c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_SummonNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_SummonNPC.py
@@ -24,6 +24,7 @@
 import IPY_GameWorld
 import GameWorld
 import SkillShell
+import TurnAttack
 import GameObj
 #---------------------------------------------------------------------
 
@@ -55,8 +56,11 @@
         return ChConfig.Type_Relation_None , ChConfig.Def_PASysMessage_None
     
     #判断阵营
-    attackerCampType = NPCCommon.GetFaction(curNormalNPC)
-    defenderCampType = NPCCommon.GetFaction(curTagSummon)
+    attackerCampType = GameObj.GetFaction(curNormalNPC)
+    defenderCampType = GameObj.GetFaction(curTagSummon)
+    
+    if attackerCampType != defenderCampType:
+        return ChConfig.Type_Relation_Enemy, ChConfig.Def_PASysMessage_None
     
     #2.不攻击阵营相同(不包括二者都为中立)
     if not (attackerCampType == ChConfig.CampType_Neutral and defenderCampType == ChConfig.CampType_Neutral): 
@@ -132,6 +136,8 @@
     
     #召唤兽死亡
     if GameObj.GetHP(curTagSummonNPC) <= 0:
+        if TurnAttack.SetKilled(curTagSummonNPC):
+            return
         curTagSummonNPCControl = NPCCommon.NPCControl(curTagSummonNPC)
         #召唤兽死亡
         curTagSummonNPCControl.SetKilled()
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py
index 403c638..8479669 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py
@@ -33,6 +33,7 @@
 import SkillShell
 import ChNPC
 import GameObj
+import TurnAttack
 #---------------------------------------------------------------------
 
 #---------------------------------------------------------------------
@@ -58,7 +59,7 @@
 def GetTagRelation(curSummonNPC, curTagNormalNPC, skill, tick):
     
     #防守方阵营
-    defenderCampType = NPCCommon.GetFaction(curTagNormalNPC)
+    defenderCampType = GameObj.GetFaction(curTagNormalNPC)
     
     #玩家的召唤兽才能攻击普通NPC
     npcOwner_Player = NPCCommon.GetSummonNPCOwner(IPY_GameWorld.gotPlayer, curSummonNPC)
@@ -72,7 +73,7 @@
         return ChConfig.Type_Relation_Enemy, ChConfig.Def_PASysMessage_None
     
     #攻击方阵营
-    attackerCampType = curSummonNPC.GetDictByKey(ChConfig.Def_NpcDictKey_CampType)
+    attackerCampType = GameObj.GetFaction(curSummonNPC)
     if attackerCampType != defenderCampType:
         return ChConfig.Type_Relation_Enemy, ChConfig.Def_PASysMessage_None
     return ChConfig.Type_Relation_Friend, ChConfig.Def_PASysMessage_None
@@ -140,6 +141,9 @@
         return
 
     #---NPC被击杀---
+    if TurnAttack.SetKilled(curTagNPC):
+        return
+    
     #玩家击杀NPC副本触发器
     curPlayer = NPCCommon.GetSummonNPCOwner(IPY_GameWorld.gotPlayer, curSummonNPC)
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_SummonNPC.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_SummonNPC.py
index f54d8b0..b657117 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_SummonNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_SummonNPC.py
@@ -30,6 +30,7 @@
 import GameWorld
 import PlayerControl
 import SkillShell
+import TurnAttack
 import GameObj
 #---------------------------------------------------------------------
 
@@ -155,6 +156,8 @@
     
     #防守方召唤兽死亡
     if GameObj.GetHP(curTagSummon) <= 0:
+        if TurnAttack.SetKilled(curTagSummon):
+            return
         curTagSummonNPCControl = NPCCommon.NPCControl(curTagSummon)
         #召唤兽死亡
         curTagSummonNPCControl.SetKilled()
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 00ff334..dbd61de 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py
@@ -40,6 +40,7 @@
 import NetPackCommon
 import PassiveBuffEffMng
 import IpyGameDataPY
+import TurnAttack
 #---------------------------------------------------------------------
 g_skillHurtList = IPY_GameWorld.IPY_HurtList()
 
@@ -1365,6 +1366,9 @@
 #  @return None
 #  @remarks 函数详细说明.
 def __Sync_AttackResult(attacker, defender, curSkill):
+    battleType = AttackCommon.GetBattleType(attacker, curSkill)
+    turnBattleType = attacker.GetDictByKey(ChConfig.Def_Obj_Dict_TurnBattleType)
+    battleType = turnBattleType * 10 + battleType # 通知的battle修改: 回合攻击战斗类型*10+原战斗类型
     #普通攻击
     if not curSkill:
         #GameWorld.Log("玩家普通攻击成功")
@@ -1374,10 +1378,8 @@
                                                            g_skillHurtList.GetHurtCount()))
         else:
             curHurt = g_skillHurtList.GetHurtAt(0)
-            attacker.BaseAttack(curHurt.GetObjID(), curHurt.GetObjType(),
-                                AttackCommon.GetBattleType(attacker, curSkill),
-                                curHurt.GetAttackType(), curHurt.GetHurtHP(), curHurt.GetHurtHPEx(), curHurt.GetCurHP(), curHurt.GetCurHPEx())
-        
+            PYView_BaseAttack(attacker, curHurt, battleType)
+            
         #//返回值无意义
         return
     
@@ -1386,7 +1388,7 @@
     changeSkillID = PassiveBuffEffMng.GetPassiveSkillValueByTriggerTypeEx(attacker, None, curSkill, ChConfig.TriggerType_ChangeSkillEff)
     if changeSkillID:
         skillID = changeSkillID
-    battleType = AttackCommon.GetBattleType(attacker, curSkill)
+        
     #无目标类技能
     if not defender:
         #玩家处理
@@ -1435,7 +1437,7 @@
         # 为了客户端表现特殊处理,冲锋通知地板坐标而不是敌方坐标
         if curSkill.GetAtkType() == 9:
             useSkillPosX, useSkillPosY = attacker.GetUseSkillPosX(), attacker.GetUseSkillPosY()
-        PYView_UseSkillPos(attacker, skillID, battleType, useSkillPosX, useSkillPosY, g_skillHurtList, False)
+        PYView_UseSkillPos(attacker, skillID, battleType, useSkillPosX, useSkillPosY, g_skillHurtList, False, defender)
         PYView_UseSkillPos_NotifySelf(attacker, skillID, battleType, useSkillPosX, useSkillPosY, g_skillHurtList)
     else:
         if attacker.GetDictByKey(ChConfig.Def_NPC_Dict_AtkMovePosX):
@@ -1443,7 +1445,7 @@
             useSkillPosY = attacker.GetDictByKey(ChConfig.Def_NPC_Dict_AtkMovePosY)
 
         # NPC没有C++View_UseSkillPos接口
-        PYView_UseSkillPos(attacker, skillID, battleType, useSkillPosX, useSkillPosY, g_skillHurtList, False)
+        PYView_UseSkillPos(attacker, skillID, battleType, useSkillPosX, useSkillPosY, g_skillHurtList, False, defender)
     
     return
 
@@ -1695,28 +1697,32 @@
 
 # 属性击晕
 def AttackFaintRate(attacker, defender, curSkill, tick):
-    if attacker.GetGameObjType() != IPY_GameWorld.gotPlayer:
-        return
+    #if attacker.GetGameObjType() != IPY_GameWorld.gotPlayer:
+    #    return
     
-    faintRate = PlayerControl.GetFaintRate(attacker)
+    faintRate = GameObj.GetFaintRate(attacker)
     if not faintRate:
         #GameWorld.DebugLog("没有击晕概率!", attacker.GetID())
         return
     
-    if curSkill:
-        useSkillData = attacker.GetUseSkill()
-        # 非主动性技能不触发
-        if not useSkillData:
-            return
-        if useSkillData.GetSkillID() != curSkill.GetSkillID():
-            return
-        
-    if not defender:
-        useSkillTagID = attacker.GetUseSkillTagID()
-        useSkillTagType = attacker.GetUseSkillTagType()
-        defender = GameWorld.GetObj(useSkillTagID, useSkillTagType)
+    if attacker.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        if curSkill:
+            useSkillData = attacker.GetUseSkill()
+            # 非主动性技能不触发
+            if not useSkillData:
+                return
+            if useSkillData.GetSkillID() != curSkill.GetSkillID():
+                return
+            
         if not defender:
-            return
+            useSkillTagID = attacker.GetUseSkillTagID()
+            useSkillTagType = attacker.GetUseSkillTagType()
+            defender = GameWorld.GetObj(useSkillTagID, useSkillTagType)
+            if not defender:
+                return
+            
+    if not defender:
+        return
     
     if attacker.GetID() == defender.GetID():
         return
@@ -1724,11 +1730,11 @@
     if GameObj.GetHP(defender) <= 0:
         return
 
-    tagFaintRate = PlayerControl.GetFaintDefRate(defender) if defender.GetGameObjType() == IPY_GameWorld.gotPlayer else 0
-
+    tagFaintDefRate = GameObj.GetFaintDefRate(defender)
+    
     # 添加最高60%击晕效果
     maxRate = IpyGameDataPY.GetFuncCfg("PassiveSkillFaint", 1)
-    rate = min(max(faintRate - tagFaintRate, 0), maxRate)
+    rate = min(max(faintRate - tagFaintDefRate, 0), maxRate)
     if not GameWorld.CanHappen(rate):
         return
     
@@ -1737,12 +1743,11 @@
         lastTick = attacker.GetDictByKey(ChConfig.Def_PlayerKey_AttrFaintCD)
         remainTick = faintCD - (tick - lastTick)
         if remainTick > 0:
-            GameWorld.DebugLog("击晕CD中! rate=%s,剩余tick=%s" % (rate, remainTick), attacker.GetID())
+            GameWorld.DebugLog("        击晕CD中! rate=%s,剩余tick=%s,atkID=%s" % (rate, remainTick, attacker.GetID()))
             return
         attacker.SetDict(ChConfig.Def_PlayerKey_AttrFaintCD, tick)
-        GameWorld.DebugLog("触发击晕! rate=%s" % rate, attacker.GetID())
-        
-    SkillCommon.AddBuffBySkillType(defender, ChConfig.Def_SkillID_AtkerFaint, tick)
+    GameWorld.DebugLog("        可触发击晕! rate=%s,atkID=%s,defID=%s" % (rate, attacker.GetID(), defender.GetID()))
+    SkillCommon.AddBuffBySkillType(defender, ChConfig.Def_SkillID_AtkerFaint, tick, buffOwner=attacker)
     return
 
 
@@ -1796,7 +1801,7 @@
 #  @param tick 当前时间
 #  @return None
 #  @remarks 设置玩家属性消耗,如魔法,XP点,HP
-def SetSkillLostAttr(curPlayer, curSkill, tick):
+def SetSkillLostAttr(curObj, curSkill, tick):
     #===========================================================================
     # #-----------扣魔法
     # lostMPValue = curSkill.GetMP()
@@ -1811,31 +1816,22 @@
     #    #自动回魔
     #    PlayerControl.PlayerAutoRestoreMP(curPlayer, tick)
     # 
-    # #----------扣XP点
-    # lostXPValue = curSkill.GetXP()
-    # curPlayerXP = curPlayer.GetXP()
-    # 
-    # if curPlayerXP < lostXPValue:
-    #    GameWorld.ErrLog('释放技能 = %s异常, XP点 = %s不足 = %s' % (
-    #                        curSkill.GetSkillTypeID(), curPlayerXP, lostXPValue))
-    # 
-    # if lostXPValue > 0:
-    #    remain = curPlayer.GetXP() - lostXPValue
-    #    remain = max(0, remain)
-    #    curPlayer.SetDict(ChConfig.Def_PlayerKey_RecordXPValue, remain)
-    #    curPlayer.SetXP(remain)
     #===========================================================================
+    
+    #----------扣XP点
+    if SkillCommon.isXPSkill(curSkill):
+        GameObj.SetXP(curObj, 0)
 
     #----------扣HP点
     lostHPValue = curSkill.GetHP()
-    curPlayerHP = GameObj.GetHP(curPlayer)
+    curPlayerHP = GameObj.GetHP(curObj)
     
     if curPlayerHP < lostHPValue:
         GameWorld.ErrLog('释放技能 = %s异常, HP点 = %s不足 = %s' % (
                             curSkill.GetSkillTypeID(), curPlayerHP, lostHPValue))
     
     if lostHPValue > 0:
-        GameObj.SetHP(curPlayer, GameObj.GetHP(curPlayer) - lostHPValue)
+        GameObj.SetHP(curObj, GameObj.GetHP(curObj) - lostHPValue)
         
     return
 
@@ -1885,6 +1881,9 @@
     #技能使用成功
     if curSkill:
         skillTypeID = curSkill.GetSkillTypeID()
+        
+        #扣属性,如魔法,XP点
+        SetSkillLostAttr(curNPC, curSkill, tick)
         
         #技能使用成功
         curNPCSkill = curNPC.GetSkillManager().FindSkillBySkillTypeID(skillTypeID)
@@ -2760,9 +2759,32 @@
     PlayerControl.PyNotifyAll(curPlayer, sendPack, notifySelf=True, notifyCnt=-1)
     return
 
+def PYView_BaseAttack(attacker, curHurt, battleType):
+    #attacker.BaseAttack(curHurt.GetObjID(), curHurt.GetObjType(),
+    #                    battleType,
+    #                    curHurt.GetAttackType(), curHurt.GetHurtHP(), curHurt.GetHurtHPEx(), curHurt.GetCurHP(), curHurt.GetCurHPEx())
+    #ChangeAction(laNPCAttack);
+    sendPack = ChNetSendPack.tagObjBaseAttack()
+    sendPack.Clear()
+    sendPack.AttackerID = attacker.GetID()
+    sendPack.AttackerObjType = attacker.GetGameObjType()
+    sendPack.ObjID = curHurt.GetObjID()
+    sendPack.ObjType = curHurt.GetObjType()
+    sendPack.BattleType = battleType
+    sendPack.AttackType = curHurt.GetAttackType()
+    sendPack.Value = curHurt.GetHurtHP()
+    sendPack.ValueEx = curHurt.GetHurtHPEx()
+    sendPack.RemainHP = curHurt.GetCurHP()
+    sendPack.RemainHPEx = curHurt.GetCurHPEx()
+    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(attacker.GetID())
+    if turnFight:
+        turnFight.addBatPack(sendPack)
+        return
+    attacker.NotifyAll(sendPack.GetBuffer(), sendPack.GetLength())
+    return
 
 # py重现View_UseSkillPos效果,对地通知,只用于玩家
-def PYView_UseSkillPos(attacker, skillID, battleType, useSkillPosX, useSkillPosY, skillHurtList, notifySelf):
+def PYView_UseSkillPos(attacker, skillID, battleType, useSkillPosX, useSkillPosY, skillHurtList, notifySelf, defender=None):
     #===========================================================================
     # C++ 此处代码PY已处理,暂不加入
     # SetAttackTick(GetGameWorldManager()->GetTick());
@@ -2770,6 +2792,32 @@
     # m_LastBattleTick = GetGameWorldManager()->GetTick();
     #===========================================================================
     
+    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(attacker.GetID())
+    if turnFight:
+        sendPack = ChNetSendPack.tagUseSkillAttack()
+        sendPack.ObjID = attacker.GetID()
+        sendPack.ObjType = attacker.GetGameObjType()
+        sendPack.BattleType = battleType
+        sendPack.SkillID = skillID
+        sendPack.AttackID = defender.GetID() if defender else 0 # 主攻击目标
+        sendPack.AttackObjType = defender.GetGameObjType() if defender else 0
+        
+        for i in range(skillHurtList.GetHurtCount()):
+            hurtObj = skillHurtList.GetHurtAt(i)
+            hurtList = ChNetSendPack.tagSkillHurtObj()
+            hurtList.ObjType = hurtObj.GetObjType()
+            hurtList.ObjID = hurtObj.GetObjID()
+            hurtList.AttackType = hurtObj.GetAttackType()
+            hurtList.HurtHP = hurtObj.GetHurtHP()
+            hurtList.HurtHPEx = hurtObj.GetHurtHPEx()
+            hurtList.CurHP = hurtObj.GetCurHP()
+            hurtList.CurHPEx = hurtObj.GetCurHPEx()
+            
+            sendPack.HurtList.append(hurtList)
+        sendPack.HurtCount = len(sendPack.HurtList)
+        turnFight.addBatPack(sendPack)
+        return
+    
     sendPack = ChNetSendPack.tagUseSkillPos()
     sendPack.Clear()
     sendPack.ObjID = attacker.GetID()
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
index 3e17b07..f0c06e5 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -10,6 +10,10 @@
 # @version 1.0
 #
 # 详细描述: 回合制攻击逻辑,均使用NPC实例作为战斗主体
+#        实时战斗:  仅用于主线,每个玩家最多仅允许存在一个实时战斗
+#                    由前端控制战斗流程,后端进行计算
+#        瞬时战斗:  用于非主线外的各种战斗,如副本PVE挑战,PVP挑战等,一样仅有一个瞬时战斗,
+#                    一次性处理完,后端控制整体战斗流程及计算,前端仅做战报解析表现战斗过程
 #
 #-------------------------------------------------------------------------------
 #"""Version = 2023-11-30 15:30"""
@@ -22,7 +26,23 @@
 import GameWorld
 import GameObj
 import FBCommon
-import FBLogic
+import IpyGameDataPY
+import PlayerOnline
+import NPCCommon
+import ShareDefine
+import PyGameData
+import IPY_GameWorld
+import ItemControler
+import SkillCommon
+import SkillShell
+import BaseAttack
+
+import random
+import time
+import json
+
+PosNumMax = 10 # 最大站位编号
+ActionNumStart = -1 # 起始行动位置编号,一般是从1开始,如果有加主公、红颜等则扣除相应位置值,如从0或-1开始
 
 # 回合战斗流程状态
 (
@@ -34,169 +54,1786 @@
 FightState_Over, # 5 结束状态,无特殊意义,仅代表所有处理结束了,与Start对应
 ) = range(6)
 
+Def_FactionA = 1
+Def_FactionB = 2
+
+class BatLineup():
+    ## 战斗阵容
+    
+    def __init__(self, faction, num, fightMgr):
+        self.fightMgr = fightMgr
+        self.faction = faction # 所属阵营
+        self.num = num # 该阵容所在阵营中的编号,不同阵营可重复,多V多
+        self.ownerID = 0 # 阵容所属玩家ID,可能为0,0代表非玩家阵容
+        self.npcPosDict = {} # 阵容NPC {站位编号:curNPC, ...}, 站位编号小于0为非主战单位,如主公、红颜等
+        self.npcObjIDDict = {} # NPC实例ID字典 {objID:curNPC, ...}
+        self.shapeType = 0 # 阵型
+        self.actionNum = ActionNumStart # 行动位置,从1开始
+        self.lordAttrDict = {} # 主公属性
+        self.npcAttrDict = {} # 阵容NPC属性 {npcID:{attrID:value, ...}, ...}
+        self.fightPower = 0 # 阵容总战力
+        
+        # 战斗统计
+        self.hurtStatDict = {} # 输出统计 {objID:totalValue, ...}
+        self.defStatDict = {} # 承伤统计 {objID:totalValue, ...}
+        self.cureStatDict = {} # 治疗统计 {objID:totalValue, ...}
+        return
+    
+    def getGUID(self): return self.fightMgr.guid
+    def getPlayerID(self): return self.fightMgr.playerID # 发起的玩家ID
+    
+    def isEmpty(self): return not self.npcPosDict
+    
+    def setLineup(self, lineupInfo):
+        ## 设置阵容
+        # @param lineupInfo: 阵容信息
+        self.clearLineup()
+        self.ownerID = lineupInfo.get("PlayerID", 0) # 阵容所属的玩家ID
+        self.shapeType = lineupInfo.get("Shape", 0)
+        self.lordAttrDict = lineupInfo.get("LordAttrDict", {})
+        SummonLineupObjs(self, self.faction, self.num, lineupInfo, self.getPlayerID())
+        return
+    
+    def refreshFightPower(self):
+        ## 刷新阵容总战力
+        if self.fightPower:
+            return self.fightPower
+        
+        return self.fightPower
+    
+    def __resetStat(self):
+        ## 重置战斗统计
+        self.actionNum = ActionNumStart
+        self.hurtStatDict = {}
+        self.defStatDict = {}
+        self.cureStatDict = {}
+        return
+    
+    def __resetAttrState(self, isReborn=True):
+        ## 重置属性状态
+        # @param isReborn: 是否复活
+        initXP = IpyGameDataPY.GetFuncCfg("AngerXP", 1)
+        for curNPC in self.npcPosDict.values():
+            if not curNPC:
+                continue
+            if GameObj.GetHP(curNPC) <= 0 and not isReborn:
+                continue
+            # 回满血,清除buff
+            GameObj.SetHPFull(curNPC, False)
+            # 重置怒气
+            GameObj.SetXP(curNPC, initXP, False)
+            # ...
+            
+        return
+    
+    def resetLineup(self, isReborn=True):
+        ## 重置阵容
+        self.__resetAttrState(isReborn)
+        self.__resetStat()
+        return
+    
+    def clearLineup(self):
+        ## 清除阵容
+        if not self.npcPosDict:
+            return
+        for curNPC in self.npcPosDict.values():
+            if not curNPC:
+                continue
+            NPCCommon.SetDeadEx(curNPC)
+        self.npcPosDict = {}
+        self.npcObjIDDict = {}
+        self.npcAttrDict = {}
+        self.fightPower = 0
+        self.__resetStat()
+        return
+    
+    def statHurtValue(self, objID, hurtValue):
+        ## 统计输出
+        if objID not in self.npcObjIDDict:
+            return
+        updValue = self.hurtStatDict.get(objID, 0) + hurtValue
+        self.hurtStatDict[objID] = updValue
+        return updValue
+    
+    def statDefValue(self, objID, lostHP):
+        ## 统计承伤
+        if objID not in self.npcObjIDDict:
+            return
+        updValue = self.defStatDict.get(objID, 0) + lostHP
+        self.defStatDict[objID] = updValue
+        return updValue
+    
+    def statCureValue(self, objID, cureValue):
+        ## 统计治疗
+        if objID not in self.npcObjIDDict:
+            return
+        updValue = self.cureStatDict.get(objID, 0) + cureValue
+        self.cureStatDict[objID] = updValue
+        return updValue
+    
+class BatFaction():
+    ## 战斗阵营
+    
+    def __init__(self, faction, fightMgr):
+        self.fightMgr = fightMgr
+        self.faction = faction
+        self.lineupDict = {} # 该阵营所有阵容信息 {编号:BatLineup, ...}
+        return
+    
+    def getBatlineup(self, num=1):
+        ## 获取战斗阵容
+        lineup = None
+        if num in self.lineupDict:
+            lineup = self.lineupDict[num]
+        else:
+            lineup = BatLineup(self.faction, num, self.fightMgr)
+            self.lineupDict[num] = lineup
+        return lineup
+    
+    def resetLineups(self):
+        ## 重置所有战斗阵容
+        for lineup in self.lineupDict.values():
+            lineup.resetLineup()
+        return
+    
+    def clearLineups(self):
+        ## 清除所有战斗阵容
+        for lineup in self.lineupDict.values():
+            lineup.clearLineup()
+        return
+    
+class TurnFight():
+    '''某场回合战斗,支持多V多,所有战斗通用,主线、爬塔、PVP等
+    可能为系统后台发起的,则攻击方、防守方阵容以玩家所保存的阵容镜像进行战斗
+    如果玩家发起的,则攻击方为发起方玩家,目标为防守方,支持多V多,如组队PK
+    多V多的情况也是某个队员各自发起战斗,只是用到队友的镜像阵容一起战斗,队友无感知
+    
+    多V多可使用两种模式:
+    1. 多个阵容一起出场,在一场战斗中完成
+    2. 以单阵容方式轮流出场战斗,该方式同样可以使用方式1的逻辑实现,固优先使用方式1战斗逻辑,方便扩展
+    '''
+    
+    def __init__(self, mapID=0, funcLineID=0, playerID=0, isNeedReport=False):
+        self.guid = GameWorld.GetGUID() # 某场战斗的唯一guid,可用于存储记录如战报等
+        self.playerID = playerID # 可能为0,系统后台自动处理的战斗时为0,某个玩家发起则为发起玩家ID,同个玩家ID可能同时存在多场战斗,如主线+其他
+        self.curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID) if playerID else None
+        self.mapID = mapID
+        self.funcLineID = funcLineID
+        self.turnNum = 1 # 当前第x回合,默认第1回合开始
+        self.turnMax = 15 # 最大回合数
+        self.enterLogic = False # 是否已执行进场逻辑
+        self.turnStart = 0 # 已执行回合开始值,如第1回合开始已执行则为1,第2回合为2
+        self.turnEnd = 0 # 已执行回合结束值,如第1回合结束已执行则为1,第2回合为2
+        self.winFaction = 0 # 本场战斗结束标记,获胜阵营,为0时代表未结束,所有小队打完或失败才有结果,0-未结束,>0-获胜的阵营
+        self.batBuffer = "" # 战报buffer,战报暂时只保留最后一个小队的
+        self.isNeedReport = isNeedReport # 是否需要战报
+        self.msgDict = {} # 扩展信息字典,一般由MapID绑定的功能决定信息内容  {k:v, ...}
+        
+        self.factionDict = {} # 战斗阵营 {faction:BatFaction, ...},一般是只有两个阵营,faction为1或2,每个阵营支持多个阵容
+        self.actionSortList = [] # 阵容行动顺序 [[faction, num], ...]
+        self.actionIndex = 0 # 行动顺序索引
+        
+        self.startTime = 0 # 开始时间戳,支持毫秒小数
+        self.costTime = 0 # 单场战斗总耗时,支持毫秒小数
+        return
+    
+    def setTurn(self, mapID, funcLineID, turnMax, isNeedReport=False, msgDict={}):
+        ## 设置本场回合战斗设定
+        self.mapID = mapID
+        self.funcLineID = funcLineID
+        self.turnMax = turnMax # 最大回合数
+        self.isNeedReport = isNeedReport
+        self.msgDict = {}
+        self.resetTurn(msgDict)
+        return
+    
+    def resetTurn(self, msgDict):
+        ## 一般用于玩家发起的战斗,在需要保留玩家阵容属性及状态的情况下,重置回合进入下一场战斗
+        self.turnNum = 1
+        self.enterLogic = False
+        self.turnStart = 0
+        self.turnEnd = 0
+        self.winFaction = 0
+        self.batBuffer = "" # 战报buffer
+        self.msgDict.update(msgDict)
+        self.startTime = time.time()
+        self.costTime = 0
+        return
+    
+    def setFactionLineup(self, faction, lineupDict, onlyReset=False):
+        ## 设置阵营阵容
+        # @param lineupDict: {阵容编号:阵容信息, ...} 每个阵营支持多个阵容,即支持多V多
+        # @param onlyReset: 是否仅重置,一般用于玩家的阵容,重复利用阵容实例,
+        batFaction = self.getBatFaction(faction)
+        if onlyReset:
+            batFaction.resetLineups()
+        else:
+            batFaction.clearLineups()
+        for num, lineupInfo in lineupDict.items():
+            batLineup = batFaction.getBatlineup(num)
+            if onlyReset and not batLineup.isEmpty():
+                continue
+            if not lineupInfo:
+                continue
+            batLineup.setLineup(lineupInfo)
+            batLineup.refreshFightPower()
+        return
+    
+    def sortActionQueue(self):
+        ## 刷新出手顺序队列
+        sortList = []
+        for batFaction in self.factionDict.values():
+            faction = batFaction.faction
+            for num, batLineup in batFaction.lineupDict.items():
+                fightPower = batLineup.refreshFightPower()
+                sortValue = -(faction * 10 + num)
+                sortList.append([fightPower, sortValue, faction, num])
+        sortList.sort(reverse=True) # 战力高的先手
+        
+        self.actionIndex = 0
+        self.actionSortList = []
+        for _, _, faction, num in sortList:
+            self.actionSortList.append([faction, num])
+            
+        GameWorld.DebugLog("阵容战力排序[fp, sortV, f, n]: %s" % sortList)
+        GameWorld.DebugLog("阵容行动顺序[f, n]: %s" % self.actionSortList)
+        return
+    
+    def getBatFaction(self, faction=Def_FactionA):
+        ## 默认阵营1
+        batFaction = None
+        if faction in self.factionDict:
+            batFaction = self.factionDict[faction]
+        else:
+            batFaction = BatFaction(faction, self)
+            self.factionDict[faction] = batFaction
+        return batFaction
+    
+    def checkOverByKilled(self):
+        ##检查是否击杀结束
+        # @return: 0-无获胜阵营,同时也代表战斗未结束;>0-获胜的阵营ID,同时也代表战斗已全部结束
+        
+        if self.winFaction:
+            return self.winFaction
+        
+        for faction, batFaction in self.factionDict.items():
+            allKilled = True
+            for batLineup in batFaction.lineupDict.values():
+                if not allKilled:
+                    break
+                for posNum, curNPC in batLineup.npcPosDict.items():
+                    if posNum <= 0:
+                        # 非主战位置不判断
+                        continue
+                    if GameObj.GetHP(curNPC) > 0:
+                        allKilled = False
+                        break
+                    
+            if allKilled:
+                self.winFaction = Def_FactionA if faction == Def_FactionB else Def_FactionA
+                DoTurnFightOver(self.guid)
+                return self.winFaction
+            
+        return 0
+    
+    def clearBatFaction(self, faction):
+        batFaction = self.getBatFaction(faction)
+        batFaction.clearLineups()
+        return
+    
+    def clearFight(self):
+        self.syncState(FightState_Over)
+        for batFaction in self.factionDict.values():
+            batFaction.clearLineups()
+        return
+    
+    def syncInit(self):
+        ## 初始化通知
+        msg = json.dumps(self.msgDict, ensure_ascii=False)
+        msg = msg.replace(" ", "")
+        clientPack = ChPyNetSendPack.tagSCTurnFightInit()
+        clientPack.MapID = self.mapID
+        clientPack.FuncLineID = self.funcLineID
+        clientPack.TurnMax = self.turnMax
+        clientPack.Msg = msg
+        clientPack.Len = len(clientPack.Msg)
+        clientPack.FactionList = []
+        for faction in self.factionDict.keys():
+            batFaction = self.getBatFaction(faction)
+            tfFaction = ChPyNetSendPack.tagSCTurnFightFaction()
+            tfFaction.Faction = faction
+            tfFaction.LineupList = []
+            for num in batFaction.lineupDict.keys():
+                batLineup = batFaction.getBatlineup(num)
+                tfLineup = ChPyNetSendPack.tagSCTurnFightLineup()
+                tfLineup.Num = num
+                tfLineup.OwnerID = batLineup.ownerID
+                tfLineup.ShapeType = batLineup.shapeType
+                tfLineup.ObjList = []
+                for posNum, curNPC in batLineup.npcPosDict.items():
+                    tfObj = ChPyNetSendPack.tagSCTurnFightObj()
+                    tfObj.ObjID = curNPC.GetID()
+                    tfObj.NPCID = curNPC.GetNPCID()
+                    tfObj.HP = curNPC.GetHP()
+                    tfObj.HPEx = curNPC.GetHPEx()
+                    tfObj.MaxHP = curNPC.GetMaxHP()
+                    tfObj.MaxHPEx = curNPC.GetMaxHPEx()
+                    tfObj.LV = NPCCommon.GetNPCLV(curNPC)
+                    tfObj.PosNum = posNum
+                    tfObj.AngreXP = GameObj.GetXP(curNPC)
+                    tfLineup.ObjList.append(tfObj)
+                tfLineup.ObjCnt = len(tfLineup.ObjList)
+                tfFaction.LineupList.append(tfLineup)
+            tfFaction.LineupCnt = len(tfFaction.LineupList)
+            clientPack.FactionList.append(tfFaction)
+        clientPack.FactionCnt = len(clientPack.FactionList)
+        self.addBatPack(clientPack)
+        return
+    
+    def syncState(self, state, msgDict={}):
+        msg = json.dumps(msgDict, ensure_ascii=False)
+        msg = msg.replace(" ", "")
+        clientPack = ChPyNetSendPack.tagMCTurnFightState()
+        clientPack.Clear()
+        clientPack.MapID = self.mapID
+        clientPack.FuncLineID = self.funcLineID
+        clientPack.State = state
+        clientPack.TurnNum = self.turnNum
+        clientPack.Msg = msg
+        clientPack.Len = len(clientPack.Msg)
+        self.addBatPack(clientPack)
+        return
+    
+    def syncObjAction(self, turnNum, objType, objID):
+        clientPack = ChPyNetSendPack.tagMCTurnFightObjAction()
+        clientPack.Clear()
+        clientPack.TurnNum = turnNum
+        clientPack.ObjID = objID
+        self.addBatPack(clientPack)
+        return
+    
+    def addBatPack(self, clientPack):
+        ## 添加战斗过程封包,非战斗相关的封包可以不使用该函数发送,如加经验、掉落等
+        ## 注:战斗相关的封包需调用本函数方便统一管理战报
+        if hasattr(clientPack, "Head"):
+            headStr = "%02x%02x" % (clientPack.Head.Cmd, clientPack.Head.SubCmd)
+        else:
+            headStr = "%02x%02x" % (clientPack.Cmd, clientPack.SubCmd)
+        GameWorld.DebugLog("回合战斗过程封包: %s, isNeedReport=%s,%s" % (headStr, self.isNeedReport, self.guid))
+        if self.isNeedReport:
+            self.batBuffer += clientPack.GetBuffer()
+        # 有玩家的统一每个包单独发送,同样也支持战报统计
+        if self.curPlayer:
+            NetPackCommon.SendFakePack(self.curPlayer, clientPack)
+        return
+    
+class TurnFightMgr():
+    ## 回合战斗管理器
+    
+    def __init__(self):
+        self.turnFightDict = {} # {guid:TurnFight, ...}
+        self.npcGUIDDict = {} # npc所属的某场战斗guid {objID:guid, ...}
+        return
+    
+    def addTurnFight(self, mapID, funcLineID=0, playerID=0):
+        tf = TurnFight(mapID, funcLineID, playerID, False)
+        self.turnFightDict[tf.guid] = tf
+        return tf
+    
+    def delTurnFight(self, guid):
+        turnFight = self.getTurnFight(guid)
+        if not turnFight:
+            return
+        turnFight.clearFight()
+        self.turnFightDict.pop(guid, None)
+        return
+    
+    def getTurnFight(self, guid):
+        tf = None
+        if guid in self.turnFightDict:
+            tf = self.turnFightDict[guid]
+        elif False:
+            tf = TurnFight()
+        return tf
+    
+    def getNPCTurnFight(self, objID):
+        ## 获取NPC所在的回合战斗,如果该npc不属于某场回合战斗则返回None
+        tf = None
+        if objID in self.npcGUIDDict:
+            guid = self.npcGUIDDict[objID]
+            if guid in self.turnFightDict:
+                tf = self.turnFightDict[guid]
+        if not tf and False:
+            tf = TurnFight()
+        return tf
+    
+    def setNPCGUID(self, objID, guid):
+        ## 设置NPC所属回合战斗guid
+        self.npcGUIDDict[objID] = guid
+        GameWorld.DebugLog("设置NPC所属回合战斗guid: %s,%s" % (objID, guid))
+        return
+    def delNPCGUID(self, objID):
+        guid = self.npcGUIDDict.pop(objID, None)
+        GameWorld.DebugLog("删除NPC所属回合战斗guid: %s,%s" % (objID, guid))
+        return
+    
+def GetTurnFightMgr():
+    tfMgr = None
+    if PyGameData.g_turnFightMgr:
+        tfMgr = PyGameData.g_turnFightMgr
+    else:
+        tfMgr = TurnFightMgr()
+        PyGameData.g_turnFightMgr = tfMgr
+    return tfMgr
+    
+class MainFight():
+    ## 主线战斗管理
+    
+    def __init__(self, playerID):
+        self.playerID = playerID
+        self.chapterID = 0 # 章节ID
+        self.levelNum = 0 # 关卡编号
+        self.waveMax = 6 # 本关最大波数,每波有多个小队,每个小队即为一张战斗 TurnFight
+        self.wave = 0 # 当前刷怪波,注意不是玩家当前进度波,比如被击杀会回退一波
+        self.teamNum = 1 # 当前小队
+        self.teamMax = 1 # 当前波最大小队,某场战斗可能包含多个小队,所有小队混流击杀完才算过了本波
+        self.nextTeam = False # 下次前端请求战斗是否是下一小队,否则都是重新刷新当前进度怪
+        self.waveLineupList = [] # 小队列表
+        self.turnFight = GetTurnFightMgr().addTurnFight(ChConfig.Def_FBMapID_Main, 0, playerID)
+        return
+    
+    def isLevelBoss(self):
+        ## 当前战斗是否关卡boss
+        return self.turnFight.mapID == ChConfig.Def_FBMapID_MainBoss
+    
+    def clear(self):
+        self.turnFight.clearFight()
+        return
+    
+    def playerLogin(self, curPlayer):
+        self.turnFight.curPlayer = curPlayer
+        return
+    
+    def playerOffline(self, curPlayer):
+        self.turnFight.curPlayer = None
+        return
+    
+def GetMainFightMgr(curPlayer):
+    ## 获取主线战斗管理
+    olPlayer = PlayerOnline.GetOnlineMgr().GetOnlinePlayer(curPlayer)
+    return olPlayer.mainFight
+
+def OnPlayerLogin(curPlayer):
+    chapterID, levelNum, _ = PlayerControl.GetMainLevelPassInfo(curPlayer)
+    nowChapterID, _, _ = PlayerControl.GetMainLevelNowInfo(curPlayer)
+    if chapterID != nowChapterID:
+        if IpyGameDataPY.GetIpyGameDataNotLog("MainChapter", chapterID) and IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID, levelNum):
+            fixNowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, 1)
+            GameWorld.Log("当前主线关卡章节与过关章节不一致时,且过关进度章节已经存在,强制修正当前刷怪进度为过关章节进度! passChapterID-LV=%s-%s,nowChapterID=%s,fixNowValue=%s" 
+                          % (chapterID, levelNum, nowChapterID, fixNowValue), curPlayer.GetPlayerID())
+    return
+
+def GetPlayerLineupByCache(playerID, lineupID):
+    ## 获取玩家阵容信息 - 根据玩家查看缓存
+    return {}
+
+def GetPlayerLineup(curPlayer, lineupID):
+    ## 获取玩家阵容信息
+    # @param lineupID: 阵容ID
+    # @return: 阵容全部信息json字典,前端通用格式
+    
+    playerID = curPlayer.GetPlayerID()
+    
+    # 武将
+    heroDict = {}
+    heroCount = 0
+    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
+    for index in range(curPack.GetCount()):
+        heroItem = curPack.GetAt(index)
+        if not heroItem or heroItem.IsEmpty():
+            continue
+        lineupCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)
+        if not lineupCount:
+            continue
+        posNum = 0 # 站位
+        for lpIndex in range(lineupCount):
+            lineupValue = heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
+            #阵容类型*10000+阵型类型*100+位置编号
+            if lineupValue / 10000 != lineupID:
+                continue
+            posNum = lineupValue % 100
+            break
+        
+        if not posNum:
+            continue
+        
+        heroID = heroItem.GetItemTypeID()
+        heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
+        if not heroIpyData:
+            continue
+        
+        npcID = heroIpyData.GetSkinNPCIDList()[0]
+        heroDict[str(posNum)] = {
+                                 "ID":heroID,
+                                 "Data":heroItem.GetUserData(),
+                                 "NPCID":npcID
+                                 }
+        
+        heroCount += 1
+        if heroCount >= ShareDefine.LineupObjMax:
+            break
+        
+    # 主公属性
+    lordAttrDict = PlayerControl.GetLordAttr(curPlayer)
+    
+    # 其他
+    
+    lineupInfo = {"PlayerID":playerID, "LordAttrDict":lordAttrDict, "Hero":heroDict}
+    return lineupInfo
+
+def GetNPCLineup(lineupID):
+    ## 获取NPC阵容信息
+    # @param lineupID: 阵容ID
+    # @return: 阵容全部信息json字典,前端通用格式
+    ipyData = IpyGameDataPY.GetIpyGameData("NPCLineup", lineupID)
+    if not ipyData:
+        return {}
+    
+    heroDict = {}
+    for posNum in range(1, 1 + 10):
+        if not hasattr(ipyData, "GetPosNPCID%s" % posNum):
+            break
+        npcID = getattr(ipyData, "GetPosNPCID%s" % posNum)()
+        if not npcID:
+            continue
+        
+        heroDict[str(posNum)] = {"NPCID":npcID}
+        
+    lineupInfo = {"Hero":heroDict}
+    return lineupInfo
+
+def SummonLineupObjs(batLineup, faction, num, lineupInfo, playerID=0):
+    '''召唤阵容战斗实例
+    @param faction: 所属阵营,目前支持两个阵营,1或2
+    @param num: 战斗阵容在该阵营上的编号,1V1时默认1,多对多时1~n
+    @param lineupInfo: 阵容信息
+    @param playerID: 发起的玩家ID,系统场次为0
+    '''
+    
+    '''关于视野层级说明 sightLevel
+        玩家发起的战斗视野层级只能用玩家ID,才能达到只通知该玩家的效果
+        但是由于同个玩家可能同时存在多场战斗,如主线战斗 + 其他战斗,所以视野层级只能用作仅通知该玩家的作用,
+        不能用于处理NPC视野来遍历伤害对象等,因为不同战场的NPC视野层级可能相同,对于同场战斗NPC的遍历处理只能用 turnFight 管理遍历
+        鉴于视野层级仅用于通知的作用,故系统场次理论上可以只用一个视野层级,但是为了减少不必要的问题,系统场次的视野层级还是尽量区分开来
+        这里暂时用时间戳的后4位来做短时间内的理论上唯一视野层级
+    '''
+    GameWorld.DebugLog("SummonLineupObjs faction:%s,num:%s,lineupInfo=%s" % (faction, num, lineupInfo), playerID)
+    if playerID:
+        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+        if not curPlayer:
+            return
+        posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
+        sightLevel = playerID
+        
+        # 开发过程中可以先开启视野层级及视野,方便检查战斗相关是否有遗漏
+        if curPlayer.GetSightLevel() != sightLevel:
+            PlayerControl.SetPlayerSightLevel(curPlayer, sightLevel)
+        sight = ChConfig.Def_PlayerSight_Default * 5
+        if curPlayer.GetSight() != sight:
+            PlayerControl.SetSight(curPlayer, sight)
+    else:
+        gameMap = GameWorld.GetMap()
+        posX, posY = gameMap.GetRebornMapX(), gameMap.GetRebornMapY() # 系统战斗默认取当前地图的复活点,需要优化
+        sightLevel = 2000000000 + int(time.time()) % 10000
+        
+    GameWorld.DebugLog("sightLevel=%s,pos=(%s,%s)" % (sightLevel, posX, posY), playerID)
+    
+    lineupPlayerID = lineupInfo.get("PlayerID", 0) # 阵容所属玩家ID
+    heroDict = lineupInfo.get("Hero", {})
+    tick = GameWorld.GetGameWorld().GetTick()
+    
+    initXP = IpyGameDataPY.GetFuncCfg("AngerXP", 1)
+    tfMgr = GetTurnFightMgr()
+    space = 3
+    for posNumKey, heroInfo in heroDict.items():
+        posNum = int(posNumKey)
+        
+        skillIDList = []
+        if lineupPlayerID:
+            heroID = heroInfo.get("ID", 0)
+            npcID = heroInfo.get("NPCID", 0)
+            heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
+            if not heroIpyData:
+                continue
+            normalSkillID = heroIpyData.GetNormalSkillID()
+            angerSkillID = heroIpyData.GetAngerSkillID()
+            skillIDList += [normalSkillID, angerSkillID]
+        else:
+            npcID = heroInfo.get("NPCID", 0)
+            npcDataEx = NPCCommon.GetNPCDataEx(npcID)
+            if not npcDataEx:
+                continue
+            skillIDList += npcDataEx.GetSkillIDList()
+            
+        if not npcID:
+            continue
+        
+        npcData = GameWorld.GetGameData().FindNPCDataByID(npcID)
+        if not npcData:
+            continue
+        curSummon = GameWorld.GetNPCManager().AddPlayerSummonNPC()
+        if not curSummon:
+            continue
+        objID = curSummon.GetID()
+        batLineup.npcPosDict[posNum] = curSummon
+        batLineup.npcObjIDDict[objID] = curSummon
+        tfMgr.setNPCGUID(objID, batLineup.getGUID())
+        
+        curSummon.SetNPCTypeID(npcID)
+        curSummon.SetBornTime(tick)
+        curSummon.SetAIType(0)
+        curSummon.SetSightLevel(sightLevel)
+        curSummon.SetIsNeedProcess(False)
+        curSummon.SetCanAttack(True)
+        curSummon.SetVisible(True)
+        curSummon.SetDict(ChConfig.Def_Obj_Dict_TurnFightPosInfo, num * 100 + posNum)
+        curSummon.SetDict(ChConfig.Def_Obj_Dict_LineupPlayerID, lineupPlayerID)
+        GameObj.SetFaction(curSummon, faction)
+        GameObj.SetXP(curSummon, initXP, False)
+        
+        skillManager = curSummon.GetSkillManager()
+        #有指定的技能,重新学习
+        if skillIDList:
+            skillManager.ResetSkill()
+            for skillID in skillIDList:
+                skillManager.LVUPSkillByID(skillID)
+                
+        rebornX = posX - space + (faction - 1) * space * 3 + ((posNum - 1) / 3 * space * 2 * (-1 if faction == 1 else 1))
+        rebornY = posY + (posNum - 1) % 3 * space
+        #GameWorld.DebugLog("Reborn ID:%s, faction:%s,posNum=%s, (%s,%s), %s" % (curSummon.GetID(), faction, posNum, rebornX, rebornY, skillIDList))
+        curSummon.Reborn(rebornX, rebornY, False)
+        NPCCommon.NPCControl(curSummon).DoNPCRebornCommLogic(tick)
+        
+    return
+
+#// B4 13 主线战斗请求 #tagCSMainFightReq
+#
+#struct    tagCSMainFightReq
+#{
+#    tagHead        Head;
+#    BYTE        ReqType;        // 0-停止战斗回城;1-设置消耗倍值;2-挑战关卡小怪;3-挑战关卡boss;4-继续战斗;
+#    DWORD        ReqValue;    // 请求值,ReqType为1时发送消耗倍值
+#};
+def OnMainFightReq(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    reqType = clientData.ReqType
+    reqValue = clientData.ReqValue
+    
+    if reqType == 0:
+        __doExitMainFight(curPlayer)
+        return
+    elif reqType == 1:
+        __doSetFightPoint(curPlayer, reqValue)
+        return
+    
+    clientPack = ChPyNetSendPack.tagSCTurnFightReportSign()
+    clientPack.Sign = 0
+    NetPackCommon.SendFakePack(curPlayer, clientPack) # 标记开始
+    
+    if reqType == 2: # 前端主动请求开始关卡小怪的视为从休息中开始
+        __doMainLevelWave(curPlayer, True)
+    elif reqType == 3:
+        __doMainBossStart(curPlayer, tick)
+    elif reqType == 4:
+        __doMainFight(curPlayer, tick)
+    else:
+        pass
+    
+    # 标记结束
+    clientPack.Sign = 1
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+def __doExitMainFight(curPlayer):
+    ## 主线退出战斗 - 回城休息
+    mainFightMgr = GetMainFightMgr(curPlayer)
+    turnFight = mainFightMgr.turnFight
+    if turnFight:
+        turnFight.clearFight()
+    return
+
+def __doSetFightPoint(curPlayer, fightPoint):
+    ## 设置消耗倍值
+    GameWorld.DebugLog("设置战锤消耗倍值: %s" % fightPoint)
+    if fightPoint == 1:
+        pass
+    elif fightPoint == 2:
+        # 条件验证
+        pass
+    elif fightPoint == 3:
+        # 条件验证
+        pass
+    else:
+        return
+    curPlayer.SetFightPoint(fightPoint)
+    return
+
+def __doMainLevelWave(curPlayer, isRestStart=False):
+    ## 开始新的关卡波
+    # @param isRestStart: 是否从休息状态重新开始的
+    playerID = curPlayer.GetPlayerID()
+    chapterID, levelNum, wave = PlayerControl.GetMainLevelNowInfo(curPlayer)
+    if not chapterID:
+        chapterID = 1
+    if not levelNum:
+        levelNum =1
+    if not wave:
+        wave = 1
+    GameWorld.DebugLog("请求关卡波战斗: chapterID=%s,levelNum=%s,wave=%s,isRestStart=%s" % (chapterID, levelNum, wave, isRestStart), playerID)
+    fightPoint = max(curPlayer.GetFightPoint(), 1)
+    if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
+        GameWorld.DebugLog("战锤不足,无法开始!")
+        return
+    chapterIpyData = IpyGameDataPY.GetIpyGameData("MainChapter", chapterID)
+    if not chapterIpyData:
+        return
+    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
+    if not levelIpyData:
+        # 如上线后减少某个章节关卡数的情况,导致找不到,则从该章节回退到存在的关卡开始
+        while levelNum > 1:
+            levelNum -= 1 
+            levelIpyData = IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID, levelNum)
+            if levelIpyData:
+                break
+    if not levelIpyData:
+        return
+    
+    # 本关卡最大波数,暂时支持最大6波
+    waveMax = 6
+    while waveMax >= 1 and (not hasattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax) or not getattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax)()):
+        waveMax -= 1
+    if waveMax < 1:
+        return
+    if wave > waveMax:
+        wave = waveMax
+        
+    waveLineupList = getattr(levelIpyData, "GetWaveLineupIDList%s" % wave)() # 小队1阵容ID|小队2阵容ID|...
+    if not waveLineupList:
+        return
+    teamMax = len(waveLineupList)
+    teamNum = 1
+    lineupID = waveLineupList[teamNum - 1] # NPC阵容ID
+    
+    mainFightMgr = GetMainFightMgr(curPlayer)
+    mainFightMgr.nextTeam = False
+    mainFightMgr.chapterID = chapterID
+    mainFightMgr.levelNum = levelNum
+    mainFightMgr.waveMax = waveMax
+    mainFightMgr.wave = wave
+    mainFightMgr.waveLineupList = waveLineupList
+    mainFightMgr.teamNum = teamNum
+    mainFightMgr.teamMax = teamMax
+    turnMax = IpyGameDataPY.GetFuncCfg("Mainline", 2)
+    mapID, funcLineID = ChConfig.Def_FBMapID_Main, PlayerControl.ComMainLevelValue(chapterID, levelNum, wave)
+    GameWorld.DebugLog("设置起始关卡波: 关卡%s-%s,波=%s/%s,teamMax=%s,mapID=%s,funcLineID=%s,lineupID=%s" 
+                       % (chapterID, levelNum, wave, waveMax, teamMax, mapID, funcLineID, lineupID), playerID)
+    
+    turnFight = mainFightMgr.turnFight
+    turnFight.setTurn(mapID, funcLineID, turnMax, False, {"teamNum":teamNum, "teamMax":teamMax})
+    turnFight.setFactionLineup(Def_FactionA, {1:GetPlayerLineup(curPlayer, ShareDefine.Lineup_Main)}, True)
+    turnFight.setFactionLineup(Def_FactionB, {1:GetNPCLineup(lineupID)})
+    turnFight.sortActionQueue()
+    turnFight.syncInit()
+    return
+
+def __doMainBossStart(curPlayer, tick):
+    ## 开始挑战关卡boss
+    playerID = curPlayer.GetPlayerID()
+    chapterID, levelNum, wave = PlayerControl.GetMainLevelPassInfo(curPlayer)
+    if not chapterID:
+        chapterID = 1
+    if not levelNum:
+        levelNum = 1
+    GameWorld.DebugLog("请求挑战关卡Boss! passInfo: chapterID=%s,levelNum=%s,wave=%s" % (chapterID, levelNum, wave), playerID)
+    chapterIpyData = IpyGameDataPY.GetIpyGameData("MainChapter", chapterID)
+    if not chapterIpyData:
+        return
+    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
+    if not levelIpyData:
+        return
+    
+    nowChapterID, nowLevelNum, _ = PlayerControl.GetMainLevelNowInfo(curPlayer)
+    if chapterID != nowChapterID or levelNum != nowLevelNum:
+        '''一般有两种情况会导致
+        1. 理论上玩家当前刷怪的章节关卡一定是与过关的章节关卡是一致的,除了全部章节已通关
+                                当通关时,已过关的记录会被设置为下一章节第1关(版本关卡表实际不存在该关卡)
+                                但是当前刷怪的还是版本的最后一关,固会出现不一致的情况
+                                在增加章节关卡新版本更新后玩家重登会自动进行修复当前刷怪进度为最新章节第1关
+        2. 真的出现问题了,可能是bug引起,这种情况只能进行bug修复及针对异常账号特殊处理,或者重登后也会自动进行修正
+        '''
+        GameWorld.ErrLog("当前刷怪章节关卡与挑战的boss章节关卡不一致,无法挑战! chapterID=%s,levelNum=%s,nowChapterID=%s,nowLevelNum=%s" 
+                         % (chapterID, levelNum, nowChapterID, nowLevelNum), playerID)
+        return
+    
+    # 本关卡最大波数
+    waveMax = 6
+    while waveMax >= 1 and (not hasattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax) or not getattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax)()):
+        waveMax -= 1
+        
+    if wave < waveMax:
+        GameWorld.DebugLog("最后一波未通过,无法挑战本关boss! passWave=%s < %s" % (wave, waveMax))
+        return
+    
+    waveLineupList = levelIpyData.GetBossLineupIDList() # Boss波阵容ID列表,小队1阵容ID|小队2阵容ID|...
+    if not waveLineupList:
+        return
+    teamMax = len(waveLineupList)
+    teamNum = 1
+    lineupID = waveLineupList[teamNum - 1] # NPC阵容ID
+    
+    wave = waveMax = 1 # 关卡boss固定只有一波
+    
+    mainFightMgr = GetMainFightMgr(curPlayer)
+    mainFightMgr.nextTeam = False
+    mainFightMgr.chapterID = chapterID
+    mainFightMgr.levelNum = levelNum
+    mainFightMgr.waveMax = waveMax
+    mainFightMgr.wave = wave
+    mainFightMgr.waveLineupList = waveLineupList
+    mainFightMgr.teamNum = teamNum
+    mainFightMgr.teamMax = teamMax
+    turnMax = IpyGameDataPY.GetFuncCfg("Mainline", 3)
+    mapID, funcLineID = ChConfig.Def_FBMapID_MainBoss, PlayerControl.ComMainLevelValue(chapterID, levelNum, wave)
+    GameWorld.DebugLog("设置关卡boss: 关卡%s-%s,波=%s/%s,teamMax=%s,mapID=%s,funcLineID=%s,lineupID=%s" 
+                       % (chapterID, levelNum, wave, waveMax, teamMax, mapID, funcLineID, lineupID), playerID)
+    
+    turnFight = mainFightMgr.turnFight
+    turnFight.setTurn(mapID, funcLineID, turnMax, False, {"teamNum":teamNum, "teamMax":teamMax})
+    turnFight.setFactionLineup(Def_FactionA, {1:GetPlayerLineup(curPlayer, ShareDefine.Lineup_Main)}, True)
+    turnFight.setFactionLineup(Def_FactionB, {1:GetNPCLineup(lineupID)})
+    turnFight.sortActionQueue()
+    turnFight.syncInit()
+    
+    # 挑战boss无中间过程,每次执行直接挑战一队结果
+    __processTurnFight(turnFight.guid, tick)
+    return
+
+def __doMainFight(curPlayer, tick):
+    ## 主线执行战斗
+    mainFightMgr = GetMainFightMgr(curPlayer)
+    turnFight = mainFightMgr.turnFight
+    
+    isLevelBoss = mainFightMgr.isLevelBoss()
+    winFaction = turnFight.winFaction
+    if winFaction:
+        if mainFightMgr.nextTeam:
+            # 自动开始下一小队
+            teamNum = mainFightMgr.teamNum = mainFightMgr.teamNum + 1
+            GameWorld.DebugLog("开始进入下一小队: teamNum=%s" % teamNum)
+            if teamNum < 1 or teamNum > len(mainFightMgr.waveLineupList):
+                GameWorld.DebugLog("111111 teamNum=%s,mainFightMgr.waveLineupList=%s" % (teamNum, mainFightMgr.waveLineupList))
+                return
+            lineupID = mainFightMgr.waveLineupList[teamNum - 1] # NPC阵容ID
+            GameWorld.DebugLog("teamNum=%s,lineupID=%s" % (teamNum, lineupID))
+            
+            mainFightMgr.nextTeam = False
+            turnFight.resetTurn({"teamNum":teamNum})
+            # 切换小队时,玩家阵容不需要处理,保留状态
+            turnFight.setFactionLineup(Def_FactionB, {1:GetNPCLineup(lineupID)})
+            turnFight.sortActionQueue()
+            turnFight.syncInit()
+            
+            if mainFightMgr.isLevelBoss():
+                # 每次处理一小队的完整战斗,相当于一次完整战报
+                __processTurnFight(turnFight.guid, tick)
+                return
+        else:
+            __doMainLevelWave(curPlayer, False)
+        return
+    
+    if isLevelBoss:
+        ## 关卡boss是一次性处理完的,一般不可能走到这里,这边做下防范
+        return
+    
+    # 以下均是处理关卡小怪分段实时战斗
+    
+    EntryLogic(turnFight)
+    
+    # 小怪战斗,每次消耗1个战锤
+    fightPoint = max(curPlayer.GetFightPoint(), 1) # 主线战斗消耗倍值,默认1
+    
+    # 按阵营阵容执行顺序,逐个遍历
+    doCnt = 0
+    doMax = (PosNumMax + 2) * len(turnFight.actionSortList) # 防止死循环,做最大循环次数限制 = (最大位置数 + 主公、红颜位置)*行动阵容数
+    overLineupList = [] # 本回合已经结束行动的阵容列表 [(faction, num), ...], 所有阵容全部结束代表本回合结束
+    
+    turnNum = turnFight.turnNum
+    unXiantaoCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCnt)
+    GameWorld.DebugLog("unXiantaoCnt=%s,turnNum=%s,doMax=%s,actionIndex=%s,%s" % (unXiantaoCnt, turnNum, doMax, turnFight.actionIndex, turnFight.actionSortList))
+    while doCnt < doMax and len(overLineupList) < len(turnFight.actionSortList):
+        doCnt += 1
+        turnNum = turnFight.turnNum
+        # 执行回合开始逻辑
+        if turnFight.turnStart < turnNum:
+            GameWorld.DebugLog("执行行动: turnNum=%s, 回合开始" % (turnFight.turnNum))
+            turnFight.syncState(FightState_Fighting)
+            if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
+                GameWorld.DebugLog("回合开始时战锤不足!")
+                return
+            turnFight.turnStart = turnNum
+            turnFight.actionIndex = 0
+            for faction, num in turnFight.actionSortList:
+                batFaction = turnFight.getBatFaction(faction)
+                batLineup = batFaction.getBatlineup(num)
+                batLineup.actionNum = ActionNumStart
+                for curNPC in batLineup.npcPosDict.values():
+                    TurnFightObjPerTurnStart(curNPC, None, turnNum, tick)
+                    
+        if turnFight.actionIndex >= len(turnFight.actionSortList):
+            turnFight.actionIndex = 0
+            
+        faction, num = turnFight.actionSortList[turnFight.actionIndex]
+        batFaction = turnFight.getBatFaction(faction)
+        batLineup = batFaction.getBatlineup(num)
+        if batLineup.actionNum > max(batLineup.npcPosDict):
+            if (faction, num) not in overLineupList:
+                overLineupList.append((faction, num))
+                
+            turnFight.actionIndex += 1
+            continue
+        
+        if faction == Def_FactionA:
+            if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
+                GameWorld.DebugLog("战锤不足!")
+                return
+        GameWorld.DebugLog("执行行动: turnNum=%s,faction=%s,num=%s,actionNum=%s" % (turnFight.turnNum, faction, num, batLineup.actionNum))
+        
+        # 主公
+        if batLineup.actionNum == -1:
+            pass
+        
+        # 红颜
+        elif batLineup.actionNum == 0:
+            pass
+        
+        # 武将
+        elif batLineup.actionNum > 0:
+            for posNum in range(batLineup.actionNum, PosNumMax + 1):
+                batLineup.actionNum = posNum
+                if posNum not in batLineup.npcPosDict:
+                    continue
+                curNPC = batLineup.npcPosDict[posNum]
+                if not curNPC or GameObj.GetHP(curNPC) <= 0:
+                    continue
+                objType = curNPC.GetGameObjType()
+                objID = curNPC.GetID()
+                turnFight.syncObjAction(turnNum, objType, objID)
+                
+                objName = GetObjName(curNPC)
+                curHP = GameObj.GetHP(curNPC)
+                
+                GameWorld.DebugLog("★回合%s %s 行动 : curHP=%s" % (turnNum, objName, curHP))
+                if not DoAttack(curNPC, None, tick):
+                    GameWorld.DebugLog("        攻击失败")
+                    continue
+                
+                if faction == Def_FactionA:
+                    if not PlayerControl.PayMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint, isNotify=False):
+                        return
+                break
+            
+        turnFight.actionIndex += 1
+        batLineup.actionNum += 1
+        if batLineup.actionNum > max(batLineup.npcPosDict):
+            GameWorld.DebugLog("该阵容本回合已经全部行动完了: turnNum=%s,faction=%s,num=%s,actionNum=%s" % (turnFight.turnNum, faction, num, batLineup.actionNum))
+            if (faction, num) not in overLineupList:
+                overLineupList.append((faction, num))
+                
+        if turnFight.checkOverByKilled():
+            break
+        
+        nowUnXiantaoCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCnt)
+        if unXiantaoCnt != nowUnXiantaoCnt:
+            # 玩家有消耗战锤则停止,一段段执行
+            GameWorld.DebugLog("玩家有消耗战锤则停止,nowUnXiantaoCnt=%s" % (nowUnXiantaoCnt))
+            break
+        
+    if turnFight.winFaction:
+        return
+    
+    GameWorld.DebugLog("turnNum=%s,doCnt=%s,overLineupList=%s" % (turnNum, doCnt, overLineupList))
+    if len(overLineupList) >= len(turnFight.actionSortList):
+        GameWorld.DebugLog("执行行动: turnNum=%s, 回合结束" % (turnFight.turnNum))
+        if turnFight.turnEnd < turnNum:
+            if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
+                GameWorld.DebugLog("回合结束时战锤不足!")
+                return
+            turnFight.turnEnd = turnNum
+            for faction, num in turnFight.actionSortList:
+                batFaction = turnFight.getBatFaction(faction)
+                batLineup = batFaction.getBatlineup(num)
+                for curNPC in batLineup.npcPosDict.values():
+                    pass
+                
+            if turnFight.checkOverByKilled():
+                return
+            
+        if turnNum < turnFight.turnMax:
+            turnFight.turnNum += 1
+        else:
+            OnTurnAllOver(turnFight.guid)
+            
+    return
+
+def __processTurnFight(guid, tick):
+    ## 一次性处理完一个小队的战斗
+    turnFight = GetTurnFightMgr().getTurnFight(guid)
+    curPlayer = turnFight.curPlayer
+    turnMax = turnFight.turnMax
+    EntryLogic(turnFight)
+    for turnNum in range(1, turnMax + 1):
+        turnFight.turnNum = turnNum
+        GameWorld.DebugLog("【----- 回合制战斗轮次: %s -----】" % turnNum)
+        if curPlayer:
+            turnFight.syncState(FightState_Fighting)
+            
+        # 回合开始
+        for faction, num in turnFight.actionSortList:
+            GameWorld.DebugLog("回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+            batFaction = turnFight.getBatFaction(faction)
+            batLineup = batFaction.getBatlineup(num)
+            batLineup.actionNum = 1
+            for curNPC in batLineup.npcPosDict.values():
+                TurnFightObjPerTurnStart(curNPC, None, turnNum, tick)
+                
+        # 主公
+        for faction, num in turnFight.actionSortList:
+            GameWorld.DebugLog("主公逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+            batFaction = turnFight.getBatFaction(faction)
+            batLineup = batFaction.getBatlineup(num)
+            
+        # 红颜
+        for faction, num in turnFight.actionSortList:
+            GameWorld.DebugLog("红颜逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+            batFaction = turnFight.getBatFaction(faction)
+            batLineup = batFaction.getBatlineup(num)
+            
+        if turnFight.checkOverByKilled():
+            break
+        
+        # 武将
+        doMax = PosNumMax * len(turnFight.actionSortList)
+        doCnt = 0
+        while doCnt < doMax and turnFight.actionIndex < len(turnFight.actionSortList):
+            doCnt += 1
+            faction, num = turnFight.actionSortList[turnFight.actionIndex]
+            batFaction = turnFight.getBatFaction(faction)
+            batLineup = batFaction.getBatlineup(num)
+            for posNum in range(batLineup.actionNum, PosNumMax + 1):
+                batLineup.actionNum = posNum + 1
+                if posNum not in batLineup.npcPosDict:
+                    continue
+                curNPC = batLineup.npcPosDict[posNum]
+                if not curNPC or GameObj.GetHP(curNPC) <= 0:
+                    continue
+                objType = curNPC.GetGameObjType()
+                objID = curNPC.GetID()
+                turnFight.syncObjAction(turnNum, objType, objID)
+                
+                objName = GetObjName(curNPC)
+                curHP = GameObj.GetHP(curNPC)
+                
+                GameWorld.DebugLog("★回合%s %s 行动 : curHP=%s" % (turnNum, objName, curHP))
+                if not DoAttack(curNPC, None, tick):
+                    continue
+                
+                break
+            
+            if turnFight.actionIndex >= len(turnFight.actionSortList) - 1:
+                turnFight.actionIndex = 0
+            else:
+                turnFight.actionIndex += 1
+                
+        # 回合结束
+        for faction, num in turnFight.actionSortList:
+            GameWorld.DebugLog("回合结束逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+            batFaction = turnFight.getBatFaction(faction)
+            batLineup = batFaction.getBatlineup(num)
+            for curNPC in batLineup.npcPosDict.values():
+                pass
+            
+        if turnFight.checkOverByKilled():
+            break
+        
+    if not turnFight.winFaction:
+        OnTurnAllOver(turnFight.guid)
+    return
+
 #// B4 10 回合制战斗 #tagCMTurnFight
 #
 #struct    tagCMTurnFight
 #{
 #    tagHead        Head;
-#    DWORD        MapID;    // 自定义地图ID,可用于绑定战斗场景功能(如野外关卡,爬塔功能,竞技场等)
-#    WORD        FuncLineID;
-#    BYTE        TagType;    // 战斗目标类型,0-NPC,1-玩家,2-队伍
-#    DWORD        TagID;    // 战斗目标类型对应的ID
+#    DWORD        MapID;        // 自定义地图ID,可用于绑定战斗地图场景功能(如主线关卡、主线boss、爬塔、竞技场等)
+#    DWORD        FuncLineID;    // MapID对应的扩展值,如具体某个关卡等
+#    BYTE        TagType;    // 目标类型,0-NPC阵容,1-玩家
+#    DWORD        TagID;    // 目标类型对应的ID,如阵容ID或玩家ID
 #    BYTE        ValueCount;
 #    DWORD        ValueList[ValueCount]; // 附加值列表,可选,具体含义由MapID决定
 #};
 def OnTurnFight(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    mapID = clientData.MapID
-    funcLineID = clientData.FuncLineID
-    tagType = clientData.TagType
-    tagID = clientData.TagID
-    valueList = clientData.ValueList
+    #curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    #mapID = clientData.MapID
+    #funcLineID = clientData.FuncLineID
+    return
+
+def GetObjName(gameObj):
+    objName = gameObj.GetName()
+    faction = GameObj.GetFaction(gameObj)
+    posInfo = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo)
+    npcID = gameObj.GetNPCID()
+    return "%s%s %s[%s-%s]" % ("A" if faction == Def_FactionA else "B", posInfo, objName, gameObj.GetID(), npcID)
+
+def EntryLogic(turnFight):
+    ## 执行进场逻辑
+    if turnFight.enterLogic:
+        return
+    GameWorld.DebugLog("执行进场逻辑...")
     
-    playerID = curPlayer.GetPlayerID()
-    if tagType == ChConfig.TurnBattle_TagType_Player:
-        if tagID == playerID:
-            GameWorld.DebugLog("不能打自己!", playerID)
+    turnFight.enterLogic = True
+    return
+
+def TurnFightObjPerTurnStart(gameObj, tagObj, turnNum, tick):
+    ## 回合制战斗实例 - 每回合开始时处理
+    if not gameObj:
+        return
+    
+#    SetTimeline(gameObj, turnNum, 0)
+#    # 重置连击、反击数
+#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnComboNum, 0)
+#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnAtkBackNum, 0)
+#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnParryNum, 0)
+#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnMissNum, 0)
+#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnAtkAddXPCount, 0)
+#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnXPUseState, 0)
+#    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrFaintCD, 0) # 击晕CD
+#    
+#    objType = gameObj.GetGameObjType()
+#    objID = gameObj.GetID()
+#    objName = GetObjName(gameObj)
+#    GameWorld.DebugLog("ObjPerTurnStart: 回合%s, %s objType-ID-HP(%s-%s-%s)" % (turnNum, objName, objType, objID, GameObj.GetHP(gameObj)))
+#    
+#    # 每回合开始减技能CD
+#    skillManager = gameObj.GetSkillManager()
+#    for i in range(skillManager.GetSkillCount()):
+#        skill = skillManager.GetSkillByIndex(i)
+#        remainTime = skill.GetRemainTime()
+#        if not remainTime:
+#            continue
+#        skillID = skill.GetSkillID()
+#        updRemainTime = max(0, remainTime - ChConfig.Def_PerTurnTick)
+#        skill.SetRemainTime(updRemainTime)
+#        GameWorld.DebugLog("    skillID=%s,remainTime=%s,updRemainTime=%s" % (skillID, remainTime, updRemainTime))
+#        
+#    # 刷新定时处理的buff效果
+#    SkillShell.ProcessPersistBuff(gameObj, tick)
+#    
+#    PassiveBuffEffMng.OnPassiveSkillTrigger(gameObj, tagObj, None, ChConfig.TriggerType_TurnNum, tick)
+#    
+#    __logGameObjAttr(gameObj)
+    return
+
+def AddTurnObjCureHP(curObj, srcObj, addValue, cureHP, skillID=0):
+    ## 回合对象添加治疗值
+    if not curObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
+        return
+    
+    curFaction = GameObj.GetFaction(curObj)
+    if not srcObj or curFaction != GameObj.GetFaction(srcObj):
+        # 同阵营的治疗才统计
+        return
+    
+    curID = curObj.GetID()
+    srcID = srcObj.GetID()
+    turnFight = GetTurnFightMgr().getNPCTurnFight(curID)
+    if not turnFight:
+        return
+    
+    curBatFaction = turnFight.getBatFaction(curFaction)
+    for num in curBatFaction.lineupDict.keys():
+        batLineup = curBatFaction.getBatlineup(num)
+        updStatValue = batLineup.statCureValue(srcID, cureHP)
+        if updStatValue == None:
+            continue
+        GameWorld.DebugLog("        统计治疗: curTD=%s,srcID=%s,skillID=%s,addValue=%s,cureHP=%s,updStatValue=%s" 
+                       % (curID, srcID, skillID, addValue, cureHP, updStatValue))
+        break
+    
+    return
+
+def AddTurnObjHurtValue(curObj, tagObj, hurtType, hurtValue, lostHP, curSkill=None):
+    ## 回合对象添加伤害值
+    if not curObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
+        return
+    curID = curObj.GetID()
+    tagID = tagObj.GetID()
+    skillID = curSkill.GetSkillID() if curSkill else 0
+    turnFight = GetTurnFightMgr().getNPCTurnFight(curID)
+    if not turnFight:
+        return
+    
+    if curID != tagID:
+        curBatFaction = turnFight.getBatFaction(GameObj.GetFaction(curObj))
+        for num in curBatFaction.lineupDict.keys():
+            batLineup = curBatFaction.getBatlineup(num)
+            updStatValue = batLineup.statHurtValue(curID, lostHP)
+            if updStatValue == None:
+                continue
+            GameWorld.DebugLog("        统计伤血: curTD=%s,tagID=%s,skillID=%s,hurtType=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,tagHP=%s" 
+                           % (curID, tagID, skillID, hurtType, hurtValue, lostHP, updStatValue, GameObj.GetHP(tagObj)))
+            break
+        
+        tagBatFaction = turnFight.getBatFaction(GameObj.GetFaction(tagObj))
+        for num in tagBatFaction.lineupDict.keys():
+            batLineup = tagBatFaction.getBatlineup(num)
+            updStatValue = batLineup.statDefValue(tagID, lostHP)
+            if updStatValue == None:
+                continue
+            GameWorld.DebugLog("        统计承伤: curTD=%s,tagID=%s,skillID=%s,hurtType=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,curHP=%s" 
+                           % (tagID, curID, skillID, hurtType, hurtValue, lostHP, updStatValue, GameObj.GetHP(tagObj)))
+            break
+        
+    else:
+        # 如换血类技能,自残的伤害不算输出
+        GameWorld.DebugLog("        自残: curTD=%s,tagID=%s,skillID=%s,hurtType=%s,hurtValue=%s,lostHP=%s,curHP=%s" 
+                           % (curID, tagID, skillID, hurtType, hurtValue, lostHP, GameObj.GetHP(curObj)))
+        
+    if lostHP and curID != tagID:
+        AddTurnFightXP(tagObj, __GetAddXP_Defender(tagObj, lostHP), "skillID:%s" % skillID)
+    return
+
+def __GetAddXP_Attack(attack, curSkill):
+    ## 攻击方增加的XP值根据主动普攻技能获得
+    if not curSkill:
+        return 0
+    if not SkillCommon.isNormalAtkSkill(curSkill):
+        return 0
+    return IpyGameDataPY.GetFuncCfg("AngerXP", 3)
+
+def __GetAddXP_Defender(defender, lostHP):
+    ## 掉血方增加的XP值根据掉血百分比获得
+    if lostHP <= 0:
+        return 0
+    return IpyGameDataPY.GetFuncCfg("AngerXP", 4)
+
+def AddTurnFightXP(gameObj, addXP, reason=""):
+    ## 回合战斗增加XP
+    if addXP <= 0 or not addXP:
+        #GameWorld.DebugLog("        没有增加XP! curID=%s" % (gameObj.GetID()))
+        return
+    posNum = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo) % 10
+    if posNum <= 0:
+        #非主战单位不加
+        return
+    curXP = GameObj.GetXP(gameObj)
+    updXP = curXP + addXP
+    GameObj.SetXP(gameObj, updXP)
+    GameWorld.DebugLog("        更新XP: curID=%s,curXP=%s,addXP=%s,updXP=%s,reason=%s" % (gameObj.GetID(), curXP, addXP, updXP, reason))
+    return
+
+def DoAttack(curNPC, tagNPC, tick, turnBattleType=ChConfig.TurnBattleType_Normal, useSkill=None):
+    curID = curNPC.GetID()
+    npcID = curNPC.GetNPCID()
+    objName = GetObjName(curNPC)
+    GameWorld.DebugLog("    ● %s DoAttack: curID=%s,,turnBattleType=%s" % (objName, curID, turnBattleType))
+    
+    atkOK = False
+    tagObj = tagNPC
+    curNPC.SetDict(ChConfig.Def_Obj_Dict_TurnBattleType, turnBattleType)
+    
+    if turnBattleType == ChConfig.TurnBattleType_AtkBack:
+        if not tagObj:
             return
+        atkOK = BaseAttack.Attack(curNPC, tagObj, None, tick) # 反击为单体普攻
+    elif turnBattleType == ChConfig.TurnBattleType_Combo:
+        if not tagObj:
+            return
+        atkOK = SkillShell.DoLogic_UseSkill(curNPC, tagObj, useSkill, tick)
+    else:
+        curXP = GameObj.GetXP(curNPC)
+        xpMax = IpyGameDataPY.GetFuncCfg("AngerXP", 2) 
+        skillManager = curNPC.GetSkillManager()
+        useSkillList = []
+        GameWorld.DebugLog('skillCount=%s' % skillManager.GetSkillCount(), npcID)
+        for index in range(0, skillManager.GetSkillCount()):
+            useSkill = skillManager.GetSkillByIndex(index)
+            #已经到尾部了
+            if not useSkill or useSkill.GetSkillTypeID() == 0:
+                break
+            #被动技能无法使用
+            if SkillCommon.isPassiveSkill(useSkill):
+                continue
+            #还在冷却时间内无法释放
+            if SkillCommon.RefreshSkillRemainTime(useSkill, tick) != 0:
+                continue
+            skillTypeID = useSkill.GetSkillTypeID()
+            # 常规攻击优先xp
+            if SkillCommon.isXPSkill(useSkill) and turnBattleType == ChConfig.TurnBattleType_Normal:
+                if curXP < xpMax:
+                    continue
+                useCnt = -1 # xp技能优先释放
+            else:
+                useCnt = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_SkillUseCnt % skillTypeID) # 该技能已使用次数
+            useSkillList.append([useCnt, index, skillTypeID, useSkill])
+            
+        useSkillList.sort() # 按使用次数优先升序排,使用次数低的优先判断使用
+        #GameWorld.DebugLog('    技能使用顺序 = useSkillList%s' % str(useSkillList), npcID)
         
-    reqRet = FBLogic.OnTurnFightRequest(curPlayer, mapID, funcLineID, tagType, tagID, valueList)
-    if not reqRet:
+        for useInfo in useSkillList:
+            useSkill = useInfo[-1]
+            skillID = useSkill.GetSkillID()
+            atkOK, tagObj = DoNPCUseSkill(curNPC, useSkill, tick)
+            if atkOK:
+                AddTurnFightXP(curNPC, __GetAddXP_Attack(curNPC, useSkill), "skillID:%s" % skillID)
+                break
+            
+        if not atkOK:
+            tagObj = GetEnemyObj(curNPC)
+            if tagObj:
+                GameWorld.DebugLog('    无技能可攻击,直接使用普攻! tagID=%s(%s)' % (tagObj.GetID(), GetObjName(tagObj)), npcID)
+                atkOK = BaseAttack.Attack(curNPC, tagObj, None, tick)
+                
+    curNPC.SetDict(ChConfig.Def_Obj_Dict_TurnBattleType, 0) # 无论攻击成功与否都重置战斗类型
+    
+    tagID = 0
+    tagHP = 0
+    if tagObj:
+        tagID = tagObj.GetID()
+        tagHP = GameObj.GetHP(tagObj)
+    GameWorld.DebugLog('        atkOK=%s,tagID=%s,tagHP=%s' % (atkOK, tagID, tagHP), npcID)
+    if not atkOK:
         return
     
-    # 需要发送到GameServer验证
-    if mapID in ChConfig.Def_TFMapID_SendToGameServer:
-        SendToGameServer_TurnFight(curPlayer, "TurnFightRequest", [mapID, funcLineID, tagType, tagID, valueList])
+    if not tagObj:
         return
     
-    DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick)
-    return
-
-def SendToGameServer_TurnFight(curPlayer, msgType, dataMsg=""):
-    playerID = curPlayer.GetPlayerID()
-    msgList = str([msgType, dataMsg])
-    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "TurnFight", msgList, len(msgList))
-    GameWorld.Log("回合战斗发送GameServer: %s, %s" % (msgType, dataMsg), playerID)
-    return
-
-def GameServer_TurnFight_DoResult(curPlayer, msgData, tick):
+    if GameObj.GetFaction(curNPC) == GameObj.GetFaction(tagObj):
+        # 同阵营直接返回,不处理连击、反击
+        return True
     
-    msgType, dataMsg, ret = msgData
+    curHP = GameObj.GetHP(curNPC)
+    tagHP = GameObj.GetHP(tagObj)
+    tagID = tagObj.GetID()
+    GameWorld.DebugLog("            curID-HP=(%s-%s),tagID-HP=(%s-%s)" % (curID, curHP, tagID, tagHP))
+    if tagHP <= 0 or curHP <= 0:
+        return True
     
-    if not ret:
+    # 反击,反击可打断连击,所以优先判断
+    if CanAtkBack(curNPC, tagObj):
+        DoAttack(tagObj, curNPC, tick, ChConfig.TurnBattleType_AtkBack)
+        return True
+    
+    # 连击
+    if CanCombo(curNPC, tagObj):
+        DoAttack(curNPC, tagObj, tick, ChConfig.TurnBattleType_Combo, useSkill)
+        
+    return True
+
+def CanAtkBack(atkObj, defObj):
+    ## 可否反击
+    posNum = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo) % 100
+    if posNum <= 0:
+        GameWorld.DebugLog("            被非主战单位攻击时无法触发反击: atkID=%s,posNum=%s" % (atkObj.GetID(), posNum))
+        return False
+    defAtkBackRate = GameObj.GetAtkBackRate(defObj) # 防方反击率
+    atkBackNum = defObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnAtkBackNum) # 已反击次数
+    if atkBackNum > 10:
+        # 内置最高反击数防范
+        return False
+    if atkBackNum > 0:
+        validPerList = IpyGameDataPY.GetFuncEvalCfg("TurnFight", 4)
+        vaildPer = validPerList[atkBackNum - 1] if len(validPerList) >= atkBackNum else 0
+        defAtkBackRate = int(defAtkBackRate * vaildPer / 100.0)
+        
+    atkAtkBackDefRate = GameObj.GetAtkBackDefRate(atkObj) # 攻方抵抗反击率
+    atkBackRate = max(0, defAtkBackRate - atkAtkBackDefRate)
+    if atkBackRate <= 0 or not GameWorld.CanHappen(atkBackRate):
+        GameWorld.DebugLog("            无法反击: defID=%s,atkBackNum=%s,atkBackRate=%s=(defAtkBackRate=%s - atkAtkBackDefRate=%s)" 
+                           % (defObj.GetID(), atkBackNum, atkBackRate, defAtkBackRate, atkAtkBackDefRate))
+        return False
+    GameWorld.DebugLog("            可以反击: defID=%s,atkBackNum=%s,atkBackRate=%s=(defAtkBackRate=%s - atkAtkBackDefRate=%s)" 
+                       % (defObj.GetID(), atkBackNum, atkBackRate, defAtkBackRate, atkAtkBackDefRate))
+    return True
+
+def CanCombo(atkObj, defObj):
+    ## 可否连击
+    
+    atkComboRate = GameObj.GetComboRate(atkObj) # 攻方连击率
+    comboNum = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnComboNum) # 已连击次数
+    if comboNum > 10:
+        # 内置最高连击数防范
+        return False
+    if comboNum > 0:
+        validPerList = IpyGameDataPY.GetFuncEvalCfg("TurnFight", 3)
+        vaildPer = validPerList[comboNum - 1] if len(validPerList) >= comboNum else 0
+        atkComboRate = int(atkComboRate * vaildPer / 100.0)
+        
+    defComboReduce = GameObj.GetComboDefRate(defObj) # 防方抵抗连击率
+    comboRate = max(0, atkComboRate - defComboReduce)
+    if comboRate <= 0 or not GameWorld.CanHappen(comboRate):
+        GameWorld.DebugLog("            无法连击: atkID=%s,comboNum=%s,comboRate=%s=(atkComboRate=%s - defComboReduce=%s)" 
+                           % (atkObj.GetID(), comboNum, comboRate, atkComboRate, defComboReduce))
+        return False
+    GameWorld.DebugLog("            可以连击: atkID=%s,comboNum=%s,comboRate=%s=(atkComboRate=%s - defComboReduce=%s)" 
+                       % (atkObj.GetID(), comboNum, comboRate, atkComboRate, defComboReduce))
+    return True
+
+def DoNPCUseSkill(curNPC, useSkill, tick):
+    '''NPC使用技能
+    @return: 是否成功, tagObj
+    '''
+    if not useSkill or useSkill.GetSkillTypeID() == 0:
+        return False, None
+    
+    #检查是否几率触发
+    rate = useSkill.GetHappenRate()
+    if rate != ChConfig.Def_MaxRateValue and not GameWorld.CanHappen(rate, ChConfig.Def_MaxRateValue):
+        #GameWorld.Log('检查是否几率触发 = %s失败 = %s'%(rate, useSkill.GetSkillName()))
+        return False, None
+    
+    tagObj = None
+    skillTag = SkillShell.GetSkillAffectTag(useSkill)
+    skillAim = SkillShell.GetSkillFireAim(useSkill)
+    
+    # 注: 多V多的情况友好目标仅针对本阵容、敌对目标可针对任意敌对阵容
+    
+    #---对自己释放,或者无目标技能---
+    if skillTag in ChConfig.Def_ST_CanNPCUseSkill or skillAim == ChConfig.Def_UseSkillAim_None:
+        #释放自身类技能
+        tagObj = curNPC
+        isOK = SkillShell.DoLogic_UseSkill(curNPC, tagObj, useSkill, tick)
+        return isOK, tagObj
+    
+    # 召唤兽对主人释放技能
+    elif skillTag == ChConfig.Def_UseSkillTag_SummonMaster:
+        if not NPCCommon.IsSummonNPC(curNPC):
+            GameWorld.ErrLog("该NPC非召唤兽,无法获得主人释放技能")
+            return False, None
+        
+        curSummonOwner = NPCCommon.GetSummonNPCOwner(IPY_GameWorld.gotPlayer, curNPC)    
+        if curSummonOwner == None:
+            curSummonOwner = NPCCommon.GetSummonNPCOwner(IPY_GameWorld.gotNPC, curNPC)
+            
+        if curSummonOwner == None:
+            GameWorld.ErrLog("召唤兽(%s)对主人释放技能,找不到主人" % curNPC.GetNPCID())
+            return False, None
+        
+        tagObj = curSummonOwner
+        
+    # 友好死亡目标,一般是复活技能
+    elif skillTag == ChConfig.Def_UseSkillTag_FriendDeath:
+        deathList = GetFriendDeathList(curNPC)
+        if not deathList:
+            return False, None
+        tagObj = random.choice(deathList) # 随机选一个
+        
+    # 友好目标
+    elif skillTag in ChConfig.Def_ST_CanNPCUseSkillFriend:
+        friendList = GetFriendObjList(curNPC)
+        if not friendList:
+            return False, None
+        tagObj = random.choice(friendList) # 随机选一个,可扩展其他选择规则
+        
+    # 敌方目标
+    else:
+        tagObj = GetEnemyObj(curNPC)
+        
+    if not tagObj:
+        return False, None
+    
+    GameWorld.DebugLog("    技能释放: skillID=%s,atkID=%s,def=%s,HP:%s/%s" 
+                       % (useSkill.GetSkillID(), curNPC.GetID(), GetObjName(tagObj), GameObj.GetHP(tagObj), GameObj.GetMaxHP(tagObj)))
+    isOK = SkillShell.DoLogic_UseSkill(curNPC, tagObj, useSkill, tick)
+    return isOK, tagObj
+
+def GetFriendDeathList(curNPC):
+    ## 获取友方死亡单位,仅针对本阵容主战单位
+    objID = curNPC.GetID()
+    turnFight = GetTurnFightMgr().getNPCTurnFight(objID)
+    if not turnFight:
+        return []
+    batFaction = turnFight.getBatFaction(GameObj.GetFaction(curNPC))
+    num = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo) / 100
+    batLineup = batFaction.getBatlineup(num)
+    deathList = []
+    for posNum, tagNPC in batLineup.npcPosDict.items():
+        if posNum <= 0 or not tagNPC:
+            continue
+        if GameObj.GetHP(tagNPC) > 0:
+            continue
+        deathList.append(tagNPC)
+        
+    return deathList
+
+def GetFriendObjList(curNPC):
+    ## 获取友方单位,仅针对本阵容主战单位
+    objID = curNPC.GetID()
+    turnFight = GetTurnFightMgr().getNPCTurnFight(objID)
+    if not turnFight:
+        return []
+    batFaction = turnFight.getBatFaction(GameObj.GetFaction(curNPC))
+    num = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo) / 100
+    batLineup = batFaction.getBatlineup(num)
+    friendList = []
+    for posNum, tagNPC in batLineup.npcPosDict.items():
+        if posNum <= 0 or not tagNPC:
+            continue
+        if GameObj.GetHP(tagNPC) <= 0:
+            continue
+        friendList.append(tagNPC)
+    return friendList
+
+def GetEnemyObj(curNPC, ruleType=0):
+    ## 获取一个敌对单位,针对所有敌对阵容主战单位,优先选择对位阵容单位,仅限活着的单位
+    # @param ruleType: 选择规则,默认0任意,1-最低血量;2-最高血量
+    objID = curNPC.GetID()
+    faction = GameObj.GetFaction(curNPC)
+    posInfo = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo)
+    num = posInfo / 100 # 阵容编号
+    posNum = posInfo % 100 # 所在阵容站位
+    turnFight = GetTurnFightMgr().getNPCTurnFight(objID)
+    if not turnFight:
         return
     
-    if msgType == "TurnFightRequest":
-        mapID, funcLineID, tagType, tagID, valueList = dataMsg
-        DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick)
-        
-    elif msgType == "TurnFightOver":
-        mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList = dataMsg
-        FBLogic.OnTurnFightOver_GameServerRet(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList, ret)
-        
-    elif msgType == "TurnFightTagPlayerInfo":
-        mapID, funcLineID, tagType, tagID, valueList = dataMsg
-        DoTrunFightVSPlayer(curPlayer, tagID, [mapID, funcLineID, valueList], ret)
-           
+    tagBatFaction = turnFight.getBatFaction(Def_FactionB if faction == Def_FactionA else Def_FactionA)
+    
+    tagLineupNumList = [num] # 目标阵容选择顺序,优先对位阵容,再其他阵容
+    if num in tagBatFaction.lineupDict:
+        tagLineupNumList = [num]
+    for tagNum in tagBatFaction.lineupDict.keys():
+        if tagNum not in tagLineupNumList:
+            tagLineupNumList.append(tagNum)
+            
+    for tagNum in tagLineupNumList:
+        tagLineup = tagBatFaction.getBatlineup(tagNum)
+        tagPosNumList = [posNum] # 优先对位位置,再其他位置按顺序遍历
+        pNumList = tagLineup.npcPosDict.keys()
+        pNumList.sort()
+        for pNum in pNumList:
+            if pNum > 0 and pNum not in tagPosNumList:
+                tagPosNumList.append(pNum)
+        for pNum in tagPosNumList:
+            tagNPC = tagLineup.npcPosDict.get(pNum)
+            if not tagNPC:
+                continue
+            if GameObj.GetHP(tagNPC) <= 0:
+                continue
+            return tagNPC
     return
 
-def DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick):
-    ## 执行回合制战斗的完整流程
+def SetKilled(gameObj, killer=None):
+    if not gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
+        #GameWorld.DebugLog("非回合战斗主体被击杀: curID=%s" % gameObj.GetID())
+        return
     
-    #if curPlayer.GetSightLevel() != curPlayer.GetID():
-    #    PlayerControl.SetPlayerSightLevel(curPlayer, curPlayer.GetID())
+    GameWorld.DebugLog("        %s 回合战斗主体被击杀: curID=%s" % (GetObjName(gameObj), gameObj.GetID()))
+    GameObj.SetHP(gameObj, 0) # 回合制死亡仅设置为0,实例暂时不回收
+    gameObj.SetVisible(False)
+    
+    turnFight = GetTurnFightMgr().getNPCTurnFight(gameObj.GetID())
+    if turnFight:
+        clientPack = ChPyNetSendPack.tagMCTurnFightObjDead()
+        clientPack.ObjID = gameObj.GetID()
+        turnFight.addBatPack(clientPack)
         
-    SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Start)
+    return True
+
+def OnTurnAllOver(guid):
+    ## 所有回合已经全部执行完毕
+    GameWorld.DebugLog("所有回合结束")
+    turnFight = GetTurnFightMgr().getTurnFight(guid)
+    if not turnFight:
+        return
     
-    tagPlayerInfo = {}
-    if tagType == ChConfig.TurnBattle_TagType_Player and tagID >= 10000 :
-        tagPlayer = GameWorld.GetMapCopyPlayerManager().FindPlayerByID(tagID)
-        if tagPlayer:
-            tagPlayerInfo = __GetPlayerInfo(tagPlayer)
+    if turnFight.winFaction:
+        return
+    
+    if turnFight.playerID:
+        # 玩家发起的,未击杀对方,算玩家输
+        turnFight.winFaction = Def_FactionB
+    else:
+        # 系统场次,按一定规则来,这里先随机
+        turnFight.winFaction = random.choice([Def_FactionA, Def_FactionB])
+        
+    DoTurnFightOver(guid)
+    return
+
+def DoTurnFightOver(guid):
+    ## 执行回合战斗结算逻辑
+    tfMgr = GetTurnFightMgr()
+    turnFight = tfMgr.getTurnFight(guid)
+    if not turnFight:
+        return
+    
+    turnFight.costTime = time.time() - turnFight.startTime
+    winFaction = turnFight.winFaction
+    GameWorld.DebugLog("--- 战斗结束处理 --- %s, winFaction=%s, costTime=%ss" % (guid, winFaction, turnFight.costTime))
+    
+    # 统计明细
+    statInfo = {}
+    for faction in turnFight.factionDict.keys():
+        if str(faction) not in statInfo:
+            statInfo[str(faction)] = {}
+        facStatInfo = statInfo[str(faction)]
+        batFaction = turnFight.getBatFaction(faction)
+        for num in batFaction.lineupDict.keys():
+            if str(num) not in facStatInfo:
+                facStatInfo[str(num)] = {}
+            lineupStatInfo = facStatInfo[str(num)]
+            batLineup = batFaction.getBatlineup(num)
+            hurtStatDict = batLineup.hurtStatDict
+            defStatDict = batLineup.defStatDict
+            cureStatDict = batLineup.cureStatDict
+            GameWorld.DebugLog("阵容明细: faction=%s,num=%s" % (faction, num))
+            for posNum, curNPC in batLineup.npcPosDict.items():
+                objID = curNPC.GetID()
+                npcID = curNPC.GetNPCID()
+                atkHurt = hurtStatDict.get(objID, 0)
+                defHurt = defStatDict.get(objID, 0)
+                cureHP = cureStatDict.get(objID, 0)
+                GameWorld.DebugLog("    Pos:%s ID=%s-%s,,HP=%s/%s, 输出=%s,承伤=%s,治疗=%s" 
+                                   % (posNum, objID, npcID, GameObj.GetHP(curNPC), GameObj.GetMaxHP(curNPC), atkHurt, defHurt, cureHP))
+                lineupStatInfo[str(posNum)] = {"ObjID":objID, "NPCID":npcID, "AtkHurt":atkHurt, "DefHurt":defHurt, "CureHP":cureHP}
+                
+    awardItemList = []
+    playerID = turnFight.playerID
+    if playerID:
+        curPlayer = turnFight.curPlayer
+        isWin = (winFaction == Def_FactionA)
+        # 主线小怪
+        if turnFight.mapID == ChConfig.Def_FBMapID_Main:
+            OnOver_MainLevel(curPlayer, isWin, awardItemList)
+        # 主线boss
+        elif turnFight.mapID == ChConfig.Def_FBMapID_MainBoss:
+            OnOver_MainLevelBoss(curPlayer, isWin, awardItemList)    
         else:
-            SendToGameServer_TurnFight(curPlayer, "TurnFightTagPlayerInfo", [mapID, funcLineID, tagType, tagID, valueList])
-            return
+            pass
         
-    DoTrunFight(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick, tagPlayerInfo)
+    overMsg = {"winFaction":winFaction, "statInfo":statInfo, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(awardItemList)}
+    turnFight.syncState(FightState_Award, overMsg)
     
-    SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Over)
+    # 除了主线外,其余战斗结束后均直接删除
+    if not turnFight.playerID:
+        tfMgr.delTurnFight(guid)
     return
 
-def __GetPlayerInfo(curPlayer):
-    infoDict = {
-                "Name":curPlayer.GetPlayerName(),
-                "Job":curPlayer.GetJob(),
-                "LV":curPlayer.GetLV(),
-                "RealmLV":curPlayer.GetOfficialRank(),
-                "MaxHP":GameObj.GetMaxHP(curPlayer),
-                "FightPower":PlayerControl.GetFightPower(curPlayer),
-                }
-    return infoDict
-
-def DoTrunFightVSPlayer(curPlayer, tagPlayerID, callData, tagPlayerInfo):
-    tagType = ChConfig.TurnBattle_TagType_Player
-    tagID = tagPlayerID
-    mapID, funcLineID, valueList = callData
-    if tagPlayerInfo and curPlayer.GetPlayerID() != tagPlayerID:
-        tick = GameWorld.GetGameWorld().GetTick()
-        DoTrunFight(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick, tagPlayerInfo)
-    SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Over)
-    return
-
-def DoTrunFight(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick, tagInfo=None):
-    if not tagID:
-        return
-    if not tagInfo:
-        tagInfo = {}
-    playerID = curPlayer.GetPlayerID()
-    GameWorld.DebugLog("回合战斗: mapID=%s,funcLineID=%s,tagType=%s,tagID=%s,valueList=%s,tagInfo=%s" 
-                       % (mapID, funcLineID, tagType, tagID, valueList, tagInfo), playerID)
-    
-    factionSyncInfoA = __GetPlayerInfo(curPlayer)
-    factionSyncInfoB = tagInfo
-    SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_PrepareOK, msg=[factionSyncInfoA, factionSyncInfoB])
-    
-    turnNum, turnMax = 1, 15
-    
-    curFightPower = PlayerControl.GetFightPower(curPlayer)
-    tagFightPower = tagInfo.get("FightPower", 0)
-    isWin = 1 if curFightPower >= tagFightPower else 0
-    GameWorld.DebugLog("    战斗结果: isWin=%s,curFightPower=%s,tagFightPower=%s" % (isWin, curFightPower, tagFightPower), playerID)
-    
-    factionTotalHurtDict = {}
-    
-    playbackID = 0 # 战斗回放ID,可根据该ID查看回放
-    
-    # 战斗结束后处理
-    fightRet = [isWin, turnNum, turnMax, factionTotalHurtDict, playbackID]
-    
-    needSendGameServer, awardItemList, overInfoEx = False, [], {}
-    overRet = FBLogic.OnTurnFightOver(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet)
-    if overRet != None:
-        needSendGameServer, awardItemList, overInfoEx = overRet
-        
-    if needSendGameServer or mapID in ChConfig.Def_TFMapID_SendToGameServer:
-        SendToGameServer_TurnFight(curPlayer, "TurnFightOver", [mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList])
-        
-    overMsg = {"isWin":isWin, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(awardItemList), "totalHurt":0}
-    playbackID and overMsg.update({"playbackID":playbackID})
-    overInfoEx and overMsg.update(overInfoEx)
-    SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Award, turnNum, turnMax, overMsg)
-    return
-
-def SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, state, turnNum=0, turnMax=0, msg=""):
+def OnOver_MainLevel(curPlayer, isWin, awardItemList):
+    ## 战斗结束额外处理 - 主线关卡
     if not curPlayer:
         return
-    clientPack = ChPyNetSendPack.tagMCTurnFightState()
-    clientPack.Clear()
-    clientPack.MapID = mapID
-    clientPack.FuncLineID = funcLineID
-    clientPack.TagType = tagType
-    clientPack.TagID = tagID
-    clientPack.State = state
-    clientPack.TurnNum = turnNum
-    clientPack.TurnMax = turnMax
-    clientPack.Msg = str(msg)
-    clientPack.Len = len(clientPack.Msg)    
-    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    playerID = curPlayer.GetPlayerID()
+    mainFightMgr = GetMainFightMgr(curPlayer)
+    mainFightMgr.nextTeam = False
+    chapterID, levelNum, wave = PlayerControl.GetMainLevelNowInfo(curPlayer)
+    
+    if not isWin:
+        nextWave = max(1, wave - 1)
+        nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, nextWave)
+        GameWorld.DebugLog("主线小怪战斗失败,降一波! chapterID=%s,levelNum=%s,wave=%s,nextWave=%s,nowValue=%s" 
+                           % (chapterID, levelNum, wave, nextWave, nowValue), playerID)
+        return
+    
+    if mainFightMgr.teamNum < mainFightMgr.teamMax:
+        mainFightMgr.nextTeam = True
+        GameWorld.DebugLog("主线小怪战斗胜利,下一小队! chapterID=%s,levelNum=%s,wave=%s,teamNum=%s/%s" 
+                           % (chapterID, levelNum, wave, mainFightMgr.teamNum, mainFightMgr.teamMax), playerID)
+        return
+    
+    # 获胜过波
+    if wave < mainFightMgr.waveMax:
+        nextWave = min(mainFightMgr.waveMax, wave + 1)
+        nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, nextWave)
+        GameWorld.DebugLog("主线小怪波战斗胜利,下一波! chapterID=%s,levelNum=%s,wave=%s,nextWave=%s,nowValue=%s" 
+                           % (chapterID, levelNum, wave, nextWave, nowValue), playerID)
+    else:
+        GameWorld.DebugLog("主线小怪波战斗胜利,最后一波循环刷! chapterID=%s,levelNum=%s,wave=%s" % (chapterID, levelNum, wave), playerID)
+        
+    # 小怪可能会退波,所以只在有超过已过关卡进度时才更新值
+    hisPassValue = PlayerControl.GetMainLevelPassValue(curPlayer)
+    curPassValue = PlayerControl.ComMainLevelValue(chapterID, levelNum, wave)
+    if curPassValue > hisPassValue:
+        GameWorld.DebugLog("更新当前过关进度! curPassValue=%s,hisPassValue=%s" % (curPassValue, hisPassValue), playerID)
+        PlayerControl.SetMainLevelPassValue(curPlayer, curPassValue)
+    else:
+        GameWorld.DebugLog("未超过当前过关进度,不更新! curPassValue=%s <= hisPassValue=%s" % (curPassValue, hisPassValue), playerID)
+        
+    return
+
+def OnOver_MainLevelBoss(curPlayer, isWin, awardItemList):
+    ## 战斗结束额外处理 - 主线关卡boss
+    if not curPlayer:
+        return
+    playerID = curPlayer.GetPlayerID()
+    mainFightMgr = GetMainFightMgr(curPlayer)
+    mainFightMgr.nextTeam = False
+    chapterID, levelNum, _ = PlayerControl.GetMainLevelPassInfo(curPlayer)
+    if not isWin:
+        nowValue = PlayerControl.GetMainLevelNowValue(curPlayer)
+        GameWorld.DebugLog("主线boss战斗失败!保持当前刷怪波进度不变! nowValue=%s" % nowValue, playerID)
+        return
+    
+    if mainFightMgr.teamNum < mainFightMgr.teamMax:
+        mainFightMgr.nextTeam = True
+        GameWorld.DebugLog("主线boss小队战斗胜利,下一小队! chapterID=%s,levelNum=%s,teamNum=%s/%s" 
+                           % (chapterID, levelNum, mainFightMgr.teamNum, mainFightMgr.teamMax), playerID)
+        return
+    
+    isAllPass = False # 是否通关
+    if IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID, levelNum + 1):
+        nextChapterID, nextLevelNum = chapterID, levelNum + 1
+        GameWorld.DebugLog("主线boss波战斗胜利!下一关! chapterID=%s,levelNum=%s,nextLevelNum=%s" 
+                           % (chapterID, levelNum, nextLevelNum), playerID)
+    elif IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID + 1, 1):
+        nextChapterID, nextLevelNum = chapterID + 1, 1
+        GameWorld.DebugLog("主线boss波战斗胜利!下一章! chapterID=%s,levelNum=%s,nextChapterID=%s,nextLevelNum=%s" 
+                           % (chapterID, levelNum, nextChapterID, nextLevelNum), playerID)
+    else:
+        # 已通关的暂时先保持不变
+        # 注意防范最后一关奖励重复获得
+        nextChapterID, nextLevelNum = chapterID + 1, 1
+        GameWorld.DebugLog("主线boss波战斗胜利!已通关! chapterID=%s,levelNum=%s,nextChapterID=%s,nextLevelNum=%s" 
+                            % (chapterID, levelNum, nextChapterID, nextLevelNum), playerID)
+        isAllPass = True
+        
+    updPassValue = PlayerControl.SetMainLevelPassInfo(curPlayer, nextChapterID, nextLevelNum, 0)
+    if isAllPass:
+        # 已通关的刷怪进度保持不变
+        updNowValue = PlayerControl.GetMainLevelNowValue(curPlayer)
+        GameWorld.DebugLog("已通关的刷怪进度保持不变: updNowValue=%s" % updNowValue, playerID)
+    else:
+        updNowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, nextChapterID, nextLevelNum, 1)
+        GameWorld.DebugLog("为通关的刷怪进度设置为下一关的第1波: updNowValue=%s" % updNowValue, playerID)
+    GameWorld.DebugLog("updPassValue=%s,updNowValue=%s" % (updPassValue, updNowValue), playerID)
+    
+    # 发放过关奖励
+    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
+    if not levelIpyData:
+        return
+    itemList = levelIpyData.GetAwardItemList()
+    GameWorld.DebugLog("过关奖励: chapterID=%s,levelNum=%s,itemList=%s" % (chapterID, levelNum, itemList), playerID)
+    
+    ItemControler.GivePlayerItemOrMail(curPlayer, itemList, event=["MainLevelBoss", False, {}], isNotifyAward=False)
+    
+    awardItemList += itemList
+    return
+
+
+#// B4 14 查看战报 #tagCSTurnFightReportView
+#
+#struct    tagCSTurnFightReportView
+#{
+#    tagHead        Head;
+#    char        GUID[40];    //战报guid
+#};
+def OnTurnFightReportView(index, clientData, tick):
     return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index f32577c..df217a5 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1192,9 +1192,6 @@
 #朋友
 Type_Relation_Friend = 2
 
-Def_NpcDictKey_CampType = 'CampType'
-#阵营 三种 中立 正义 邪恶
-
 CampType_Neutral = ShareDefine.CampType_Neutral  # 中立
 CampType_Justice = ShareDefine.CampType_Justice  # 正义
 CampType_Evil = ShareDefine.CampType_Evil  # 邪恶
@@ -1238,7 +1235,7 @@
 ) = range(Def_UseSkillAim_Type)
 
 #技能施法目标类型, 修改此处应在Def_Dict_UseSkillTag_ObjType 相应更改
-Def_UseSkillTag_Type = 16
+Def_UseSkillTag_Type = 17
 (
     Def_UseSkillTag_None      ,        #无需选择对象 0
     Def_UseSkillTag_Self      ,        #自己 1 
@@ -1256,6 +1253,7 @@
     Def_UseSkillTag_CanAttackBaseNPC,      #可攻击的野外小怪(含精英)怪物 13
     Def_UseSkillTag_FriendNPC,         #友好NPC 14
     Def_UseSkillTag_AppointNPC,         #指定NPC 15 必须和效果值配合 Def_Skill_Effect_AppointNPC
+    Def_UseSkillTag_FriendDeath,        #友方死亡目标16
     
 ) = range( 0, Def_UseSkillTag_Type )
 
@@ -1294,6 +1292,13 @@
 Def_ST_CanNPCUseSkill = [ 
                         Def_UseSkillTag_None,
                         Def_UseSkillTag_Self, 
+                        ]
+
+#NPC技能:可以对友好目标释放的技能
+Def_ST_CanNPCUseSkillFriend = [ 
+                        Def_UseSkillTag_Friend, 
+                        Def_UseSkillTag_SelfAndFriend, 
+                        Def_UseSkillTag_FriendNPC, 
                         ]
 
 #自动攻击技能类型
@@ -1343,7 +1348,7 @@
                        Def_SkillType_PlsBuff    : IPY_GameWorld.bfBuff   ,  #增益BUFF 5
                        Def_SkillType_DepBuff    : IPY_GameWorld.bfDeBuff ,  #减益BUFF 6
                        Def_SkillType_PassiveBuff    : IPY_GameWorld.btPassiveBuf,  # 被动技能 7 (废弃,无此定义分散为其他buff)
-                       Def_SkillType_Revive     : IPY_GameWorld.bfIncBuff,  #复活     8
+                       #Def_SkillType_Revive     : IPY_GameWorld.bfIncBuff,  #复活     8
                        Def_SkillType_Increment  : IPY_GameWorld.bfIncBuff,  #增值技能(不可清除)9
                        Def_SkillType_Aura       : IPY_GameWorld.bfAura   ,  #光环技能  10
                        Def_SkillType_Equip      : IPY_GameWorld.bfEquipBuff,#装备技能  11
@@ -1733,6 +1738,11 @@
 Def_NstNull, Def_NstMoving, Def_NstDead, Def_NstAttack = range(4)
 
 #-------------------------------#副本相关#------------------------
+# 主线小怪
+Def_FBMapID_Main = 1
+# 主线Boss
+Def_FBMapID_MainBoss = 2
+
 #创角新手村地图ID列表
 Def_CreatRoleMapIDList = [10000]
 #PK周赛
@@ -1850,9 +1860,6 @@
 TurnFightMapIDList = (
 Def_TFMapID_MineArea, # 福地 1
 ) = range(1, 1 + 1)
-
-#回合战斗自定义地图需要发送GameServer的列表
-Def_TFMapID_SendToGameServer = [Def_TFMapID_MineArea]
 
 #前端自定义场景地图
 ClientCustomSceneList = [Def_FBMapID_PersonalBoss, Def_FBMapID_ArenaBattle, Def_FBMapID_MirrorBattle]
@@ -2966,7 +2973,7 @@
 
 # 回合攻击战斗类型
 (
-TurnBattleType_Normal, # 普通
+TurnBattleType_Normal, # 常规攻击
 TurnBattleType_Combo, # 连击
 TurnBattleType_AtkBack, # 反击
 ) = range(3)
@@ -2974,9 +2981,20 @@
 Def_PerTurnTick = 1000 # 每回合等同于常规tick时长
 
 # 回合战斗目标类型
-TurnBattle_TagType_NPC = 0
+TurnBattle_TagType_NPCLineup = 0 # NPC阵容
 TurnBattle_TagType_Player = 1
 TurnBattle_TagType_Team = 2
+
+#---Obj字典-------
+Def_Obj_Dict_Faction = 'Faction' # 所属阵营
+Def_Obj_Dict_LineupPlayerID = 'LineupPlayerID' # 阵容所属玩家ID,可用于判断是否玩家阵容,PVP或PVE
+Def_Obj_Dict_TurnFightPosInfo = 'TurnFightPosInfo' # 回合制站位: 阵营编号*100+阵型站位,阵型站位为0时代表非主战单位
+Def_Obj_Dict_TurnFightTimeline = 'TurnFightTimeline' # 回合制战斗时间线: 回合数*100+行动编号节点
+Def_Obj_Dict_TurnComboNum = 'TurnComboNum' # 单次累计连击次数
+Def_Obj_Dict_TurnAtkBackNum = 'TurnAtkBackNum' # 单次累计反击次数
+Def_Obj_Dict_TurnParryNum = 'TurnParryNum' # 单次累计格挡次数
+Def_Obj_Dict_TurnMissNum = 'TurnMissNum' # 单次累计闪避次数
+Def_Obj_Dict_TurnBattleType = 'TurnBattleType' # 本次攻击战斗类型:TurnBattleType_xxx
 
 #---NPC字典-------
 #每道龙卷风最终坐标
@@ -3203,6 +3221,7 @@
 Def_PlayerKey_AttrPerLVAtk = "PerLVAtk"    #每1级+%s攻击, 数值取万分率,支持小数算法
 Def_PlayerKey_AttrPerLVMaxHP = "PerLVMaxHP"    #每1级+%s生命, 数值为固定值
 Def_PlayerKey_AttrShieldMPCostRate = "AttrShieldMPCostRate"    #魔法盾伤害吸收蓝耗比率
+Def_PlayerKey_AttrXP = "AttrXP"    #当前XP
 Def_PlayerKey_AttrXPRestorePer = "AttrXPRestorePer"    #自动恢复XP值比率
 Def_PlayerKey_MarkLoadMapTick = "LoadMapTickVIP"        #记录切换地图后的tick,VIP
 Def_PlayerKey_MTFreeOnlineRefreshTick = "MTFreeOnlineRTick"        # 寻宝在线计算时间
@@ -4422,6 +4441,10 @@
 #武将
 Def_PDict_HeroSkin = "HeroSkin_%s" # 武将皮肤解锁状态,按皮肤索引二进制存储,参数(武将ID)
 Def_PDict_HeroBook = "HeroBook_%s" # 武将图鉴激活等级,参数(武将ID) cccbbba a-初始激活状态1-英雄激活,2-初始图鉴激活; bbb-存星级图鉴激活等级;ccc-存突破图鉴激活等级
+
+#主线
+Def_PDict_UnXiantaoCnt = "UnXiantaoCnt" # 累计未结算的战锤数
+
 #-------------------------------------------------------------------------------
 #可以从07 41封包购买的背包类型,和对应字典{背包类型:[字典key, 默认格子数]}
 
@@ -4515,6 +4538,7 @@
 AttrName_FightExpRate = "FightExpRate"  # 杀怪经验倍率
 AttrName_GameExpRate = "GameExpRate"  # 游戏事件经验倍率
 AttrName_SkillAtkRate = "SkillAtkRate"  # 技能伤害加成 (用作伤害加成)
+AttrName_AtkBackHP = "AtkBackHP"    # 攻击回复血量固定值
 AttrName_AtkBackHPPer = "AtkBackHPPer"    # 攻击回复血量比率
 AttrName_SuperHit = "SuperHit"  # 暴击伤害固定值
 AttrName_SuperHitRate = "SuperHitRate"  # 暴击概率
@@ -4545,6 +4569,17 @@
 AttrName_PetAtk = "PetAtk"  # 灵宠攻击
 AttrName_PetSkillAtkRate = "PetSkillAtkRate"  # 灵宠技能
 AttrName_PetDamPer = "PetDamPer"  # 灵宠伤害增加
+AttrName_ComboDefRate = "ComboDefRate"  # 抗连击概率
+AttrName_AtkBackRate = "AtkBackRate"  # 反击概率
+AttrName_AtkBackDefRate = "AtkBackDefRate"  # 抗反击概率
+AttrName_SuckHPPer = "SuckHPPer"  # 吸血比率
+AttrName_SuckHPDefPer = "SuckHPDefPer"  # 抗吸血比率
+AttrName_CurePer = "CurePer"  # 强化治疗
+AttrName_CureDefPer = "CureDefPer"  # 弱化治疗
+AttrName_PetStrengthenPer = "PetStrengthenPer"  # 强化灵兽
+AttrName_PetWeakenPer = "PetWeakenPer"  # 弱化灵兽
+AttrName_SuperHitHurtPer = "SuperHitHurtPer"  # 强化暴伤
+AttrName_SuperHitHurtDefPer = "SuperHitHurtDefPer"  # 弱化暴伤
 
 #物品效果(ID或指定类型)对应的属性计算信息 {效果(ID/指定类型):[[属性索引, ...], 是否基础属性,(非)线性]}
 #对应 Def_Calc_AllAttrType_MAX
@@ -4735,6 +4770,22 @@
     ShareDefine.Def_Effect_FamilyWarHPPer:[[TYPE_Calc_FamilyWarHPPer], False, TYPE_Linear],
     ShareDefine.Def_Effect_FamilyWarAtkPer:[[TYPE_Calc_FamilyWarAtkPer], False, TYPE_Linear],
     ShareDefine.Def_Effect_FamilySitExpPer:[[TYPE_Calc_FamilySitExpPer], False, TYPE_Linear],
+    ShareDefine.Def_Effect_ComboDefRate:[[TYPE_Calc_ComboDefRate], False, TYPE_Linear],
+    AttrName_ComboDefRate:[[TYPE_Calc_ComboDefRate], False, TYPE_Linear],
+    ShareDefine.Def_Effect_AtkBackRate:[[TYPE_Calc_AtkBackRate], False, TYPE_Linear],
+    AttrName_AtkBackRate:[[TYPE_Calc_AtkBackRate], False, TYPE_Linear],
+    ShareDefine.Def_Effect_AtkBackDefRate:[[TYPE_Calc_AtkBackDefRate], False, TYPE_Linear],
+    AttrName_AtkBackDefRate:[[TYPE_Calc_AtkBackDefRate], False, TYPE_Linear],
+    ShareDefine.Def_Effect_SuckHPPer:[[TYPE_Calc_SuckHPPer], False, TYPE_Linear],
+    AttrName_SuckHPPer:[[TYPE_Calc_SuckHPPer], False, TYPE_Linear],
+    ShareDefine.Def_Effect_SuckHPDefPer:[[TYPE_Calc_SuckHPDefPer], False, TYPE_Linear],
+    AttrName_SuckHPDefPer:[[TYPE_Calc_SuckHPDefPer], False, TYPE_Linear],
+    ShareDefine.Def_Effect_CurePer:[[TYPE_Calc_CurePer], False, TYPE_Linear],
+    ShareDefine.Def_Effect_CureDefPer:[[TYPE_Calc_CureDefPer], False, TYPE_Linear],
+    ShareDefine.Def_Effect_PetStrengthenPer:[[TYPE_Calc_PetStrengthenPer], False, TYPE_Linear],
+    ShareDefine.Def_Effect_PetWeakenPer:[[TYPE_Calc_PetWeakenPer], False, TYPE_Linear],
+    ShareDefine.Def_Effect_SuperHitHurtPer:[[TYPE_Calc_SuperHitHurtPer], False, TYPE_Linear],
+    ShareDefine.Def_Effect_SuperHitHurtDefPer:[[TYPE_Calc_SuperHitHurtDefPer], False, TYPE_Linear],
     
     #战斗非线性
     ShareDefine.Def_Effect_SuperHitPer:[[TYPE_Calc_SuperHit], False, TYPE_NoLinear],
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py
index 2752f9e..4a062ca 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py
@@ -110,6 +110,7 @@
                   ("ObjID", c_int),    
                   ("AttackType", c_ubyte),    #爆击, miss
                   ("HurtHP", c_int),    
+                  ("HurtHPEx", c_int),    
                   ("CurHP", c_int),    
                   ("CurHPEx", c_int),    
                   ]
@@ -128,6 +129,7 @@
         self.ObjID = 0
         self.AttackType = 0
         self.HurtHP = 0
+        self.HurtHPEx = 0
         self.CurHP = 0
         self.CurHPEx = 0
         return
@@ -144,6 +146,7 @@
                                 ObjID:%d,
                                 AttackType:%d,
                                 HurtHP:%d,
+                                HurtHPEx:%d,
                                 CurHP:%d,
                                 CurHPEx:%d
                                 '''\
@@ -152,6 +155,7 @@
                                 self.ObjID,
                                 self.AttackType,
                                 self.HurtHP,
+                                self.HurtHPEx,
                                 self.CurHP,
                                 self.CurHPEx
                                 )
@@ -845,8 +849,9 @@
                   ("SubCmd", c_ubyte),
                   ("ObjID", c_int),    
                   ("ObjType", c_ubyte),    
-                  ("RefreshType", c_ubyte),    
+                  ("RefreshType", c_ushort),    
                   ("Value", c_int),    
+                  ("ValueEx", c_int),    
                   ]
 
     def __init__(self):
@@ -867,6 +872,7 @@
         self.ObjType = 0
         self.RefreshType = 0
         self.Value = 0
+        self.ValueEx = 0
         return
 
     def GetLength(self):
@@ -882,7 +888,8 @@
                                 ObjID:%d,
                                 ObjType:%d,
                                 RefreshType:%d,
-                                Value:%d
+                                Value:%d,
+                                ValueEx:%d
                                 '''\
                                 %(
                                 self.Cmd,
@@ -890,7 +897,8 @@
                                 self.ObjID,
                                 self.ObjType,
                                 self.RefreshType,
-                                self.Value
+                                self.Value,
+                                self.ValueEx
                                 )
         return DumpString
 
@@ -1105,3 +1113,326 @@
 
 m_NAtagNPCDie=tagNPCDie()
 ChNetPackDict[eval("0x%02x%02x"%(m_NAtagNPCDie.Cmd,m_NAtagNPCDie.SubCmd))] = m_NAtagNPCDie
+
+#04 06 NPC出现#tagNPCAppear
+
+class  tagNPCAppear(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ObjID", c_int),    
+                  ("NPCID", c_int),    
+                  ("NPCHP", c_int),    
+                  ("NPCHPEx", c_int),    #扩展超过20E血量
+                  ("MaxHP", c_int),    #成长型NPC需要通知
+                  ("MaxHPEx", c_int),    #超过20E血量
+                  ("CurLV", c_ushort),    #成长型NPC
+                  ("PosX", c_ushort),    
+                  ("PosY", c_ushort),    
+                  ("Speed", c_ushort),    
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0x04
+        self.SubCmd = 0x06
+        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 = 0x04
+        self.SubCmd = 0x06
+        self.ObjID = 0
+        self.NPCID = 0
+        self.NPCHP = 0
+        self.NPCHPEx = 0
+        self.MaxHP = 0
+        self.MaxHPEx = 0
+        self.CurLV = 0
+        self.PosX = 0
+        self.PosY = 0
+        self.Speed = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagNPCAppear)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''//04 06 NPC出现//tagNPCAppear:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ObjID:%d,
+                                NPCID:%d,
+                                NPCHP:%d,
+                                NPCHPEx:%d,
+                                MaxHP:%d,
+                                MaxHPEx:%d,
+                                CurLV:%d,
+                                PosX:%d,
+                                PosY:%d,
+                                Speed:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ObjID,
+                                self.NPCID,
+                                self.NPCHP,
+                                self.NPCHPEx,
+                                self.MaxHP,
+                                self.MaxHPEx,
+                                self.CurLV,
+                                self.PosX,
+                                self.PosY,
+                                self.Speed
+                                )
+        return DumpString
+
+
+m_NAtagNPCAppear=tagNPCAppear()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagNPCAppear.Cmd,m_NAtagNPCAppear.SubCmd))] = m_NAtagNPCAppear
+
+#04 07 NPC消失#tagNPCDisappear
+
+class  tagNPCDisappear(Structure):
+    Head = tagHead()
+    Count = 0    #(WORD Count)
+    NPCID = list()    #(vector<DWORD> NPCID)//size = Count
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0x04
+        self.Head.SubCmd = 0x07
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        for i in range(self.Count):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.NPCID.append(value)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0x04
+        self.Head.SubCmd = 0x07
+        self.Count = 0
+        self.NPCID = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 2
+        length += 4 * self.Count
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteWORD(data, self.Count)
+        for i in range(self.Count):
+            data = CommFunc.WriteDWORD(data, self.NPCID[i])
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                Count:%d,
+                                NPCID:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.Count,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagNPCDisappear=tagNPCDisappear()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagNPCDisappear.Head.Cmd,m_NAtagNPCDisappear.Head.SubCmd))] = m_NAtagNPCDisappear
+
+#04 23 对象状态刷新通知(只显示)#tagObjPropertyRefreshView
+
+class  tagObjPropertyRefreshView(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ObjID", c_int),    #对象ID
+                  ("ObjType", c_ubyte),    #对象类型
+                  ("SkillID", c_int),    #技能ID
+                  ("DiffValue", c_int),    #值
+                  ("DiffValueEx", c_int),    #超亿值
+                  ("AttackType", c_ubyte),    #攻击类型
+                  ("SrcObjID", c_int),    #飘血来源
+                  ("SrcObjType", c_ubyte),    
+                  ("HP", c_int),    #剩余血量
+                  ("HPEx", c_int),    #剩余血量 亿
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0x04
+        self.SubCmd = 0x23
+        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 = 0x04
+        self.SubCmd = 0x23
+        self.ObjID = 0
+        self.ObjType = 0
+        self.SkillID = 0
+        self.DiffValue = 0
+        self.DiffValueEx = 0
+        self.AttackType = 0
+        self.SrcObjID = 0
+        self.SrcObjType = 0
+        self.HP = 0
+        self.HPEx = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagObjPropertyRefreshView)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''//04 23 对象状态刷新通知(只显示)//tagObjPropertyRefreshView:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ObjID:%d,
+                                ObjType:%d,
+                                SkillID:%d,
+                                DiffValue:%d,
+                                DiffValueEx:%d,
+                                AttackType:%d,
+                                SrcObjID:%d,
+                                SrcObjType:%d,
+                                HP:%d,
+                                HPEx:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ObjID,
+                                self.ObjType,
+                                self.SkillID,
+                                self.DiffValue,
+                                self.DiffValueEx,
+                                self.AttackType,
+                                self.SrcObjID,
+                                self.SrcObjType,
+                                self.HP,
+                                self.HPEx
+                                )
+        return DumpString
+
+
+m_NAtagObjPropertyRefreshView=tagObjPropertyRefreshView()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagObjPropertyRefreshView.Cmd,m_NAtagObjPropertyRefreshView.SubCmd))] = m_NAtagObjPropertyRefreshView
+
+#06 02 普通攻击#tagObjBaseAttack
+
+class  tagObjBaseAttack(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("AttackerID", c_int),    
+                  ("AttackerObjType", c_ubyte),    
+                  ("BattleType", c_ubyte),    #攻击类型 物理/魔法
+                  ("ObjID", c_int),    #对象ID
+                  ("ObjType", c_ubyte),    #对象类型
+                  ("AttackType", c_ubyte),    #普攻, 闪躲, 致命 类型
+                  ("Value", c_int),    
+                  ("ValueEx", c_int),    
+                  ("RemainHP", c_int),    #对方剩余的血
+                  ("RemainHPEx", c_int),    #对方剩余的血, 超亿支持
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0x06
+        self.SubCmd = 0x02
+        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 = 0x02
+        self.AttackerID = 0
+        self.AttackerObjType = 0
+        self.BattleType = 0
+        self.ObjID = 0
+        self.ObjType = 0
+        self.AttackType = 0
+        self.Value = 0
+        self.ValueEx = 0
+        self.RemainHP = 0
+        self.RemainHPEx = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagObjBaseAttack)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''//06 02 普通攻击//tagObjBaseAttack:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                AttackerID:%d,
+                                AttackerObjType:%d,
+                                BattleType:%d,
+                                ObjID:%d,
+                                ObjType:%d,
+                                AttackType:%d,
+                                Value:%d,
+                                ValueEx:%d,
+                                RemainHP:%d,
+                                RemainHPEx:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.AttackerID,
+                                self.AttackerObjType,
+                                self.BattleType,
+                                self.ObjID,
+                                self.ObjType,
+                                self.AttackType,
+                                self.Value,
+                                self.ValueEx,
+                                self.RemainHP,
+                                self.RemainHPEx
+                                )
+        return DumpString
+
+
+m_NAtagObjBaseAttack=tagObjBaseAttack()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagObjBaseAttack.Cmd,m_NAtagObjBaseAttack.SubCmd))] = m_NAtagObjBaseAttack
\ No newline at end of file
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
index 324f86d..4983cd7 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
@@ -19056,9 +19056,9 @@
 
 
 #------------------------------------------------------
-# B4 12 战斗阵型保存 #tagCSHeroBattlePosSave
+# B4 12 战斗阵容保存 #tagCSHeroLineupSave
 
-class  tagCSHeroBattlePos(Structure):
+class  tagCSHeroLineupPos(Structure):
     _pack_ = 1
     _fields_ = [
                   ("ItemIndex", c_ushort),    #武将物品所在武将背包位置索引
@@ -19080,13 +19080,13 @@
         return
 
     def GetLength(self):
-        return sizeof(tagCSHeroBattlePos)
+        return sizeof(tagCSHeroLineupPos)
 
     def GetBuffer(self):
         return string_at(addressof(self), self.GetLength())
 
     def OutputString(self):
-        DumpString = '''// B4 12 战斗阵型保存 //tagCSHeroBattlePosSave:
+        DumpString = '''// B4 12 战斗阵容保存 //tagCSHeroLineupSave:
                                 ItemIndex:%d,
                                 PosNum:%d
                                 '''\
@@ -19097,11 +19097,12 @@
         return DumpString
 
 
-class  tagCSHeroBattlePosSave(Structure):
+class  tagCSHeroLineupSave(Structure):
     Head = tagHead()
-    FuncType = 0    #(BYTE FuncType)//布阵功能类型:0-默认主阵型;其他待扩展,如某个活动的防守阵型
+    LineupID = 0    #(BYTE LineupID)//阵容ID:1-主阵容;其他待扩展,如某个防守阵容
+    ShapeType = 0    #(BYTE ShapeType)//本阵容阵型,0为默认阵型,可扩展不同的阵型
     PosCnt = 0    #(BYTE PosCnt)
-    HeroPosList = list()    #(vector<tagCSHeroBattlePos> HeroPosList)// 保存的阵型,只要发送最终的阵型武将位置即可
+    HeroPosList = list()    #(vector<tagCSHeroLineupPos> HeroPosList)// 保存的阵容,只发送最终的阵容武将位置即可
     data = None
 
     def __init__(self):
@@ -19113,10 +19114,11 @@
     def ReadData(self, _lpData, _pos=0, _Len=0):
         self.Clear()
         _pos = self.Head.ReadData(_lpData, _pos)
-        self.FuncType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.LineupID,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ShapeType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
         self.PosCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
         for i in range(self.PosCnt):
-            temHeroPosList = tagCSHeroBattlePos()
+            temHeroPosList = tagCSHeroLineupPos()
             _pos = temHeroPosList.ReadData(_lpData, _pos)
             self.HeroPosList.append(temHeroPosList)
         return _pos
@@ -19126,7 +19128,8 @@
         self.Head.Clear()
         self.Head.Cmd = 0xB4
         self.Head.SubCmd = 0x12
-        self.FuncType = 0
+        self.LineupID = 0
+        self.ShapeType = 0
         self.PosCnt = 0
         self.HeroPosList = list()
         return
@@ -19134,6 +19137,7 @@
     def GetLength(self):
         length = 0
         length += self.Head.GetLength()
+        length += 1
         length += 1
         length += 1
         for i in range(self.PosCnt):
@@ -19144,7 +19148,8 @@
     def GetBuffer(self):
         data = ''
         data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
-        data = CommFunc.WriteBYTE(data, self.FuncType)
+        data = CommFunc.WriteBYTE(data, self.LineupID)
+        data = CommFunc.WriteBYTE(data, self.ShapeType)
         data = CommFunc.WriteBYTE(data, self.PosCnt)
         for i in range(self.PosCnt):
             data = CommFunc.WriteString(data, self.HeroPosList[i].GetLength(), self.HeroPosList[i].GetBuffer())
@@ -19153,21 +19158,79 @@
     def OutputString(self):
         DumpString = '''
                                 Head:%s,
-                                FuncType:%d,
+                                LineupID:%d,
+                                ShapeType:%d,
                                 PosCnt:%d,
                                 HeroPosList:%s
                                 '''\
                                 %(
                                 self.Head.OutputString(),
-                                self.FuncType,
+                                self.LineupID,
+                                self.ShapeType,
                                 self.PosCnt,
                                 "..."
                                 )
         return DumpString
 
 
-m_NAtagCSHeroBattlePosSave=tagCSHeroBattlePosSave()
-ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCSHeroBattlePosSave.Head.Cmd,m_NAtagCSHeroBattlePosSave.Head.SubCmd))] = m_NAtagCSHeroBattlePosSave
+m_NAtagCSHeroLineupSave=tagCSHeroLineupSave()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCSHeroLineupSave.Head.Cmd,m_NAtagCSHeroLineupSave.Head.SubCmd))] = m_NAtagCSHeroLineupSave
+
+
+#------------------------------------------------------
+# B4 13 主线战斗请求 #tagCSMainFightReq
+
+class  tagCSMainFightReq(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ReqType", c_ubyte),    # 0-停止战斗回城;1-设置消耗倍值;2-挑战小怪;3-挑战boss;4-下一段战报;5-下一队;
+                  ("ReqValue", c_int),    # 请求值,ReqType为1时发送消耗倍值
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xB4
+        self.SubCmd = 0x13
+        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 = 0xB4
+        self.SubCmd = 0x13
+        self.ReqType = 0
+        self.ReqValue = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCSMainFightReq)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// B4 13 主线战斗请求 //tagCSMainFightReq:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ReqType:%d,
+                                ReqValue:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ReqType,
+                                self.ReqValue
+                                )
+        return DumpString
+
+
+m_NAtagCSMainFightReq=tagCSMainFightReq()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCSMainFightReq.Cmd,m_NAtagCSMainFightReq.SubCmd))] = m_NAtagCSMainFightReq
 
 
 #------------------------------------------------------
@@ -20267,10 +20330,10 @@
 
 class  tagCMTurnFight(Structure):
     Head = tagHead()
-    MapID = 0    #(DWORD MapID)// 自定义地图ID,可用于绑定战斗场景功能(如野外关卡,爬塔功能,竞技场等)
-    FuncLineID = 0    #(WORD FuncLineID)
-    TagType = 0    #(BYTE TagType)// 战斗目标类型,0-NPC,1-玩家,2-队伍
-    TagID = 0    #(DWORD TagID)// 战斗目标类型对应的ID
+    MapID = 0    #(DWORD MapID)// 自定义地图ID,可用于绑定战斗地图场景功能(如主线关卡、主线boss、爬塔、竞技场等)
+    FuncLineID = 0    #(DWORD FuncLineID)// MapID对应的扩展值,如具体某个关卡等
+    TagType = 0    #(BYTE TagType)// 目标类型,0-NPC阵容,1-玩家
+    TagID = 0    #(DWORD TagID)// 目标类型对应的ID,如阵容ID或玩家ID
     ValueCount = 0    #(BYTE ValueCount)
     ValueList = list()    #(vector<DWORD> ValueList)// 附加值列表,可选,具体含义由MapID决定
     data = None
@@ -20285,7 +20348,7 @@
         self.Clear()
         _pos = self.Head.ReadData(_lpData, _pos)
         self.MapID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
-        self.FuncLineID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.FuncLineID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
         self.TagType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
         self.TagID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
         self.ValueCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
@@ -20311,7 +20374,7 @@
         length = 0
         length += self.Head.GetLength()
         length += 4
-        length += 2
+        length += 4
         length += 1
         length += 4
         length += 1
@@ -20323,7 +20386,7 @@
         data = ''
         data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
         data = CommFunc.WriteDWORD(data, self.MapID)
-        data = CommFunc.WriteWORD(data, self.FuncLineID)
+        data = CommFunc.WriteDWORD(data, self.FuncLineID)
         data = CommFunc.WriteBYTE(data, self.TagType)
         data = CommFunc.WriteDWORD(data, self.TagID)
         data = CommFunc.WriteBYTE(data, self.ValueCount)
@@ -20358,6 +20421,63 @@
 
 
 #------------------------------------------------------
+# B4 14 查看战报 #tagCSTurnFightReportView
+
+class  tagCSTurnFightReportView(Structure):
+    Head = tagHead()
+    GUID = ""    #(char GUID[40])//战报guid
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xB4
+        self.Head.SubCmd = 0x14
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.GUID,_pos = CommFunc.ReadString(_lpData, _pos,40)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xB4
+        self.Head.SubCmd = 0x14
+        self.GUID = ""
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 40
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteString(data, 40, self.GUID)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                GUID:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.GUID
+                                )
+        return DumpString
+
+
+m_NAtagCSTurnFightReportView=tagCSTurnFightReportView()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCSTurnFightReportView.Head.Cmd,m_NAtagCSTurnFightReportView.Head.SubCmd))] = m_NAtagCSTurnFightReportView
+
+
+#------------------------------------------------------
 # B5 18 拍卖行修改关注物品 #tagCGAttentionAuctionItemChange
 
 class  tagCGAttentionAuctionItemChange(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
index bad86e4..6908bca 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -50727,17 +50727,613 @@
 
 
 #------------------------------------------------------
+# B4 24 回合战斗初始化 #tagSCTurnFightInit
+
+class  tagSCTurnFightObj(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("ObjID", c_int),    # 实例唯一ID
+                  ("NPCID", c_int),    # 绑定的NPCID,不同的实例ID对应的NPCID可能一样
+                  ("HP", c_int),    # 当前血量,求余20亿部分
+                  ("HPEx", c_int),    # 当前血量,整除20亿部分
+                  ("MaxHP", c_int),    # 最大血量,求余20亿部分
+                  ("MaxHPEx", c_int),    # 最大血量,整除20亿部分
+                  ("LV", c_ushort),    # 等级
+                  ("PosNum", c_ubyte),    # 在本阵容中的站位,从1开始,非主战斗武将为0,如红颜
+                  ("AngreXP", c_ushort),    # 当前怒气值
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.ObjID = 0
+        self.NPCID = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.MaxHP = 0
+        self.MaxHPEx = 0
+        self.LV = 0
+        self.PosNum = 0
+        self.AngreXP = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagSCTurnFightObj)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// B4 24 回合战斗初始化 //tagSCTurnFightInit:
+                                ObjID:%d,
+                                NPCID:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                MaxHP:%d,
+                                MaxHPEx:%d,
+                                LV:%d,
+                                PosNum:%d,
+                                AngreXP:%d
+                                '''\
+                                %(
+                                self.ObjID,
+                                self.NPCID,
+                                self.HP,
+                                self.HPEx,
+                                self.MaxHP,
+                                self.MaxHPEx,
+                                self.LV,
+                                self.PosNum,
+                                self.AngreXP
+                                )
+        return DumpString
+
+
+class  tagSCTurnFightLineup(Structure):
+    Num = 0    #(BYTE Num)// 该阵容在本阵营的编号,不同阵营的阵容编号可能相同,都是从1开始,一般1V1时每个阵营为1个阵容,多V多时则每个阵营为多个阵容
+    OwnerID = 0    #(DWORD OwnerID)// 阵容所属的玩家ID,可能为0,0代表非玩家阵容
+    ShapeType = 0    #(BYTE ShapeType)// 本阵容阵型,0为默认阵型,可扩展不同的阵型,如boss特殊战斗阵型,或者其他不同站位的阵型
+    ObjCnt = 0    #(BYTE ObjCnt)
+    ObjList = list()    #(vector<tagSCTurnFightObj> ObjList)// 本阵容战斗单位列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.Num,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.OwnerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.ShapeType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ObjCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.ObjCnt):
+            temObjList = tagSCTurnFightObj()
+            _pos = temObjList.ReadData(_lpData, _pos)
+            self.ObjList.append(temObjList)
+        return _pos
+
+    def Clear(self):
+        self.Num = 0
+        self.OwnerID = 0
+        self.ShapeType = 0
+        self.ObjCnt = 0
+        self.ObjList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 1
+        length += 4
+        length += 1
+        length += 1
+        for i in range(self.ObjCnt):
+            length += self.ObjList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteBYTE(data, self.Num)
+        data = CommFunc.WriteDWORD(data, self.OwnerID)
+        data = CommFunc.WriteBYTE(data, self.ShapeType)
+        data = CommFunc.WriteBYTE(data, self.ObjCnt)
+        for i in range(self.ObjCnt):
+            data = CommFunc.WriteString(data, self.ObjList[i].GetLength(), self.ObjList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Num:%d,
+                                OwnerID:%d,
+                                ShapeType:%d,
+                                ObjCnt:%d,
+                                ObjList:%s
+                                '''\
+                                %(
+                                self.Num,
+                                self.OwnerID,
+                                self.ShapeType,
+                                self.ObjCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+class  tagSCTurnFightFaction(Structure):
+    Faction = 0    #(BYTE Faction)//阵营编号,1或2,1为发起方的阵营编号
+    LineupCnt = 0    #(BYTE LineupCnt)
+    LineupList = list()    #(vector<tagSCTurnFightLineup> LineupList)// 本阵营所有阵容列表,为支持多V多扩展用,通常情况下每个阵营只有一个阵容
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.Faction,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.LineupCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.LineupCnt):
+            temLineupList = tagSCTurnFightLineup()
+            _pos = temLineupList.ReadData(_lpData, _pos)
+            self.LineupList.append(temLineupList)
+        return _pos
+
+    def Clear(self):
+        self.Faction = 0
+        self.LineupCnt = 0
+        self.LineupList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 1
+        length += 1
+        for i in range(self.LineupCnt):
+            length += self.LineupList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteBYTE(data, self.Faction)
+        data = CommFunc.WriteBYTE(data, self.LineupCnt)
+        for i in range(self.LineupCnt):
+            data = CommFunc.WriteString(data, self.LineupList[i].GetLength(), self.LineupList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Faction:%d,
+                                LineupCnt:%d,
+                                LineupList:%s
+                                '''\
+                                %(
+                                self.Faction,
+                                self.LineupCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+class  tagSCTurnFightInit(Structure):
+    Head = tagHead()
+    MapID = 0    #(DWORD MapID)// 自定义地图ID,可用于绑定战斗地图场景功能(如主线关卡、主线boss、爬塔、竞技场等)
+    FuncLineID = 0    #(DWORD FuncLineID)// MapID对应的扩展值,如具体某个关卡等
+    TurnMax = 0    #(BYTE TurnMax)// 最大轮次
+    Len = 0    #(WORD Len)
+    Msg = ""    #(String Msg)// 本场战斗扩展信息,一般为json格式,具体内容由MapID决定
+    FactionCnt = 0    #(BYTE FactionCnt)
+    FactionList = list()    #(vector<tagSCTurnFightFaction> FactionList)// 阵营列表,通常固定只有两个阵营
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xB4
+        self.Head.SubCmd = 0x24
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.MapID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FuncLineID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TurnMax,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.Len,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.Msg,_pos = CommFunc.ReadString(_lpData, _pos,self.Len)
+        self.FactionCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FactionCnt):
+            temFactionList = tagSCTurnFightFaction()
+            _pos = temFactionList.ReadData(_lpData, _pos)
+            self.FactionList.append(temFactionList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xB4
+        self.Head.SubCmd = 0x24
+        self.MapID = 0
+        self.FuncLineID = 0
+        self.TurnMax = 0
+        self.Len = 0
+        self.Msg = ""
+        self.FactionCnt = 0
+        self.FactionList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 4
+        length += 1
+        length += 2
+        length += len(self.Msg)
+        length += 1
+        for i in range(self.FactionCnt):
+            length += self.FactionList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.MapID)
+        data = CommFunc.WriteDWORD(data, self.FuncLineID)
+        data = CommFunc.WriteBYTE(data, self.TurnMax)
+        data = CommFunc.WriteWORD(data, self.Len)
+        data = CommFunc.WriteString(data, self.Len, self.Msg)
+        data = CommFunc.WriteBYTE(data, self.FactionCnt)
+        for i in range(self.FactionCnt):
+            data = CommFunc.WriteString(data, self.FactionList[i].GetLength(), self.FactionList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                MapID:%d,
+                                FuncLineID:%d,
+                                TurnMax:%d,
+                                Len:%d,
+                                Msg:%s,
+                                FactionCnt:%d,
+                                FactionList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.MapID,
+                                self.FuncLineID,
+                                self.TurnMax,
+                                self.Len,
+                                self.Msg,
+                                self.FactionCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagSCTurnFightInit=tagSCTurnFightInit()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagSCTurnFightInit.Head.Cmd,m_NAtagSCTurnFightInit.Head.SubCmd))] = m_NAtagSCTurnFightInit
+
+
+#------------------------------------------------------
+# B4 21 回合战斗对象开始行动 #tagMCTurnFightObjAction
+
+class  tagMCTurnFightObjAction(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("TurnNum", c_ubyte),    # 当前轮次
+                  ("ObjID", c_int),    
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xB4
+        self.SubCmd = 0x21
+        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 = 0xB4
+        self.SubCmd = 0x21
+        self.TurnNum = 0
+        self.ObjID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCTurnFightObjAction)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// B4 21 回合战斗对象开始行动 //tagMCTurnFightObjAction:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                TurnNum:%d,
+                                ObjID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.TurnNum,
+                                self.ObjID
+                                )
+        return DumpString
+
+
+m_NAtagMCTurnFightObjAction=tagMCTurnFightObjAction()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCTurnFightObjAction.Cmd,m_NAtagMCTurnFightObjAction.SubCmd))] = m_NAtagMCTurnFightObjAction
+
+
+#------------------------------------------------------
+# B4 22 回合战斗对象死亡 #tagMCTurnFightObjDead
+
+class  tagMCTurnFightObjDead(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ObjID", c_int),    
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xB4
+        self.SubCmd = 0x22
+        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 = 0xB4
+        self.SubCmd = 0x22
+        self.ObjID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCTurnFightObjDead)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// B4 22 回合战斗对象死亡 //tagMCTurnFightObjDead:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ObjID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ObjID
+                                )
+        return DumpString
+
+
+m_NAtagMCTurnFightObjDead=tagMCTurnFightObjDead()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCTurnFightObjDead.Cmd,m_NAtagMCTurnFightObjDead.SubCmd))] = m_NAtagMCTurnFightObjDead
+
+
+#------------------------------------------------------
+# B4 23 回合战斗对象复活 #tagMCTurnFightObjReborn
+
+class  tagMCTurnFightObjReborn(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ObjID", c_int),    
+                  ("HP", c_int),    # 复活后血量,求余亿部分
+                  ("HPEx", c_int),    # 复活后血量,整除亿部分
+                  ("RebornType", c_ubyte),    # 复活方式:1-灵宠技能复活;2-待扩展
+                  ("RebornValue1", c_int),    # 复活方式对应值1,由复活方式决定其值意义
+                  ("RebornValue2", c_int),    # 复活方式对应值2
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xB4
+        self.SubCmd = 0x23
+        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 = 0xB4
+        self.SubCmd = 0x23
+        self.ObjID = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.RebornType = 0
+        self.RebornValue1 = 0
+        self.RebornValue2 = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCTurnFightObjReborn)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// B4 23 回合战斗对象复活 //tagMCTurnFightObjReborn:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ObjID:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                RebornType:%d,
+                                RebornValue1:%d,
+                                RebornValue2:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ObjID,
+                                self.HP,
+                                self.HPEx,
+                                self.RebornType,
+                                self.RebornValue1,
+                                self.RebornValue2
+                                )
+        return DumpString
+
+
+m_NAtagMCTurnFightObjReborn=tagMCTurnFightObjReborn()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCTurnFightObjReborn.Cmd,m_NAtagMCTurnFightObjReborn.SubCmd))] = m_NAtagMCTurnFightObjReborn
+
+
+#------------------------------------------------------
+# B4 30 查看战报结果 #tagSCTurnFightReportRet
+
+class  tagSCTurnFightReport(Structure):
+    Head = tagHead()
+    GUID = ""    #(char GUID[40])//该战报guid
+    Len = 0    #(DWORD Len)
+    Report = ""    #(String Report)//完整战报
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xB4
+        self.Head.SubCmd = 0x30
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.GUID,_pos = CommFunc.ReadString(_lpData, _pos,40)
+        self.Len,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Report,_pos = CommFunc.ReadString(_lpData, _pos,self.Len)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xB4
+        self.Head.SubCmd = 0x30
+        self.GUID = ""
+        self.Len = 0
+        self.Report = ""
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 40
+        length += 4
+        length += len(self.Report)
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteString(data, 40, self.GUID)
+        data = CommFunc.WriteDWORD(data, self.Len)
+        data = CommFunc.WriteString(data, self.Len, self.Report)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                GUID:%s,
+                                Len:%d,
+                                Report:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.GUID,
+                                self.Len,
+                                self.Report
+                                )
+        return DumpString
+
+
+
+#------------------------------------------------------
+# B4 25 回合战斗战报片段标记 #tagSCTurnFightReportSign
+
+class  tagSCTurnFightReportSign(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("Sign", c_ubyte),    # 0-战报片段开始;1-战报片段结束;
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xB4
+        self.SubCmd = 0x25
+        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 = 0xB4
+        self.SubCmd = 0x25
+        self.Sign = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagSCTurnFightReportSign)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// B4 25 回合战斗战报片段标记 //tagSCTurnFightReportSign:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                Sign:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.Sign
+                                )
+        return DumpString
+
+
+m_NAtagSCTurnFightReportSign=tagSCTurnFightReportSign()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagSCTurnFightReportSign.Cmd,m_NAtagSCTurnFightReportSign.SubCmd))] = m_NAtagSCTurnFightReportSign
+
+
+#------------------------------------------------------
 # B4 20 回合制战斗状态 #tagMCTurnFightState
 
 class  tagMCTurnFightState(Structure):
     Head = tagHead()
-    MapID = 0    #(DWORD MapID)// 自定义地图ID,可用于绑定战斗场景功能(如野外关卡,爬塔功能,竞技场等)
-    FuncLineID = 0    #(WORD FuncLineID)
-    TagType = 0    #(BYTE TagType)// 战斗目标类型,0-NPC,1-玩家,2-队伍
-    TagID = 0    #(DWORD TagID)// 战斗目标类型对应的ID
+    MapID = 0    #(DWORD MapID)// 自定义地图ID,可用于绑定战斗地图场景功能(如主线关卡、主线boss、爬塔、竞技场等)
+    FuncLineID = 0    #(DWORD FuncLineID)// MapID对应的扩展值,如具体某个关卡等
     State = 0    #(BYTE State)// 0-起始状态标记;1-准备完毕;2-战斗中;3-战斗结束;4-结算奖励;5-结束状态标记
     TurnNum = 0    #(BYTE TurnNum)// 当前轮次
-    TurnMax = 0    #(BYTE TurnMax)// 最大轮次
     Len = 0    #(WORD Len)
     Msg = ""    #(String Msg)//size = Len
     data = None
@@ -50752,12 +51348,9 @@
         self.Clear()
         _pos = self.Head.ReadData(_lpData, _pos)
         self.MapID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
-        self.FuncLineID,_pos = CommFunc.ReadWORD(_lpData, _pos)
-        self.TagType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
-        self.TagID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FuncLineID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
         self.State,_pos = CommFunc.ReadBYTE(_lpData, _pos)
         self.TurnNum,_pos = CommFunc.ReadBYTE(_lpData, _pos)
-        self.TurnMax,_pos = CommFunc.ReadBYTE(_lpData, _pos)
         self.Len,_pos = CommFunc.ReadWORD(_lpData, _pos)
         self.Msg,_pos = CommFunc.ReadString(_lpData, _pos,self.Len)
         return _pos
@@ -50769,11 +51362,8 @@
         self.Head.SubCmd = 0x20
         self.MapID = 0
         self.FuncLineID = 0
-        self.TagType = 0
-        self.TagID = 0
         self.State = 0
         self.TurnNum = 0
-        self.TurnMax = 0
         self.Len = 0
         self.Msg = ""
         return
@@ -50782,10 +51372,7 @@
         length = 0
         length += self.Head.GetLength()
         length += 4
-        length += 2
-        length += 1
         length += 4
-        length += 1
         length += 1
         length += 1
         length += 2
@@ -50797,12 +51384,9 @@
         data = ''
         data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
         data = CommFunc.WriteDWORD(data, self.MapID)
-        data = CommFunc.WriteWORD(data, self.FuncLineID)
-        data = CommFunc.WriteBYTE(data, self.TagType)
-        data = CommFunc.WriteDWORD(data, self.TagID)
+        data = CommFunc.WriteDWORD(data, self.FuncLineID)
         data = CommFunc.WriteBYTE(data, self.State)
         data = CommFunc.WriteBYTE(data, self.TurnNum)
-        data = CommFunc.WriteBYTE(data, self.TurnMax)
         data = CommFunc.WriteWORD(data, self.Len)
         data = CommFunc.WriteString(data, self.Len, self.Msg)
         return data
@@ -50812,11 +51396,8 @@
                                 Head:%s,
                                 MapID:%d,
                                 FuncLineID:%d,
-                                TagType:%d,
-                                TagID:%d,
                                 State:%d,
                                 TurnNum:%d,
-                                TurnMax:%d,
                                 Len:%d,
                                 Msg:%s
                                 '''\
@@ -50824,11 +51405,8 @@
                                 self.Head.OutputString(),
                                 self.MapID,
                                 self.FuncLineID,
-                                self.TagType,
-                                self.TagID,
                                 self.State,
                                 self.TurnNum,
-                                self.TurnMax,
                                 self.Len,
                                 self.Msg
                                 )
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py
new file mode 100644
index 0000000..ec633fb
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GM.Commands.MainLevel
+#
+# @todo:主线关卡
+# @author hxp
+# @date 2025-07-02
+# @version 1.0
+#
+# 详细描述: 主线关卡
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-07-02 17:30"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import PlayerControl
+import IpyGameDataPY
+
+def OnExec(curPlayer, gmList):
+    
+    if not gmList:
+        GameWorld.DebugAnswer(curPlayer, "重置主线: MainLevel 0")
+        GameWorld.DebugAnswer(curPlayer, "设置主线: MainLevel 章节 关卡 波")
+        return
+    
+    value = gmList[0]
+    
+    if value <= 0:
+        nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, 1, 1, 1)
+        passValue = PlayerControl.SetMainLevelPassInfo(curPlayer, 0, 0, 0)
+        GameWorld.DebugAnswer(curPlayer, "重置主线:now=%s,pass=%s" % (nowValue, passValue))
+        return
+    
+    chapterID = value
+    levelNum = gmList[1] if len(gmList) > 1 else 1
+    wave = gmList[2] if len(gmList) > 2 else 1
+    
+    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
+    if not levelIpyData:
+        GameWorld.DebugAnswer(curPlayer, "章节关卡不存在: 章关=%s-%s" % (chapterID, levelNum))
+        return
+    
+    # 本关卡最大波数,暂时支持最大6波
+    waveMax = 10
+    while waveMax >= 1 and (not hasattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax) or not getattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax)()):
+        waveMax -= 1
+    if wave > waveMax:
+        wave = waveMax
+        
+    nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, wave)
+    passValue = PlayerControl.SetMainLevelPassInfo(curPlayer, chapterID, levelNum, wave)
+    GameWorld.DebugAnswer(curPlayer, "设置主线:章关=%s-%s,波=%s,%s,%s" % (chapterID, levelNum, wave, nowValue, passValue))
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameObj.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameObj.py
index 715dbb1..06009e9 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameObj.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameObj.py
@@ -18,6 +18,8 @@
 import IpyGameDataPY
 import SkillCommon
 import PlayerControl
+import ChNetSendPack
+import TurnAttack
 
 # 关于血量的函数这里只包装最简单的超DWORD处理
 
@@ -35,7 +37,6 @@
     if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
         if isNotify == None:
             isNotify = True
-        gameObj.SetHP(value % ShareDefine.Def_PerPointValue, value / ShareDefine.Def_PerPointValue, isNotify)
     else:
         if isNotify == None:
             isNotify = False
@@ -44,17 +45,28 @@
             if IpyGameDataPY.GetIpyGameDataNotLog("NPCTimeLostHP", npcID) and not isByTime and value not in [GetMaxHP(gameObj), 0]:
                 #GameWorld.DebugLog("不能设置按时间掉血的怪物血量! id=%s,npciD=%s,value=%s,isByTime=%s" % (gameObj.GetID(), gameObj.GetNPCID(), value, isByTime))
                 return
-        gameObj.SetHP(value % ShareDefine.Def_PerPointValue, value / ShareDefine.Def_PerPointValue, isNotify)
+            
+    gameObj.SetHP(value % ShareDefine.Def_PerPointValue, value / ShareDefine.Def_PerPointValue, False)
+    if isNotify:
+        NotifyObjInfoRefresh(gameObj, IPY_GameWorld.CDBPlayerRefresh_HP, value)             
     return
 
-def SetHPFull(gameObj):
-    SetHP(gameObj, GetMaxHP(gameObj))
+def SetHPFull(gameObj, isNotify=True):
+    SetHP(gameObj, GetMaxHP(gameObj), isNotify)
     return
 
 def SetBaseMaxHP(gameObj, value):
     gameObj.SetBaseMaxHP(value % ShareDefine.Def_PerPointValue, value / ShareDefine.Def_PerPointValue)
     return
-    
+
+def GetXP(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_AttrXP)
+def SetXP(gameObj, value, isNotify=True):
+    ## XP值,用作怒气值
+    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrXP, value)
+    if isNotify:
+        NotifyObjInfoRefresh(gameObj, IPY_GameWorld.CDBPlayerRefresh_XP, value)
+    return
+
 def GetAngryValue(curAngry):
     return curAngry.GetAngryValue() + curAngry.GetAngryValueEx() * ShareDefine.Def_PerPointValue
 def SetAngryValue(curAngry, value):
@@ -152,13 +164,6 @@
     gameObj.SetDict(ChConfig.Def_PlayerKey_CurState, 0)
     return
 
-def GetPetDamPer(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_AttrPetDamPer)
-def SetPetDamPer(gameObj, value): 
-    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrPetDamPer, value)
-    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
-        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_PetDamPer, value)
-    return
-
 def GetLastHurtValue(gameObj):
     ## 最后一击伤害值
     hurt = gameObj.GetDictByKey(ChConfig.Def_PlayerKey_LastHurtValue)
@@ -167,6 +172,17 @@
 def SetLastHurtValue(gameObj, value):
     gameObj.SetDict(ChConfig.Def_PlayerKey_LastHurtValue, value % ShareDefine.Def_PerPointValue)
     gameObj.SetDict(ChConfig.Def_PlayerKey_LastHurtValueEx, value / ShareDefine.Def_PerPointValue)
+    return
+
+def GetLastBeHurtValue(gameObj):
+    ## 最后一次受伤值
+    return 0
+    #hurt = gameObj.GetDictByKey(ChConfig.Def_PlayerKey_LastBeHurtValue)
+    #hurtEx = gameObj.GetDictByKey(ChConfig.Def_PlayerKey_LastBeHurtValueEx)
+    #return hurtEx * ShareDefine.Def_PerPointValue + hurt
+def SetLastBeHurtValue(gameObj, value):
+    #gameObj.SetDict(ChConfig.Def_PlayerKey_LastBeHurtValue, value % ShareDefine.Def_PerPointValue)
+    #gameObj.SetDict(ChConfig.Def_PlayerKey_LastBeHurtValueEx, value / ShareDefine.Def_PerPointValue)
     return
 
 def GetBloodShiledHurt(gameObj):
@@ -179,28 +195,262 @@
     gameObj.SetDict(ChConfig.Def_PlayerKey_BloodShiledHurtEx, value / ShareDefine.Def_PerPointValue)
     return
 
-def GetSuperHitHurtPer(gameObj): return 0
-def SetSuperHitHurtPer(gameObj, value): return
-def GetSuperHitHurtDefPer(gameObj): return 0
-def SetSuperHitHurtDefPer(gameObj, value): return
+## ---------------------------------------------------------
+def ClearBattleEffect(gameObj):
+    gameObj.ClearBattleEffect()
+    # 其他py层自定义战斗属性,由于EffGetSet中不是所有属性接口均通用,固这里先手动调用
+    SetPetDamPer(gameObj, 0)
+    SetFinalHurtPer(gameObj, 0)
+    SetFinalHurtReducePer(gameObj, 0)
+    SetAtkSpeed(gameObj, 0)
+    SetSuperHitRateReduce(gameObj, 0)
+    SetSuperHitReduce(gameObj, 0)
+    SetFaintRate(gameObj, 0)
+    SetFaintDefRate(gameObj, 0)
+    SetComboRate(gameObj, 0)
+    SetComboDefRate(gameObj, 0)
+    SetComboDamPer(gameObj, 0)
+    SetAtkBackRate(gameObj, 0)
+    SetAtkBackDefRate(gameObj, 0)
+    SetSuckHPPer(gameObj, 0)
+    SetSuckHPDefPer(gameObj, 0)
+    SetAtkBackHP(gameObj, 0)
+    SetCurePer(gameObj, 0)
+    SetCureDefPer(gameObj, 0)
+    SetPetStrengthenPer(gameObj, 0)
+    SetPetWeakenPer(gameObj, 0)
+    SetDamageBackRate(gameObj, 0)
+    SetSuperHitHurtPer(gameObj, 0)
+    SetSuperHitHurtDefPer(gameObj, 0)
+    return
 
-def GetComboDefRate(gameObj): return 0
-def SetComboDefRate(gameObj, value): return
+def GetPetDamPer(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_AttrPetDamPer)
+def SetPetDamPer(gameObj, value): 
+    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrPetDamPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_PetDamPer, value)
+    return
 
-def GetAtkBackRate(gameObj): return 0
-def SetAtkBackRate(gameObj, value): return
-def GetAtkBackDefRate(gameObj): return 0
-def SetAtkBackDefRate(gameObj, value): return
+def GetFinalHurtPer(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_FinalHurtPer)
+def SetFinalHurtPer(gameObj, value):
+    ## 最终伤害百分比
+    gameObj.SetDict(ChConfig.Def_PlayerKey_FinalHurtPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_FinalHurtPer, value)
+    return
 
-def GetSuckHPPer(gameObj): return 0
-def SetSuckHPPer(gameObj, value): return
-def GetSuckHPDefPer(gameObj): return 0
-def SetSuckHPDefPer(gameObj, value): return
+def GetFinalHurtReducePer(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_FinalHurtReducePer)
+def SetFinalHurtReducePer(gameObj, value):
+    ## 最终伤害减免百分比
+    gameObj.SetDict(ChConfig.Def_PlayerKey_FinalHurtReducePer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_FinalHurtReducePer, value)
+    return
 
-def GetCureDefPer(gameObj): return 0
-def SetCureDefPer(gameObj, value): return
+def GetFaction(gameObj):
+    faction = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_Faction)
+    if faction:
+        return faction
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        return gameObj.GetFaction()
+    return gameObj.GetCountry()
+def SetFaction(gameObj, value):
+    gameObj.SetDict(ChConfig.Def_Obj_Dict_Faction, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        gameObj.SetFaction(value)
+    return
 
-def GetPetStrengthenPer(gameObj): return 0
-def SetPetStrengthenPer(gameObj, value): return
-def GetPetWeakenPer(gameObj): return 0
-def SetPetWeakenPer(gameObj, value): return
+def GetAtkSpeed(gameObj):
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        return gameObj.GetBattleValEx1()
+    return gameObj.GetDictByKey(ChConfig.AttrName_AtkSpeed)
+def SetAtkSpeed(gameObj, value):
+    ## 攻击速度
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        gameObj.SetBattleValEx1(value, True)
+    else:
+        gameObj.SetDict(ChConfig.AttrName_AtkSpeed, value)
+    return
+
+def GetMissRate(gameObj): return gameObj.GetMiss()
+def SetMissRate(gameObj, value):
+    ## 闪避概率
+    gameObj.SetMiss(value)
+    return
+def GetMissDefRate(gameObj): return gameObj.GetHit()
+def SetMissDefRate(gameObj, value):
+    ## 抗闪避率 = 命中
+    gameObj.SetHit(value)
+    return
+def GetSuperHitRate(gameObj): return gameObj.GetSuperHitRate()
+def SetSuperHitRate(gameObj, value):
+    ## 暴击概率
+    gameObj.SetSuperHitRate(value)
+    return
+def GetSuperHitRateReduce(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_SuperHitRateReduce)
+def SetSuperHitRateReduce(gameObj, value): 
+    ## 抗暴击概率
+    gameObj.SetDict(ChConfig.Def_PlayerKey_SuperHitRateReduce, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_SuperHitRateReduce, value)
+    return
+
+def GetSuperHitReduce(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_SuperHitReduce)
+def SetSuperHitReduce(gameObj, value):
+    ## 暴击固定伤害减免 - 暴击时附加固定伤害减免,与 atkObj.GetSuperHit() 相对
+    gameObj.SetDict(ChConfig.Def_PlayerKey_SuperHitReduce, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_SuperHitReduce, value)
+    return
+
+def GetSuperHitHurtPer(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_SuperHitHurtPer)
+def SetSuperHitHurtPer(gameObj, value):
+    ## 强化暴伤 - 暴击时的总伤害加成,与 SuperHitHurtDefPer 相对,注意与暴击固定伤害区分
+    gameObj.SetDict(ChConfig.AttrName_SuperHitHurtPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_SuperHitHurtPer, value)
+    return
+def GetSuperHitHurtDefPer(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_SuperHitHurtDefPer)
+def SetSuperHitHurtDefPer(gameObj, value):
+    ## 弱化暴伤 - 暴击时的总伤害减免
+    gameObj.SetDict(ChConfig.AttrName_SuperHitHurtDefPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_SuperHitHurtDefPer, value)
+    return
+
+
+def GetFaintRate(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_AttrFaintRate)
+def SetFaintRate(gameObj, value):
+    ## 击晕概率
+    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrFaintRate, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_FaintRate, value)
+    return
+def GetFaintDefRate(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_AttrFaintDefRate)
+def SetFaintDefRate(gameObj, value):
+    ## 抗击晕概率
+    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrFaintDefRate, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_FaintDefRate, value)
+    return
+
+def GetComboRate(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_AttrComboRate)
+def SetComboRate(gameObj, value):
+    ## 连击概率
+    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrComboRate, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_ComboRate, value)
+    return
+def GetComboDefRate(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_ComboDefRate)
+def SetComboDefRate(gameObj, value):
+    ## 抗连击概率
+    gameObj.SetDict(ChConfig.AttrName_ComboDefRate, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_ComboDefRate, value)
+    return
+def GetComboDamPer(gameObj): return gameObj.GetDictByKey(ChConfig.Def_PlayerKey_AttrComboDamPer)
+def SetComboDamPer(gameObj, value):
+    ## 连击伤害
+    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrComboDamPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_ComboDamPer, value)
+    return
+
+def GetAtkBackRate(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_AtkBackRate)
+def SetAtkBackRate(gameObj, value):
+    ## 反击概率
+    gameObj.SetDict(ChConfig.AttrName_AtkBackRate, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_AtkBackRate, value)
+    return
+def GetAtkBackDefRate(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_AtkBackDefRate)
+def SetAtkBackDefRate(gameObj, value):
+    ## 抗反击概率
+    gameObj.SetDict(ChConfig.AttrName_AtkBackDefRate, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_AtkBackDefRate, value)
+    return
+
+def GetSuckHPPer(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_SuckHPPer)
+def SetSuckHPPer(gameObj, value):
+    ## 吸血比率
+    gameObj.SetDict(ChConfig.AttrName_SuckHPPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_SuckHPPer, value)
+    return
+def GetSuckHPDefPer(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_SuckHPDefPer)
+def SetSuckHPDefPer(gameObj, value):
+    ## 抗吸血比率
+    gameObj.SetDict(ChConfig.AttrName_SuckHPDefPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_SuckHPDefPer, value)
+    return
+def GetAtkBackHP(gameObj):
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        return gameObj.GetBattleValEx2()
+    return gameObj.GetDictByKey(ChConfig.AttrName_AtkBackHP)
+def SetAtkBackHP(gameObj, value):
+    ## 吸血固定值
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        gameObj.SetBattleValEx2(value)
+    else:
+        gameObj.SetDict(ChConfig.AttrName_AtkBackHP, value)
+    return
+
+def GetCurePer(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_CurePer)
+def SetCurePer(gameObj, value):
+    ## 强化治疗
+    gameObj.SetDict(ChConfig.AttrName_CurePer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_CurePer, value)
+    return
+def GetCureDefPer(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_CureDefPer)
+def SetCureDefPer(gameObj, value):
+    ## 弱化治疗
+    gameObj.SetDict(ChConfig.AttrName_CureDefPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_CureDefPer, value)
+    return
+
+def GetPetStrengthenPer(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_PetStrengthenPer)
+def SetPetStrengthenPer(gameObj, value):
+    ## 强化灵兽
+    gameObj.SetDict(ChConfig.AttrName_PetStrengthenPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_PetStrengthenPer, value)
+    return
+def GetPetWeakenPer(gameObj): return gameObj.GetDictByKey(ChConfig.AttrName_PetWeakenPer)
+def SetPetWeakenPer(gameObj, value):
+    ## 弱化灵兽
+    gameObj.SetDict(ChConfig.AttrName_PetWeakenPer, value)
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        PlayerControl.SendPropertyRefresh(gameObj, ShareDefine.CDBPlayerRefresh_PetWeakenPer, value)
+    return
+
+def GetDamageBackRate(gameObj):
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        return gameObj.GetDamageBackRate()
+    return gameObj.GetDictByKey(ChConfig.AttrName_DamBackPer)
+def SetDamageBackRate(gameObj, value):
+    ## 反弹伤害
+    if gameObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        gameObj.SetDamageBackRate(value)
+    else:
+        gameObj.SetDict(ChConfig.AttrName_DamBackPer, value)
+    return
+
+def NotifyObjInfoRefresh(gameObj, refreshType, value):
+    ##0418通知对象属性刷新
+    sendPack = ChNetSendPack.tagObjInfoRefresh()
+    sendPack.Clear()
+    sendPack.ObjID = gameObj.GetID()
+    sendPack.ObjType = gameObj.GetGameObjType()
+    sendPack.RefreshType = refreshType
+    sendPack.Value = value % ShareDefine.Def_PerPointValue
+    sendPack.ValueEx = value / ShareDefine.Def_PerPointValue
+    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(gameObj.GetID())
+    if turnFight:
+        turnFight.addBatPack(sendPack)
+        return
+    gameObj.NotifyAll(sendPack.GetBuffer(), sendPack.GetLength())
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
index a08a79c..73211db 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
@@ -507,6 +507,8 @@
 # @return 返回值. 是否通过检查
 # @remarks 概率相关, 这个事件是否能够出现
 def CanHappen(rate, maxRate=ShareDefine.Def_MaxRateValue):
+    if rate <= 0:
+        return 0
     if random.randint(0, maxRate -1) < rate:
         return 1
     
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 6dee3aa..32d0998 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
@@ -669,8 +669,8 @@
                 itemDict['ItemID'] = itemInfo[0]
             if infolen > 1:
                 itemDict['Count'] = itemInfo[1]
-            if infolen > 2:
-                itemDict['IsAuctionItem'] = int(itemInfo[2])
+            #if infolen > 2:
+            #    itemDict['IsAuctionItem'] = int(itemInfo[2])
         elif isinstance(itemInfo, int):
             itemDict['ItemID'] = itemInfo
         elif isinstance(itemInfo, dict):
@@ -680,7 +680,7 @@
                 continue
             itemDict['ItemID'] = itemInfo.GetItemTypeID()
             itemDict['Count'] = itemInfo.GetCount()
-            itemDict['IsAuctionItem'] = ItemControler.GetIsAuctionItem(itemInfo)
+            #itemDict['IsAuctionItem'] = ItemControler.GetIsAuctionItem(itemInfo)
             #itemDict['IsSuite'] = int(itemInfo.GetIsSuite())
             itemDict['UserData'] = itemInfo.GetUserData()
         jsonItemList.append(itemDict)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldEvent.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldEvent.py
index ed0e8f0..0b2a0a9 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldEvent.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldEvent.py
@@ -22,6 +22,7 @@
 import PlayerEventCounter
 import PlayerControl
 import NetPackCommon
+import PlayerOnline
 import ShareDefine
 import PlayerTeam
 import PyGameData
@@ -263,6 +264,7 @@
     #GameWorldActionControl.Dispose_OperationActionState()
     #GameWorldActionControl.Dispose_DailyActionState()
     #GameWorldActionControl.Dispose_FBStateTime()
+    PlayerOnline.OnMinute()
     
     PlayerTeam.OnCheckTeamPlayerDisconnectTimeout(tick)
     __CheckIpyDataRecycle(curTime)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
index 3b1c8ad..c8f801b 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -61,7 +61,7 @@
                         ("BYTE", "Country", 0),
                         ("BYTE", "Quality", 0),
                         ("list", "SkinNPCIDList", 0),
-                        ("DWORD", "AtkSkillID", 0),
+                        ("DWORD", "NormalSkillID", 0),
                         ("DWORD", "AngerSkillID", 0),
                         ("WORD", "AtkInheritPer", 0),
                         ("WORD", "DefInheritPer", 0),
@@ -137,6 +137,37 @@
                         ("DWORD", "Quality", 1),
                         ("DWORD", "AwakeLV", 1),
                         ("list", "UPCostItem", 0),
+                        ),
+
+                "MainChapter":(
+                        ("BYTE", "ChapterID", 1),
+                        ("list", "DailyBootyUpperList", 0),
+                        ("list", "BootyWeightList", 0),
+                        ),
+
+                "MainLevel":(
+                        ("BYTE", "ChapterID", 1),
+                        ("BYTE", "LevelNum", 1),
+                        ("list", "WaveLineupIDList1", 0),
+                        ("list", "WaveLineupIDList2", 0),
+                        ("list", "WaveLineupIDList3", 0),
+                        ("list", "WaveLineupIDList4", 0),
+                        ("list", "WaveLineupIDList5", 0),
+                        ("list", "WaveLineupIDList6", 0),
+                        ("list", "BossLineupIDList", 0),
+                        ("list", "AwardItemList", 0),
+                        ),
+
+                "NPCLineup":(
+                        ("DWORD", "LineupID", 1),
+                        ("DWORD", "PosNPCID1", 0),
+                        ("DWORD", "PosNPCID2", 0),
+                        ("DWORD", "PosNPCID3", 0),
+                        ("DWORD", "PosNPCID4", 0),
+                        ("DWORD", "PosNPCID5", 0),
+                        ("DWORD", "PosNPCID6", 0),
+                        ("DWORD", "PosNPCID7", 0),
+                        ("DWORD", "BossID", 0),
                         ),
 
                 "Dienstgrad":(
@@ -682,6 +713,27 @@
                         ("DWORD", "NPCID", 1),
                         ("BYTE", "FightPowerLackAtkLimit", 0),
                         ("DWORD", "SuppressFightPower", 0),
+                        ("BYTE", "AtkDictType", 0),
+                        ("DWORD", "Atk", 0),
+                        ("DWORD", "Def", 0),
+                        ("DWORD", "MaxHP", 0),
+                        ("list", "SkillIDList", 0),
+                        ("DWORD", "FinalHurtPer", 0),
+                        ("DWORD", "FinalHurtReducePer", 0),
+                        ("DWORD", "MissRate", 0),
+                        ("DWORD", "MissDefRate", 0),
+                        ("DWORD", "SuperHitRate", 0),
+                        ("DWORD", "SuperHitRateReduce", 0),
+                        ("DWORD", "FaintRate", 0),
+                        ("DWORD", "FaintDefRate", 0),
+                        ("DWORD", "ComboRate", 0),
+                        ("DWORD", "ComboDefRate", 0),
+                        ("DWORD", "ParryRate", 0),
+                        ("DWORD", "ParryDefRate", 0),
+                        ("DWORD", "ParryDamPer", 0),
+                        ("DWORD", "SuckHPPer", 0),
+                        ("DWORD", "SuckHPDefPer", 0),
+                        ("dict", "SpecAttrInfo", 0),
                         ),
 
                 "NPCRealmStrengthen":(
@@ -2784,7 +2836,7 @@
     def GetCountry(self): return self.attrTuple[1] #  国家 BYTE
     def GetQuality(self): return self.attrTuple[2] #  品质 BYTE
     def GetSkinNPCIDList(self): return self.attrTuple[3] #  皮肤NPCID列表 list
-    def GetAtkSkillID(self): return self.attrTuple[4] # 普攻技能ID DWORD
+    def GetNormalSkillID(self): return self.attrTuple[4] # 普攻技能ID DWORD
     def GetAngerSkillID(self): return self.attrTuple[5] # 怒气技能ID DWORD
     def GetAtkInheritPer(self): return self.attrTuple[6] # 攻击继承 WORD
     def GetDefInheritPer(self): return self.attrTuple[7] # 防御继承 WORD
@@ -2900,6 +2952,52 @@
     def GetQuality(self): return self.attrTuple[0] # 品质 DWORD
     def GetAwakeLV(self): return self.attrTuple[1] # 觉醒等级 DWORD
     def GetUPCostItem(self): return self.attrTuple[2] #  觉醒到下级消耗道具 list
+
+# 主线章节表
+class IPY_MainChapter():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetChapterID(self): return self.attrTuple[0] # 章节ID BYTE
+    def GetDailyBootyUpperList(self): return self.attrTuple[1] #  每日战利品掉落上限,[[物品ID,每日上限], ...] list
+    def GetBootyWeightList(self): return self.attrTuple[2] #  战利品掉落权重,[[权重,物品ID,掉落个数下限, 上限], ...] list
+
+# 主线关卡表
+class IPY_MainLevel():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetChapterID(self): return self.attrTuple[0] # 章节ID BYTE
+    def GetLevelNum(self): return self.attrTuple[1] # 章节关卡编号 BYTE
+    def GetWaveLineupIDList1(self): return self.attrTuple[2] #  波1阵容ID列表,小队1阵容ID|小队2阵容ID|... list
+    def GetWaveLineupIDList2(self): return self.attrTuple[3] #  波2阵容ID列表,小队1阵容ID|小队2阵容ID|... list
+    def GetWaveLineupIDList3(self): return self.attrTuple[4] #  波3阵容ID列表,小队1阵容ID|小队2阵容ID|... list
+    def GetWaveLineupIDList4(self): return self.attrTuple[5] #  波4阵容ID列表,小队1阵容ID|小队2阵容ID|... list
+    def GetWaveLineupIDList5(self): return self.attrTuple[6] #  波5阵容ID列表,小队1阵容ID|小队2阵容ID|... list
+    def GetWaveLineupIDList6(self): return self.attrTuple[7] #  波6阵容ID列表,小队1阵容ID|小队2阵容ID|... list
+    def GetBossLineupIDList(self): return self.attrTuple[8] #  Boss波阵容ID列表,小队1阵容ID|小队2阵容ID|... list
+    def GetAwardItemList(self): return self.attrTuple[9] #  过关奖励列表,[[物品ID,个数], ...] list
+
+# NPC阵容表
+class IPY_NPCLineup():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetLineupID(self): return self.attrTuple[0] # 阵容ID DWORD
+    def GetPosNPCID1(self): return self.attrTuple[1] # 1号位NPCID DWORD
+    def GetPosNPCID2(self): return self.attrTuple[2] # 2号位NPCID DWORD
+    def GetPosNPCID3(self): return self.attrTuple[3] # 3号位NPCID DWORD
+    def GetPosNPCID4(self): return self.attrTuple[4] # 4号位NPCID DWORD
+    def GetPosNPCID5(self): return self.attrTuple[5] # 5号位NPCID DWORD
+    def GetPosNPCID6(self): return self.attrTuple[6] # 6号位NPCID DWORD
+    def GetPosNPCID7(self): return self.attrTuple[7] # 7号位NPCID DWORD
+    def GetBossID(self): return self.attrTuple[8] #  本阵容的BossID,没有boss时为0 DWORD
 
 # 称号表
 class IPY_Dienstgrad():
@@ -3734,7 +3832,28 @@
         
     def GetNPCID(self): return self.attrTuple[0] # NPCID DWORD
     def GetFightPowerLackAtkLimit(self): return self.attrTuple[1] # 战力不足限制攻击 BYTE
-    def GetSuppressFightPower(self): return self.attrTuple[2] # 推荐/压制战力 DWORD
+    def GetSuppressFightPower(self): return self.attrTuple[2] # 推荐/压制战力 DWORD
+    def GetAtkDictType(self): return self.attrTuple[3] # 远近类型;1-近战;2-远程 BYTE
+    def GetAtk(self): return self.attrTuple[4] # 攻击力 DWORD
+    def GetDef(self): return self.attrTuple[5] # 防御值 DWORD
+    def GetMaxHP(self): return self.attrTuple[6] # 最大生命值,可超过20E DWORD
+    def GetSkillIDList(self): return self.attrTuple[7] # 技能ID列表 list
+    def GetFinalHurtPer(self): return self.attrTuple[8] # 最终增伤 DWORD
+    def GetFinalHurtReducePer(self): return self.attrTuple[9] # 最终减伤 DWORD
+    def GetMissRate(self): return self.attrTuple[10] # 闪避概率 DWORD
+    def GetMissDefRate(self): return self.attrTuple[11] # 抗闪避概率 DWORD
+    def GetSuperHitRate(self): return self.attrTuple[12] # 暴击概率 DWORD
+    def GetSuperHitRateReduce(self): return self.attrTuple[13] # 抗暴击概率 DWORD
+    def GetFaintRate(self): return self.attrTuple[14] # 击晕概率 DWORD
+    def GetFaintDefRate(self): return self.attrTuple[15] # 抗击晕概率 DWORD
+    def GetComboRate(self): return self.attrTuple[16] # 连击概率 DWORD
+    def GetComboDefRate(self): return self.attrTuple[17] # 抗连击概率 DWORD
+    def GetParryRate(self): return self.attrTuple[18] # 格挡概率 DWORD
+    def GetParryDefRate(self): return self.attrTuple[19] # 抗格挡概率 DWORD
+    def GetParryDamPer(self): return self.attrTuple[20] # 格挡减伤比率 DWORD
+    def GetSuckHPPer(self): return self.attrTuple[21] # 吸血比率 DWORD
+    def GetSuckHPDefPer(self): return self.attrTuple[22] # 抗吸血比率 DWORD
+    def GetSpecAttrInfo(self): return self.attrTuple[23] # 特殊属性信息 {"属性ID":值, ...} dict
 
 # 成长型境界怪物表
 class IPY_NPCRealmStrengthen():
@@ -6919,6 +7038,9 @@
         self.__LoadFileData("HeroQuality", onlyCheck)
         self.__LoadFileData("HeroQualityBreak", onlyCheck)
         self.__LoadFileData("HeroQualityAwake", onlyCheck)
+        self.__LoadFileData("MainChapter", onlyCheck)
+        self.__LoadFileData("MainLevel", onlyCheck)
+        self.__LoadFileData("NPCLineup", onlyCheck)
         self.__LoadFileData("Dienstgrad", onlyCheck)
         self.__LoadFileData("TitleStarUp", onlyCheck)
         self.__LoadFileData("PlayerFace", onlyCheck)
@@ -7485,6 +7607,27 @@
         self.CheckLoadData("HeroQualityAwake")
         return self.ipyHeroQualityAwakeCache[index]
 
+    def GetMainChapterCount(self):
+        self.CheckLoadData("MainChapter")
+        return self.ipyMainChapterLen
+    def GetMainChapterByIndex(self, index):
+        self.CheckLoadData("MainChapter")
+        return self.ipyMainChapterCache[index]
+
+    def GetMainLevelCount(self):
+        self.CheckLoadData("MainLevel")
+        return self.ipyMainLevelLen
+    def GetMainLevelByIndex(self, index):
+        self.CheckLoadData("MainLevel")
+        return self.ipyMainLevelCache[index]
+
+    def GetNPCLineupCount(self):
+        self.CheckLoadData("NPCLineup")
+        return self.ipyNPCLineupLen
+    def GetNPCLineupByIndex(self, index):
+        self.CheckLoadData("NPCLineup")
+        return self.ipyNPCLineupCache[index]
+
     def GetDienstgradCount(self):
         self.CheckLoadData("Dienstgrad")
         return self.ipyDienstgradLen
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py
index 2700dd6..2ea5c7d 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py
@@ -2003,8 +2003,23 @@
     武将等级>突破等级>武将星级>武将品质>武将ID
     '''
     
-    posNum1 = item1.GetUserAttr(ShareDefine.Def_IudetHeroPosNum)
-    posNum2 = item2.GetUserAttr(ShareDefine.Def_IudetHeroPosNum)
+    posNum1, posNum2 = 0, 0
+    for lpIndex in range(item1.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)):
+        lineupValue = item1.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
+        lineupID = lineupValue / 10000
+        if lineupID != ShareDefine.Lineup_Main:
+            continue
+        posNum1 = lineupValue % 100
+        break
+        
+    for lpIndex in range(item2.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)):
+        lineupValue = item2.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
+        lineupID = lineupValue / 10000
+        if lineupID != ShareDefine.Lineup_Main:
+            continue
+        posNum2 = lineupValue % 100
+        break
+    
     if (posNum1 and posNum2) or (not posNum1 and not posNum2):
         lv1 = item1.GetUserAttr(ShareDefine.Def_IudetHeroLV)
         lv2 = item2.GetUserAttr(ShareDefine.Def_IudetHeroLV)
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 7ccb093..cbb2482 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py
@@ -412,6 +412,9 @@
 #        if tick - curNPC.GetAttackTick() < ChConfig.TYPE_NPC_Tick_ProcessAI:
 #            #攻击间隔没有到, 返回
 #            return
+    if curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
+        #GameWorld.DebugLog("回合制NPC不处理AI!", curNPC.GetID())
+        return
     npcID = curNPC.GetNPCID()
     endTick = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FBDict_NPCShowEndTick % npcID)
     if endTick:
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 806394e..e0edf24 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
@@ -71,6 +71,7 @@
 import IpyGameDataPY
 import PlayerGubao
 import PlayerState
+import TurnAttack
 import PyGameData
 import PlayerTeam
 import NPCHurtMgr
@@ -149,7 +150,7 @@
 def SetSuppressFightPower(curNPC, value): return curNPC.SetThunderDef(min(value, ShareDefine.Def_UpperLimit_DWord))
 def GetCommendFightPower(curNPC): return curNPC.GetFireDef() # 火防代表推荐战力
 def GetDropOwnerType(curNPC): return curNPC.GetThunderAtk() # 雷攻代表掉落归属类型
-def GetFaction(curNPC): return curNPC.GetCountry()
+def GetFaction(curNPC): return GameObj.GetFaction(curNPC)
 def GetSkillAtkRate(curNPC): return curNPC.GetPoisionAtk() # 毒攻代表NPC技能伤害加成万分率
 def GetFinalHurt(curNPC): return curNPC.GetFireAtk() # 火攻代表NPC最终固定伤害加成, 普攻也有效果
 def SetFinalHurt(curNPC, hurt): return curNPC.SetFireAtk(hurt) # 火攻代表NPC最终固定伤害加成, 普攻也有效果
@@ -2261,7 +2262,9 @@
 #  @return 
 def SetDeadEx(curNPC):
     summon_List = []
+    objID = curNPC.GetID()
     npcid = curNPC.GetNPCID()
+    GameWorld.DebugLog("SetDeadEx objID=%s,npcID=%s" % (objID, npcid))
     #将涉及到C++中列表删除的功能,统一改成 -> 复制Py列表后,然后进行删除逻辑 
     for index in range(curNPC.GetSummonCount()):
         curSummonNPC = curNPC.GetSummonNPCAt(index)
@@ -2322,9 +2325,20 @@
                 break
             
     # C++设置npc死亡
+    notifyClient = True
+    tfMgr = TurnAttack.GetTurnFightMgr()
+    turnFight = tfMgr.getNPCTurnFight(objID)
+    if turnFight:
+        notifyClient = False # 回合制战斗的由py自己通知
+        # //04 07 NPC消失#tagNPCDisappear 此处通知消失,与回合制死亡区分
+        clientPack = ChNetSendPack.tagNPCDisappear()
+        clientPack.NPCID = [objID]
+        clientPack.Count = len(clientPack.NPCID)
+        turnFight.addBatPack(clientPack)
     curNPC.SetDead(curNPC.GetDictByKey(ChConfig.Def_NPCDead_Reason),
                    curNPC.GetDictByKey(ChConfig.Def_NPCDead_KillerType),
-                   curNPC.GetDictByKey(ChConfig.Def_NPCDead_KillerID))
+                   curNPC.GetDictByKey(ChConfig.Def_NPCDead_KillerID), notifyClient)
+    tfMgr.delNPCGUID(objID)
     return
 
 def GameServer_KillGameWorldBoss(bossID, killPlayerName, hurtValue, isNotify=True, killerIDList=[]):
@@ -3627,41 +3641,35 @@
             GameWorld.DebugLog("NPC复活,套上光环: objID=%s,npcID=%s,skillID=%s" % (curNPC.GetID(), curNPC.GetNPCID(), useSkill.GetSkillID()))
             SkillShell.NPCUseSkill(curNPC, useSkill, tick)
             
-        curNPC.NotifyAppear() # 最终统一通知NPC出现
-        self.NotifyNPCShow(curNPCID, tick) # 广播NPC秀
+        self.__notifyAppear() # 最终统一通知NPC出现
         return
     
-    def NotifyNPCShow(self, npcID, tick):
-        ## 广播NPC秀
-        mapID = GameWorld.GetMap().GetMapID()
-        npcShowIpyData = IpyGameDataPY.GetIpyGameDataNotLog("NPCShow", npcID, mapID)
-        if not npcShowIpyData:
-            #GameWorld.DebugLog("不需要NPC秀: npcID=%s" % npcID)
+    def __notifyAppear(self):
+        ## //04 06 NPC出现#tagNPCAppear,可能也有 04 08 玩家召唤NPC出现#tagPlayerSummonNPCAppear,卡牌先简化,只使用0406
+        curNPC = self.__Instance
+        objID = curNPC.GetID()
+        turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(objID)
+        if not turnFight:
+            # 非回合制怪保留原通知
+            curNPC.NotifyAppear()
             return
-        #if npcShowIpyData.GetBindMissionID():
-        #    #GameWorld.DebugLog("有绑定任务ID的,前端自己展示NPC秀!mapID=%s,npcID=%s" % (mapID, npcID))
-        #    return
-        if npcShowIpyData.GetShowType():
-            #GameWorld.DebugLog("前端自己展示的NPC秀!mapID=%s,npcID=%s" % (mapID, npcID))
-            return
-        endTick = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.Def_FBDict_NPCShowEndTick % npcID)
-        if endTick:
-            #GameWorld.DebugLog("已经存在同个NPCID的NPC秀,不重复展示!npcID=%s" % (npcID))
-            return
-        endTick = tick + npcShowIpyData.GetProtectTime()
-        GameWorld.GetGameFB().SetGameFBDict(ChConfig.Def_FBDict_NPCShowEndTick % npcID, endTick)
         
-        # 广播地图内玩家展示NPC秀
-        npcShowPack = ChPyNetSendPack.tagMCNPCShow()
-        npcShowPack.NPCID = npcID
-        playerManager = GameWorld.GetMapCopyPlayerManager()
-        for index in xrange(playerManager.GetPlayerCount()):
-            player = playerManager.GetPlayerByIndex(index)
-            if not player.GetPlayerID():
-                continue
-            NetPackCommon.SendFakePack(player, npcShowPack)
-            
-        GameWorld.DebugLog("开始NPC秀: npcID=%s,tick=%s,endTick=%s" % (npcID, tick, endTick))
+        # 回合制怪不通知,统一由 // B4 24 回合战斗初始化 #tagSCTurnFightInit
+        
+        # 使用bt版本开发测试暂时留着,方便测试,正式需删除通知
+        if turnFight.curPlayer:
+            clientPack = ChNetSendPack.tagNPCAppear()
+            clientPack.ObjID = objID
+            clientPack.NPCID = curNPC.GetNPCID()
+            clientPack.NPCHP = curNPC.GetHP()
+            clientPack.NPCHPEx = curNPC.GetHPEx()
+            clientPack.MaxHP = curNPC.GetMaxHP()
+            clientPack.MaxHPEx = curNPC.GetMaxHPEx()
+            clientPack.CurLV = curNPC.GetCurLV()
+            clientPack.PosX = curNPC.GetPosX()
+            clientPack.PosY = curNPC.GetPosY()
+            clientPack.Speed = curNPC.GetSpeed()
+            NetPackCommon.SendFakePack(turnFight.curPlayer, clientPack)
         return
     
     #---------------------------------------------------------------------
@@ -3771,12 +3779,61 @@
     #  @return 返回值无意义
     #  @remarks 刷新NPC属性和行为状态
     def RefreshNPCState(self, canSyncClient=True, isReborn=False):
+        curNPC = self.__Instance
+        if curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
+            # 回合制怪走自己的刷属性规则
+            self.RefreshTurnfightNPCAttr()
+            return
+        
         self.RefreshNPCAttrState(canSyncClient, isReborn)
         
         self.RefreshNPCActionState()
 
-
+    def RefreshTurnfightNPCAttr(self):
+        curNPC = self.__Instance
+        lineupPlayerID = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_LineupPlayerID)
+        heroAttrDict = {}
+        if lineupPlayerID:
+            heroAttrDict.update({
+                                 ShareDefine.Def_Effect_Atk:500000000,
+                                 ShareDefine.Def_Effect_Def:50000000,
+                                 ShareDefine.Def_Effect_MaxHP:3000000000,
+                                 })
+        else:
+            npcDataEx = GetNPCDataEx(curNPC.GetNPCID())
+            if not npcDataEx:
+                return
+            heroAttrDict.update({
+                                 ShareDefine.Def_Effect_Atk:npcDataEx.GetAtk(),
+                                 ShareDefine.Def_Effect_Def:npcDataEx.GetDef(),
+                                 ShareDefine.Def_Effect_MaxHP:npcDataEx.GetMaxHP(),
+                                 })
+            
+        GameWorld.DebugLog("heroAttrDict: ID:%s,NPCID:%s,%s" % (curNPC.GetID(), curNPC.GetNPCID(), heroAttrDict))
+        # 重置属性状态
+        GameObj.ClearBattleEffect(curNPC)
+        curNPC.ResetNPCBattleState()
         
+        # 设置属性
+        curNPC.SetMinAtk(heroAttrDict.get(ShareDefine.Def_Effect_Atk, 1))
+        curNPC.SetMaxAtk(heroAttrDict.get(ShareDefine.Def_Effect_Atk, 1))
+        curNPC.SetDef(heroAttrDict.get(ShareDefine.Def_Effect_Def, 1))
+        GameObj.SetMaxHP(curNPC, heroAttrDict.get(ShareDefine.Def_Effect_MaxHP, 1))
+        
+        #GameObj.SetMissRate(curNPC, npcDataEx.GetMissRate())
+        #GameObj.SetMissDefRate(curNPC, npcDataEx.GetMissDefRate())
+        #GameObj.SetSuperHitRate(curNPC, npcDataEx.GetSuperHitRate())
+        #GameObj.SetSuperHitRateReduce(curNPC, npcDataEx.GetSuperHitRateReduce())
+        #GameObj.SetFaintRate(curNPC, npcDataEx.GetFaintRate())
+        #GameObj.SetFaintDefRate(curNPC, npcDataEx.GetFaintDefRate())
+        #GameObj.SetComboRate(curNPC, npcDataEx.GetComboRate())
+        #GameObj.SetComboDefRate(curNPC, npcDataEx.GetComboDefRate())
+        #GameObj.SetAtkBackRate(curNPC, npcDataEx.GetAtkBackRate())
+        #GameObj.SetAtkBackDefRate(curNPC, npcDataEx.GetAtkBackDefRate())
+        #GameObj.SetSuckHPPer(curNPC, npcDataEx.GetSuckHPPer())
+        #GameObj.SetSuckHPDefPer(curNPC, npcDataEx.GetSuckHPDefPer())
+        return
+    
     ## 刷新NPC属性
     #  @param self 类实例
     #  @param canSyncClient 是否通知客户端刷新信息(宠物)
@@ -6119,13 +6176,7 @@
 
 def NPCSpeedChangeNotify(curNPC, speed):
     ##通知NPC速度
-    sendPack = ChNetSendPack.tagObjInfoRefresh()
-    sendPack.Clear()
-    sendPack.ObjID = curNPC.GetID()
-    sendPack.ObjType = curNPC.GetGameObjType()
-    sendPack.RefreshType = IPY_GameWorld.CDBPlayerRefresh_Speed
-    sendPack.Value = speed
-    curNPC.NotifyAll(sendPack.GetBuffer(), sendPack.GetLength())
+    GameObj.NotifyObjInfoRefresh(curNPC, IPY_GameWorld.CDBPlayerRefresh_Speed, speed)
     return
 
 def UpdateNPCAttackCount(curPlayer, npcID, attackCount, maxCount=0):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCHurtManager.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCHurtManager.py
index 8e4e67d..ab672bf 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCHurtManager.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCHurtManager.py
@@ -987,26 +987,26 @@
         
     return
 
-def OnSetAssistTagPlayerID(curPlayer, value):
-    '''玩家更新了新的协助对象玩家ID
-        需要 清除本地图中玩家以非协助身份正在攻击的boss
-        
-        以协助身份攻击的通过GameServer进行清除,因为玩家可能不和协助目标同一个地图
-        比如先点了协助A玩家,还没过去的时候,又点了协助B玩家,所以需要通过GameServer清除协助目标的相关数据
-    '''
-        
-    if not value:
-        # 只处理有协助目标的情况
-        return
-    
-    playerID = curPlayer.GetPlayerID()
-    for hurtList in PyGameData.g_npcHurtDict.values():
-        if hurtList.IsNoAssistPlayer(playerID):
-            GameWorld.DebugLog("玩家开始协助其他人, 删除该boss伤血!npcID=%s" % (hurtList.npcID), playerID)
-            hurtList.DelHurtPlayer(playerID, "StartAssistBoss")
-            break
-        
-    return
+#def OnSetAssistTagPlayerID(curPlayer, value):
+#    '''玩家更新了新的协助对象玩家ID
+#        需要 清除本地图中玩家以非协助身份正在攻击的boss
+#        
+#        以协助身份攻击的通过GameServer进行清除,因为玩家可能不和协助目标同一个地图
+#        比如先点了协助A玩家,还没过去的时候,又点了协助B玩家,所以需要通过GameServer清除协助目标的相关数据
+#    '''
+#        
+#    if not value:
+#        # 只处理有协助目标的情况
+#        return
+#    
+#    playerID = curPlayer.GetPlayerID()
+#    for hurtList in PyGameData.g_npcHurtDict.values():
+#        if hurtList.IsNoAssistPlayer(playerID):
+#            GameWorld.DebugLog("玩家开始协助其他人, 删除该boss伤血!npcID=%s" % (hurtList.npcID), playerID)
+#            hurtList.DelHurtPlayer(playerID, "StartAssistBoss")
+#            break
+#        
+#    return
 
 def ClearPlayerHurtList(curNPC):
     ## 清空伤血列表
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 12eae96..dcc0065 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -175,6 +175,8 @@
 import PyMongoMain
 import PlayerTalk
 import PlayerHero
+import PlayerOnline
+import TurnAttack
 
 import datetime
 import time
@@ -509,6 +511,8 @@
         if PlayerControl.GetCrossMapID(curPlayer):
             PlayerControl.SetCrossMapID(curPlayer, 0)
         
+    PlayerOnline.OnPlayerLogin(curPlayer)
+    TurnAttack.OnPlayerLogin(curPlayer)
     SyncGuideState(curPlayer)
     
     #上线检查一次装备属性
@@ -924,6 +928,8 @@
     EventReport.WriteEvent_Entry(curPlayer, 4)
     #EventReport.EventReport(ShareDefine.Def_UserAction_FirstLogin, "", curPlayer)
     
+    PlayerControl.SetMainLevelNowInfo(curPlayer) # 初始化章节关卡
+    
     #---补满血满魔---
     GameObj.SetHP(curPlayer, GameObj.GetMaxHP(curPlayer))
     curPlayer.SetMP(curPlayer.GetMaxMP())
@@ -1268,17 +1274,22 @@
     if GameWorld.GetMap().GetMapFBType() != IPY_GameWorld.fbtNull:
         #副本地图上线切换才加无敌buff
         SkillCommon.AddBuffBySkillType_NoRefurbish(curPlayer, ChConfig.Def_SkillID_LimitSuperBuff, tick)
-            
-    #刷新玩家的视野
-    if not GameWorld.IsCrossServer() and (PlayerControl.GetCrossMapID(curPlayer) or PlayerControl.GetCustomMapID(curPlayer)):
-        GameWorld.DebugLog("===登录本服地图时,处于跨服或自定义场景状态,不刷新视野!", curPlayer.GetPlayerID())
+        
+    #卡牌改为0视野            
+    if curPlayer.GetSightLevel() != curPlayer.GetID():
         PlayerControl.SetPlayerSightLevel(curPlayer, curPlayer.GetID())
-    elif not GameWorld.IsCrossServer():
-        realmDifficulty = PlayerControl.GetMapRealmDifficulty(curPlayer)
-        if realmDifficulty:
-            GameWorld.DebugLog("===登录本服地图时,处于境界难度地图,自动设置难度! realmDifficulty=%s" % realmDifficulty, curPlayer.GetPlayerID())
-            PlayerControl.SetRealmDifficulty(curPlayer, realmDifficulty)
-            
+    if curPlayer.GetSight() != 0:
+        PlayerControl.SetSight(curPlayer, 0)
+    #刷新玩家的视野
+    #if not GameWorld.IsCrossServer() and (PlayerControl.GetCrossMapID(curPlayer) or PlayerControl.GetCustomMapID(curPlayer)):
+    #    GameWorld.DebugLog("===登录本服地图时,处于跨服或自定义场景状态,不刷新视野!", curPlayer.GetPlayerID())
+    #    PlayerControl.SetPlayerSightLevel(curPlayer, curPlayer.GetID())
+    #elif not GameWorld.IsCrossServer():
+    #    realmDifficulty = PlayerControl.GetMapRealmDifficulty(curPlayer)
+    #    if realmDifficulty:
+    #        GameWorld.DebugLog("===登录本服地图时,处于境界难度地图,自动设置难度! realmDifficulty=%s" % realmDifficulty, curPlayer.GetPlayerID())
+    #        PlayerControl.SetRealmDifficulty(curPlayer, realmDifficulty)
+    
     PlayerState.ChangePlayerSigh(curPlayer, tick)
     
     if GameWorld.IsCrossServer():
@@ -1611,16 +1622,21 @@
     #将玩家放置在这个地图上
     curPlayer.InitPos(curPlayer.GetPosX(), curPlayer.GetPosY())
     
-    #刷新自己的视野
-    if not GameWorld.IsCrossServer() and (PlayerControl.GetCrossMapID(curPlayer) or curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomScene)):
-        GameWorld.DebugLog("===本服LoadMapOK时玩家处于跨服或自定义场景状态,不设置可见!", curPlayer.GetPlayerID())
+    #卡牌改为0视野
+    if curPlayer.GetSightLevel() != curPlayer.GetID():
         PlayerControl.SetPlayerSightLevel(curPlayer, curPlayer.GetID())
-    elif not GameWorld.IsCrossServer():
-        realmDifficulty = PlayerControl.GetMapRealmDifficulty(curPlayer)
-        if realmDifficulty:
-            GameWorld.DebugLog("===本服LoadMapOK时玩家处于境界难度地图,自动设置难度!realmDifficulty=%s" % realmDifficulty, curPlayer.GetPlayerID())
-            PlayerControl.SetRealmDifficulty(curPlayer, realmDifficulty)
-            
+    if curPlayer.GetSight() != 0:
+        PlayerControl.SetSight(curPlayer, 0)
+    #刷新自己的视野
+    #if not GameWorld.IsCrossServer() and (PlayerControl.GetCrossMapID(curPlayer) or curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomScene)):
+    #    GameWorld.DebugLog("===本服LoadMapOK时玩家处于跨服或自定义场景状态,不设置可见!", curPlayer.GetPlayerID())
+    #    PlayerControl.SetPlayerSightLevel(curPlayer, curPlayer.GetID())
+    #elif not GameWorld.IsCrossServer():
+    #    realmDifficulty = PlayerControl.GetMapRealmDifficulty(curPlayer)
+    #    if realmDifficulty:
+    #        GameWorld.DebugLog("===本服LoadMapOK时玩家处于境界难度地图,自动设置难度!realmDifficulty=%s" % realmDifficulty, curPlayer.GetPlayerID())
+    #        PlayerControl.SetRealmDifficulty(curPlayer, realmDifficulty)
+    
     curPlayer.RefreshView()
     curPlayer.SetVisible(True)
         
@@ -2387,6 +2403,7 @@
     #下线了,将存储在字典中的真实XP值,设置给玩家,完成通知和存储
     #curPlayer.SetXP(curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_RecordXPValue))
     #######################################################################
+    PlayerOnline.OnPlayerLogoff(curPlayer)
     #下线逻辑
     PlayerControl.PlayerLeaveServer(curPlayer, tick)
     
@@ -4371,7 +4388,7 @@
 #    WORD        RealmDifficulty;    //境界难度 = 1000 + 所选境界等级,如境界13,则发1013
 #};
 def OnSelectRealmDifficulty(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    PlayerControl.SetRealmDifficulty(curPlayer, clientData.RealmDifficulty)
+    #curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    #PlayerControl.SetRealmDifficulty(curPlayer, clientData.RealmDifficulty)
     return
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
index 647a9d6..e839b60 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -100,6 +100,7 @@
 import PlayerTask
 import PlayerFace
 import PlayerMail
+import PlayerHero
 import ChPlayer
 import GameObj
 
@@ -2883,6 +2884,8 @@
     #轮回殿
     PlayerActLunhuidian.AddLunhuidianValue(curPlayer, PlayerActLunhuidian.AwardType_PayMoney, type_Price, price)
     if type_Price == ShareDefine.TYPE_Price_Xiantao:
+        unXiantaoCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCnt)
+        NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCnt, unXiantaoCnt + price)
         PlayerPrestigeSys.AddRealmTaskValue(curPlayer, PlayerPrestigeSys.RealmTaskType_UseXiantao, price)
     unitPrice = price if quantity == 1 else int(math.ceil(price * 1.0 / quantity)) # 单价
     #reason_name = "Unknown" if not costType else costType
@@ -4238,6 +4241,7 @@
         PlayerFamilyZhenfa.CalcZhenfaAttr(curPlayer)
         PlayerFace.CalcFaceAttr(curPlayer)
         PlayerFace.CalcFacePicAttr(curPlayer)
+        PlayerHero.CalcHeroItemAddAttr(curPlayer)
         self.RefreshAllState(isForce=True)
         GameWorld.DebugLog("End ReCalcAllState!!!", playerID)
         return
@@ -5037,7 +5041,7 @@
     def __SetAtkInterval(self):
         curPlayer = self.__Player
         
-        atkSpeed = GetAtkSpeed(curPlayer)
+        atkSpeed = GameObj.GetAtkSpeed(curPlayer)
         
         formula = IpyGameDataPY.GetFuncCfg("AtkInterval")
         atkInterval = 0 if not formula else eval(FormulaControl.GetCompileFormula("AtkInterval", formula))
@@ -5930,18 +5934,51 @@
         SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_ForbidenTalk, 0)
     return
 
+## 主线关卡过关进度值 = 章节*10000+关卡编号*100+第x波
+def GetMainLevelPassValue(curPlayer): return curPlayer.GetExAttr1()
+def SetMainLevelPassValue(curPlayer, value): curPlayer.SetExAttr1(value, False, False) # 不通知GameServer
+def SetMainLevelPassInfo(curPlayer, chapterID, levelNum, wave=0):
+    ## 设置主线关卡过关进度
+    # @param chapterID: 章节ID
+    # @param levelNum: 关卡编号
+    # @param wave: 第x波
+    value = ComMainLevelValue(chapterID, levelNum, wave)
+    SetMainLevelPassValue(curPlayer, value)
+    return value
+def GetMainLevelPassInfo(curPlayer):
+    ## 获取主线关卡过关进度信息
+    # @return: chapterID, levelNum, wave
+    return GetMainLevelValue(GetMainLevelPassValue(curPlayer))
+
+## 主线关卡当前进度值 = 章节*10000+关卡编号*100+第x波
+def GetMainLevelNowValue(curPlayer): return curPlayer.GetExAttr2()
+def SetMainLevelNowValue(curPlayer, value): curPlayer.SetExAttr2(value, False, False) # 不通知GameServer
+def SetMainLevelNowInfo(curPlayer, chapterID=1, levelNum=1, wave=1):
+    ## 设置主线关卡当前进度
+    # @param chapterID: 章节ID
+    # @param levelNum: 关卡编号
+    # @param wave: 第x波
+    value = ComMainLevelValue(chapterID, levelNum, wave)
+    SetMainLevelNowValue(curPlayer, value)
+    return value
+def GetMainLevelNowInfo(curPlayer):
+    ## 获取主线关卡当前进度信息
+    # @return: chapterID, levelNum, wave
+    return GetMainLevelValue(GetMainLevelNowValue(curPlayer))
+
+def ComMainLevelValue(chapterID, levelNum, wave=0): return chapterID * 10000 + levelNum * 100 + wave
+def GetMainLevelValue(value):
+    chapterID = value / 10000
+    levelNum = value % 10000 / 100
+    wave = value % 100
+    return chapterID, levelNum, wave
+
 ## 协助目标玩家ID
-def SetAssistTagPlayerID(curPlayer, value):
-    curPlayer.SetExAttr1(value, True, False) # 不通知GameServer
-    NPCHurtManager.OnSetAssistTagPlayerID(curPlayer, value)
-    return
-def GetAssistTagPlayerID(curPlayer): return curPlayer.GetExAttr1()
+def GetAssistTagPlayerID(curPlayer): return 0
 
 ## 队伍相关审核开关状态, joinReqCheck-入队申请是否需要审核; inviteCheck-组队邀请是否需要审核;
 def SetTeamCheckStateEx(curPlayer, joinReqCheck, inviteCheck): return SetTeamCheckState(curPlayer, joinReqCheck * 10 + inviteCheck)
-def SetTeamCheckState(curPlayer, checkState): return curPlayer.SetExAttr2(checkState, False, True)
-def GetTeamCheckState(curPlayer): return curPlayer.GetExAttr2()
-
+def SetTeamCheckState(curPlayer, checkState): return
 ## 副本功能线路ID, 这里做db存储,防止在合并地图副本中掉线重上时前端无法加载正确的场景资源,登录加载场景时机为0102包
 def SetFBFuncLineID(curPlayer, mapID, funcLineID):
     value = mapID * 1000 + funcLineID
@@ -6608,18 +6645,6 @@
 def SetSpeedValue(curPlayer, value):
     curPlayer.SetDict(ChConfig.Def_PlayerKey_SpeedValue, value)
     SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_SpeedValue, value, True) # 移动速度值暂定广播周围玩家
-
-##获取玩家攻击速度,用于计算攻击间隔
-# @param curPlayer 玩家实例
-# @return 玩家攻击速度
-def GetAtkSpeed(curPlayer):
-    return curPlayer.GetBattleValEx1()
-
-##设置玩家攻击速度,用于计算攻击间隔
-# @param curPlayer 玩家实例
-# @return None
-def SetAtkSpeed(curPlayer, value):
-    curPlayer.SetBattleValEx1(value, True)
     
 #---攻击回复血量比率----
 ## 获取玩家攻击回复血量比率
@@ -6691,11 +6716,7 @@
 ## 卓越一击伤害减免
 def GetGreatHitReducePer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_GreatHitReducePer)
 def SetGreatHitReducePer(curPlayer, value): curPlayer.SetDict(ChConfig.Def_PlayerKey_GreatHitReducePer, value)
-## 暴击伤害减免
-def GetSuperHitReduce(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_SuperHitReduce)
-def SetSuperHitReduce(curPlayer, value):
-    curPlayer.SetDict(ChConfig.Def_PlayerKey_SuperHitReduce, value)
-    SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_SuperHitReduce, value)
+
 ## 无视防御伤害减免
 def GetIgnoreDefReducePer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_IgnoreDefReducePer)
 def SetIgnoreDefReducePer(curPlayer, value): curPlayer.SetDict(ChConfig.Def_PlayerKey_IgnoreDefReducePer, value)
@@ -6709,11 +6730,7 @@
 ## 抗卓越一击概率
 def GetGreatHitRateReduce(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_GreatHitRateReduce)
 def SetGreatHitRateReduce(curPlayer, value): curPlayer.SetDict(ChConfig.Def_PlayerKey_GreatHitRateReduce, value)
-## 抗暴击概率
-def GetSuperHitRateReduce(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_SuperHitRateReduce)
-def SetSuperHitRateReduce(curPlayer, value): 
-    curPlayer.SetDict(ChConfig.Def_PlayerKey_SuperHitRateReduce, value)
-    SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_SuperHitRateReduce, value)
+
 ## 抗无视防御概率
 def GetIgnoreDefRateReduce(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_IgnoreDefRateReduce)
 def SetIgnoreDefRateReduce(curPlayer, value):
@@ -6736,18 +6753,6 @@
 def SetBossFinalHurtPer(curPlayer, value):
     curPlayer.SetDict(ChConfig.Def_PlayerKey_BossFinalHurtPer, value)
     SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_BossFinalHurtPer, value)
-    
-## 最终伤害百分比
-def GetFinalHurtPer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_FinalHurtPer)
-def SetFinalHurtPer(curPlayer, value):
-    curPlayer.SetDict(ChConfig.Def_PlayerKey_FinalHurtPer, value)
-    SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_FinalHurtPer, value)
-    
-## 最终伤害减免百分比
-def GetFinalHurtReducePer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_FinalHurtReducePer)
-def SetFinalHurtReducePer(curPlayer, value):
-    curPlayer.SetDict(ChConfig.Def_PlayerKey_FinalHurtReducePer, value)
-    SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_FinalHurtReducePer, value)
     
 ## 最终固定伤害增加
 def GetFinalHurt(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_FinalHurt)
@@ -6995,17 +7000,6 @@
 #  @return None
 def SetReduceBackHPPer(curPlayer, value):
     curPlayer.SetDict(ChConfig.Def_PlayerKey_ReduceBackHPPer, value)
-
-#---触发击晕----
-def GetFaintRate(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrFaintRate)
-def SetFaintRate(curPlayer, value):
-    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrFaintRate, value)
-    SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_FaintRate, value)
-#---击晕抵抗----
-def GetFaintDefRate(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrFaintDefRate)
-def SetFaintDefRate(curPlayer, value):
-    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrFaintDefRate, value)
-    SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_FaintDefRate, value)
     
 #---触发定身----
 def GetAtkerFreezed(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrAtkerFreezed)
@@ -7015,17 +7009,6 @@
 def GetAddAngry(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrAddAngry)
 def SetAddAngry(curPlayer, value): curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrAddAngry, value)
 
-#---连击几率----
-def GetComboRate(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrComboRate)
-def SetComboRate(curPlayer, value):
-    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrComboRate, value)
-    SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_ComboRate, value)
-#---连击伤害----
-def GetComboDamPer(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_AttrComboDamPer)
-def SetComboDamPer(curPlayer, value):
-    curPlayer.SetDict(ChConfig.Def_PlayerKey_AttrComboDamPer, value)
-    SendPropertyRefresh(curPlayer, ShareDefine.CDBPlayerRefresh_ComboDamPer, value)
-    
 #---技能攻击比例减少----
 def GetSkillAtkRateReduce(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_SkillAtkRateReduce)
 def SetSkillAtkRateReduce(curPlayer, value):
@@ -7339,6 +7322,12 @@
                 dict1[key] = aValue + value
     return
 
+def GetLordAttr(curPlayer):
+    ## 获取主公属性汇总
+    lordAttrDict = {"Atk":curPlayer.GetMaxAtk(), "Def":curPlayer.GetDef(), "MaxHP":GameObj.GetMaxHP(curPlayer), 
+                    "Hit":curPlayer.GetHit(), "Miss":curPlayer.GetMiss()}
+    return lordAttrDict
+
 #-------------------------------------------------------------------------------
 ## 设置玩家字典值, 存库
 def NomalDictSetProperty(curPlayer, key, value, dType=0):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py
index 0a0aee9..d842b7d 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py
@@ -43,8 +43,6 @@
         singleItem.SetUserAttr(ShareDefine.Def_IudetHeroAwakeLV, 0)
     if singleItem.GetUserAttr(ShareDefine.Def_IudetHeroSkin):
         singleItem.SetUserAttr(ShareDefine.Def_IudetHeroSkin, 0)
-    if singleItem.GetUserAttr(ShareDefine.Def_IudetHeroPosNum):
-        singleItem.SetUserAttr(ShareDefine.Def_IudetHeroPosNum, 0)
         
     if singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentID):
         singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentID)
@@ -56,6 +54,8 @@
         singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentWashID)
     if singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentIDAwakeRand):
         singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentIDAwakeRand)
+    if singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup):
+        singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroLineup)
         
     InitHeroTalent(singleItem)
     return
@@ -192,6 +192,21 @@
         return
     return heroItem
 
+def GetHeroLineupPosNum(heroItem, lineupID=ShareDefine.Lineup_Main):
+    ## 获取英雄所在阵型站位
+    # @param lineupID: 阵型ID,默认主阵型
+    # @return: 0-没有在该阵型;>0-在该阵型中的站位编号
+    lineupCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)
+    if not lineupCount:
+        return 0
+    for lpIndex in range(lineupCount)[::-1]:
+        lineupValue = heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
+        #阵容类型*10000+阵型类型*100+位置编号
+        if lineupValue / 10000 != lineupID:
+            continue
+        return lineupValue % 100
+    return 0
+
 #// B2 30 武将升级 #tagCSHeroLVUP
 #
 #struct    tagCSHeroLVUP
@@ -239,8 +254,9 @@
     GameWorld.DebugLog("武将升级: itemIndex=%s,heroID=%s,updHeroLV=%s" % (itemIndex, heroID, updHeroLV), playerID)
     heroItem.SetUserAttr(ShareDefine.Def_IudetHeroLV, updHeroLV)
     
-    # 刷属性,之后扩展
-    
+    # 刷属性
+    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
+        RefreshLordAttr(curPlayer)
     return
 
 def GetHeroLVMax(heroItem):
@@ -353,8 +369,9 @@
         __DoHeroStarTalentUp(item, addStar)
     heroItem.Sync_Item()
     
-    # 刷属性,之后扩展
-    
+    # 刷属性
+    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
+        RefreshLordAttr(curPlayer)
     return
 
 def __DoHeroStarTalentUp(singleItem, addLV):
@@ -496,8 +513,9 @@
     GameWorld.DebugLog("武将突破: itemIndex=%s,heroID=%s,nextBreakLV=%s" % (itemIndex, heroID, nextBreakLV), playerID)
     SetHeroBreakLV(heroItem, nextBreakLV)
     
-    # 刷属性,之后扩展
-    
+    # 刷属性
+    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
+        RefreshLordAttr(curPlayer)
     return
 
 def SetHeroBreakLV(heroItem, breakLV):
@@ -555,8 +573,9 @@
     GameWorld.DebugLog("武将觉醒: itemIndex=%s,heroID=%s,nextBreakLV=%s" % (itemIndex, heroID, nextAwakeLV), playerID)
     SetHeroAwakeLV(heroItem, nextAwakeLV)
     
-    # 刷属性,之后扩展
-    
+    # 刷属性
+    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
+        RefreshLordAttr(curPlayer)
     return
 
 def SetHeroAwakeLV(heroItem, awakeLV):
@@ -688,8 +707,9 @@
     
     heroItem.Sync_Item()
     
-    # 刷属性,之后扩展
-    
+    # 刷属性
+    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
+        RefreshLordAttr(curPlayer)
     return
 
 #// B2 35 武将洗炼 #tagCSHeroWash
@@ -825,8 +845,9 @@
     heroItem.Sync_Item()
     GameWorld.DebugLog("武将洗炼替换! itemIndex=%s,heroID=%s,washIDList=%s" % (itemIndex, heroID, washIDList))
     
-    # 刷属性,之后扩展
-    
+    # 刷属性
+    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
+        RefreshLordAttr(curPlayer)
     return
 
 #// B2 36 武将换肤 #tagCSHeroWearSkin
@@ -860,7 +881,9 @@
     heroItem.SetUserAttr(ShareDefine.Def_IudetHeroSkin, skinIndex)
     
     # 刷属性
-    
+    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
+        RefreshLordAttr(curPlayer)
+        
     return
 
 def ActiveHeroSkin(curPlayer, heroID, skinIndex, isActive=True):
@@ -875,6 +898,8 @@
                            % (heroID, skinIndex, skinState, updState), curPlayer.GetPlayerID())
     PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroSkin % heroID, updState)
     Sync_HeroInfo(curPlayer, [heroID])
+    
+    RefreshLordAttr(curPlayer)
     return
 
 #// B2 37 武将图鉴激活升级 #tagCSHeroBookUP
@@ -926,6 +951,8 @@
             PlayerControl.GiveMoney(curPlayer, moneyType, moneyValue, "HeroBookAct")
                     
     Sync_HeroInfo(curPlayer, [heroID])
+    
+    RefreshLordAttr(curPlayer)
     return
 
 def __doHeroBookStarLVUP(curPlayer, heroID, itemIndex):
@@ -946,8 +973,7 @@
     SetHeroBookStarLV(curPlayer, heroID, bookStar + 1)
     Sync_HeroInfo(curPlayer, [heroID])
     
-    # 刷属性
-    
+    RefreshLordAttr(curPlayer)
     return
 
 def __doHeroBookBreakLVUP(curPlayer, heroID, itemIndex):
@@ -968,8 +994,7 @@
     SetHeroBookBreakLV(curPlayer, heroID, bookBreakLV + 1)
     Sync_HeroInfo(curPlayer, [heroID])
     
-    # 刷属性
-    
+    RefreshLordAttr(curPlayer)
     return
 
 #// B2 38 武将锁定 #tagCSHeroLock
@@ -990,24 +1015,26 @@
     heroItem.SetIsLocked(1 if isLock else 0)
     return
 
-#// B4 12 战斗阵型保存 #tagCSHeroBattlePosSave
+#// B4 12 战斗阵容保存 #tagCSHeroLineupSave
 #
-#struct    tagCSHeroBattlePos
+#struct    tagCSHeroLineupPos
 #{
 #    WORD        ItemIndex;    //武将物品所在武将背包位置索引
 #    BYTE        PosNum;        //1~n上阵位置编号  
 #};
 #
-#struct    tagCSHeroBattlePosSave
+#struct    tagCSHeroLineupSave
 #{
 #    tagHead        Head;
-#    BYTE        FuncType;    //布阵功能类型:0-默认主阵型;其他待扩展,如某个活动的防守阵型
+#    BYTE        LineupID;        //阵容ID:1-主阵容;其他待扩展,如某个防守阵容
+#    BYTE        ShapeType;    //本阵容阵型,0为默认阵型,可扩展不同的阵型
 #    BYTE        PosCnt;
-#    tagCSHeroBattlePos    HeroPosList[PosCnt];    // 保存的阵型,只要发送最终的阵型武将位置即可
+#    tagCSHeroLineupPos    HeroPosList[PosCnt];    // 保存的阵容,只发送最终的阵容武将位置即可
 #};
-def OnHeroBattlePosSave(index, clientData, tick):
+def OnHeroLineupSave(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    funcType = clientData.FuncType
+    lineupID = clientData.LineupID
+    shapeType = clientData.ShapeType
     heroPosList = clientData.HeroPosList
     
     heroPosDict = {}
@@ -1021,40 +1048,61 @@
         indexList.append(itemIndex)
         heroPosDict[posNum] = itemIndex
         
-    # 主阵型
-    if funcType == 0:
-        MainBattlePosSave(curPlayer, heroPosDict)
-        
-    # 其他待扩展
-    elif funcType == 1:
-        pass
+    if lineupID not in ShareDefine.LineupList:
+        GameWorld.DebugLog("不存在该阵容,无法保存! lineupID=%s" % lineupID)
+        return
     
-    return
-
-def MainBattlePosSave(curPlayer, heroPosDict):
-    GameWorld.DebugLog("保留主战斗阵型: %s" % heroPosDict, curPlayer.GetPlayerID())
+    GameWorld.DebugLog("保存阵容: lineupID=%s, %s" % (lineupID, heroPosDict), curPlayer.GetPlayerID())
     curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
     # 直接重置旧阵型
+    delCount = 0
+    syncItemDict = {}
     for index in range(curPack.GetCount()):
         heroItem = curPack.GetAt(index)
         if not heroItem or heroItem.IsEmpty():
             continue
-        if not heroItem.GetUserAttr(ShareDefine.Def_IudetHeroPosNum):
+        lineupCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)
+        if not lineupCount:
             continue
         item = heroItem.GetItem()
-        item.ClearUserAttr(ShareDefine.Def_IudetHeroPosNum)
-        
+        for lpIndex in range(lineupCount)[::-1]:
+            lineupValue = item.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
+            #阵容类型*10000+阵型类型*100+位置编号
+            if lineupValue / 10000 != lineupID:
+                continue
+            item.DelUserAttr(ShareDefine.Def_IudetHeroLineup, lineupValue)
+            delCount += 1
+            if delCount >= ShareDefine.LineupObjMax:
+                break
+            syncItemDict[index] = heroItem
+            
     # 更新新阵型
+    heroIDList = []
     for posNum, itemIndex in heroPosDict.items():
         if itemIndex < 0 or itemIndex >= curPack.GetCount():
             continue
         heroItem = curPack.GetAt(itemIndex)
         if not heroItem or heroItem.IsEmpty():
             continue
+        itemID = heroItem.GetItemTypeID()
+        if itemID in heroIDList:
+            GameWorld.DebugLog("同个武将只能上阵一个! itemIndex=%s,itemID=%s" % (itemIndex, itemID))
+            continue
+        heroIDList.append(itemID)
         item = heroItem.GetItem()
-        item.SetUserAttr(ShareDefine.Def_IudetHeroPosNum, posNum)
+        lineupValue = lineupID * 10000 + shapeType * 100 + posNum
+        item.AddUserAttr(ShareDefine.Def_IudetHeroLineup, lineupValue)
+        if itemIndex not in syncItemDict:
+            syncItemDict[itemIndex] = heroItem
         
-    ResetHeroPack(curPlayer)
+    # 主阵容修改时重整背包
+    if lineupID == ShareDefine.Lineup_Main:
+        ResetHeroPack(curPlayer)
+    else:
+        for syncItem in syncItemDict.values():
+            syncItem.Sync_Item()
+            
+    RefreshLordAttr(curPlayer)
     return
 
 def ResetHeroPack(curPlayer):
@@ -1063,6 +1111,24 @@
     ItemControler.ResetItem(curPlayer, ShareDefine.rptHero, 0, 0, tick)
     return
     
+def RefreshLordAttr(curPlayer):
+    ## 刷新主公属性
+    CalcHeroItemAddAttr(curPlayer)
+    return
+
+def CalcHeroItemAddAttr(curPlayer):
+    #allAttrListPet = [{} for _ in range(4)]
+    return
+
+def RefreshLineupHeroAttr(curPlayer):
+    ## 刷新阵容武将属性
+    
+    # 计算阵容总战力 = 角色总战力为主阵容战力,需同步计算不同阵容战力
+    return
+
+def CaclHeroCardAttr():
+    return
+
 def Sync_HeroInfo(curPlayer, heroIDList=None):
     if heroIDList != None:
         syncHeroIDList = heroIDList
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py
new file mode 100644
index 0000000..ef64c77
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py
@@ -0,0 +1,118 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.PlayerOnline
+#
+# @todo:在线玩家管理
+# @author hxp
+# @date 2025-07-02
+# @version 1.0
+#
+# 详细描述: 在线玩家管理,用于管理在线玩家、准在线玩家的临时数据
+#         准在线玩家 - 实际不在线,x分钟内离线的玩家,用于支持断线重连,短时间内临时数据可持续
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-07-02 17:30"""
+#-------------------------------------------------------------------------------
+
+import TurnAttack
+import PyGameData
+
+import time
+
+class OnlinePlayer():
+    ## 准在线玩家临时数据
+    
+    def __init__(self, playerID):
+        self.playerID = playerID
+        self.mainFight = TurnAttack.MainFight(playerID)
+        return
+    
+    def OnPlayerLogin(self, curPlayer):
+        self.mainFight.playerLogin(curPlayer)
+        return
+    
+    def OnPlayerOffline(self, curPlayer):
+        self.mainFight.playerOffline(curPlayer)
+        return
+    
+    def OnClear(self):
+        self.mainFight.clear()
+        return
+    
+class OnlineMgr():
+    ## 准在线玩家管理
+    
+    def __init__(self):
+        self.__onlinePlayerDict = {} # 准在线玩家临时数据字典 {playerID:OnlinePlayer, ...}
+        self.__offlinePlayerTimeDict = {} # 准在线玩家临时离线时间戳 {playerID:离线时间戳, ...}
+        return
+    
+    def GetOnlinePlayer(self, curPlayer):
+        olPlayer = None
+        playerID = curPlayer.GetPlayerID()
+        if playerID in self.__onlinePlayerDict:
+            olPlayer = self.__onlinePlayerDict[playerID]
+        else:
+            olPlayer = OnlinePlayer(playerID)
+            self.__onlinePlayerDict[playerID] = olPlayer
+            
+        return olPlayer
+    
+    def SetPlayerOnline(self, curPlayer):
+        ## 设置玩家在线
+        playerID = curPlayer.GetPlayerID()
+        self.__offlinePlayerTimeDict.pop(playerID, None)
+        if playerID not in self.__onlinePlayerDict:
+            olPlayer = OnlinePlayer(playerID)
+            self.__onlinePlayerDict[playerID] = olPlayer
+        else:
+            olPlayer = self.__onlinePlayerDict[playerID]
+        olPlayer.OnPlayerLogin(curPlayer)
+        return
+    
+    def SetPlayerOffline(self, curPlayer):
+        ## 设置玩家离线
+        playerID = curPlayer.GetPlayerID()
+        if playerID not in self.__onlinePlayerDict:
+            return
+        olPlayer = self.__onlinePlayerDict[playerID]
+        olPlayer.OnPlayerOffline(curPlayer)
+        self.__offlinePlayerTimeDict[playerID] = int(time.time())
+        return
+    
+    def ProcessOffline(self):
+        ## 定时处理离线玩家
+        curTime = int(time.time())
+        offlineTimes = 5 * 60 # 5分钟后清除数据
+        for playerID, offlineTime in self.__offlinePlayerTimeDict.items():
+            if curTime - offlineTime < offlineTimes:
+                continue
+            self.__offlinePlayerTimeDict.pop(playerID, None)
+            if playerID not in self.__onlinePlayerDict:
+                continue
+            olPlayer = self.__onlinePlayerDict.pop(playerID, None)
+            olPlayer.OnClear()
+        return
+    
+def GetOnlineMgr():
+    mgr = None
+    if PyGameData.g_onlineMgr:
+        mgr = PyGameData.g_onlineMgr
+    else:
+        mgr = OnlineMgr()
+        PyGameData.g_onlineMgr = mgr
+    return mgr
+    
+def OnPlayerLogin(curPlayer):
+    GetOnlineMgr().SetPlayerOnline(curPlayer)
+    return
+
+def OnPlayerLogoff(curPlayer):
+    GetOnlineMgr().SetPlayerOffline(curPlayer)
+    return
+
+def OnMinute():
+    GetOnlineMgr().ProcessOffline()
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_TurnFight.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_TurnFight.py
deleted file mode 100644
index e7db6c9..0000000
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_TurnFight.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/python
-# -*- coding: GBK -*-
-#-------------------------------------------------------------------------------
-#
-##@package Player.RemoteQuery.GY_Query_TurnFight
-#
-# @todo:回合制
-# @author hxp
-# @date 2024-04-10
-# @version 1.0
-#
-# 详细描述: 回合制
-#
-#-------------------------------------------------------------------------------
-#"""Version = 2024-04-10 14:00"""
-#-------------------------------------------------------------------------------
-
-import GameWorld
-import TurnAttack
-
-#---------------------------------------------------------------------
-#逻辑实现
-## 请求逻辑
-#  @param query_Type 请求类型
-#  @param query_ID 请求的玩家ID
-#  @param packCMDList 发包命令 [ ]
-#  @param tick 当前时间
-#  @return "True" or "False" or ""
-#  @remarks 函数详细说明.
-def DoLogic(query_Type, query_ID, packCMDList, tick):
-    return ""
-
-#---------------------------------------------------------------------
-#执行结果
-## 执行结果
-#  @param curPlayer 发出请求的玩家
-#  @param callFunName 功能名称
-#  @param funResult 查询的结果
-#  @param tick 当前时间
-#  @return None
-#  @remarks 函数详细说明.
-def DoResult(curPlayer, callFunName, funResult, tick):
-    GameWorld.DebugLog("GY_Query_TurnFight DoResult %s" % str(funResult), curPlayer.GetPlayerID())
-    if funResult != "":
-        TurnAttack.GameServer_TurnFight_DoResult(curPlayer, eval(funResult), tick)
-    return
-
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
index a4e7f38..dc9a2e1 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -28,6 +28,10 @@
 
 g_dbPlayerIDMap = {} # 本服DBPlayer玩家表ID映射关系 {playerID:accID, ...}
 
+g_onlineMgr = None
+
+g_turnFightMgr = None
+
 g_mapIDTxtInfo = {} # MapID.txt 加载的信息
 g_realmDiffPlayerDict = {} # 境界难度玩家信息 {realm:[playerID, ...], ...}
 g_realmDiffNPCRefresh = {} # {(lineID, realm):{refreshID:tagNPCRefresh, ...}}
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
index 42cde85..4287272 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -774,6 +774,18 @@
 #头像框防御加成
 Def_Effect_FacePicDefPer = 181 # 头像框防御加成
 
+Def_Effect_ComboDefRate = 210 # 抗连击概率
+Def_Effect_AtkBackRate = 211 # 反击概率
+Def_Effect_AtkBackDefRate = 212 # 抗反击概率
+Def_Effect_SuckHPPer = 213 # 吸血比率
+Def_Effect_SuckHPDefPer = 214 # 抗吸血比率
+Def_Effect_CurePer = 215 # 强化治疗
+Def_Effect_CureDefPer = 216 # 弱化治疗
+Def_Effect_PetStrengthenPer = 217 # 强化灵兽
+Def_Effect_PetWeakenPer = 218 # 弱化灵兽
+Def_Effect_SuperHitHurtPer = 219 # 强化暴伤
+Def_Effect_SuperHitHurtDefPer = 220 # 弱化暴伤
+
 #增加%d物理伤害值,其中a值为伤害值
 Def_Effect_AddAtk = 1005
 #增加%d魔法伤害值,其中a值为伤害值
@@ -1761,6 +1773,7 @@
 Def_IudetHeroTalentWashLock = 75  # 英雄天赋洗炼锁定索引列表
 Def_IudetHeroTalentWashID = 77  # 英雄天赋洗炼随机ID列表
 Def_IudetHeroTalentIDAwakeRand = 79  # 英雄觉醒时随机天赋选项ID列表
+Def_IudetHeroLineup = 81 # 所在阵容信息列表 [阵容类型*10000+阵型类型*100+位置编号, ...]
 
 Def_IudetItemColor = 16  # 物品颜色,如果该值没有就取物品
 Def_IudetItemCount = 18  # 物品个数,支持20亿,目前仅特殊转化物品会用到
@@ -1782,7 +1795,6 @@
 Def_IudetHeroBreakLV = 74 # 英雄突破等级
 Def_IudetHeroAwakeLV = 76 # 英雄觉醒等级
 Def_IudetHeroSkin = 78 # 英雄使用的皮肤索引
-Def_IudetHeroPosNum = 80 # 主阵型上阵位置
 
 # 200~300 宠物数据用
 Def_IudetPet_NPCID = 200  # npcID
@@ -1796,6 +1808,14 @@
 Def_IudetHorsePetSkinIndex = 210  # 骑宠觉醒外观索引
 # ----------------------------------------------------
 
+LineupObjMax = 6 # 阵容最大上阵武将数
+
+# 阵容定义
+LineupList = (
+Lineup_Main, # 主阵容
+Lineup_Arena, # 竞技场防守阵容
+) = range(1, 1 + 2)
+
 # 宠物物品数据状态
 Def_PetStateList = (
 Def_PetState_Null, # 无
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/EffGetSet.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/EffGetSet.py
index ee9deb3..fb3c2e9 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/EffGetSet.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/EffGetSet.py
@@ -45,12 +45,12 @@
    #[lambda curObj:curObj.GetMAtkMin(), lambda curObj, value:curObj.SetMAtkMin(value), IPY_PlayerDefine.CDBPlayerRefresh_MAtkMin, 0, 0],  # 最小魔攻
    #[lambda curObj:curObj.GetMAtkMax(), lambda curObj, value:curObj.SetMAtkMax(value), IPY_PlayerDefine.CDBPlayerRefresh_MAtkMax, 0, 0],  # 最大魔攻
    [lambda curObj:PlayerControl.GetSpeedNotBuff(curObj), lambda curObj, value:PlayerControl.SetSpeedNotBuff(curObj, value), 0, 0, 0],  # 移动速度
-   [lambda curObj:PlayerControl.GetAtkSpeed(curObj), lambda curObj, value:PlayerControl.SetAtkSpeed(curObj, value), IPY_PlayerDefine.CDBPlayerRefresh_BattleValEx1, 1, 1],  # 攻击速度
+   [lambda curObj:GameObj.GetAtkSpeed(curObj), lambda curObj, value:GameObj.SetAtkSpeed(curObj, value), IPY_PlayerDefine.CDBPlayerRefresh_BattleValEx1, 1, 1],  # 攻击速度
    
    [lambda curObj:curObj.GetSuperHitRate(), lambda curObj, value:curObj.SetSuperHitRate(value), IPY_PlayerDefine.CDBPlayerRefresh_SuperHitRate, 1, 0],     # 暴击概率
    [lambda curObj:curObj.GetSuperHit(), lambda curObj, value:curObj.SetSuperHit(value), IPY_PlayerDefine.CDBPlayerRefresh_SuperHit, 1, 0],                 # 暴击伤害固定值
-   [lambda curObj:PlayerControl.GetSuperHitRateReduce(curObj), lambda curObj, value:PlayerControl.SetSuperHitRateReduce(curObj, value), ShareDefine.CDBPlayerRefresh_SuperHitRateReduce, 1, 0],# 暴击概率抗性
-   [lambda curObj:PlayerControl.GetSuperHitReduce(curObj), lambda curObj, value:PlayerControl.SetSuperHitReduce(curObj, value), ShareDefine.CDBPlayerRefresh_SuperHitReduce, 1, 0],  # 暴击伤害抗性固定值
+   [lambda curObj:GameObj.GetSuperHitRateReduce(curObj), lambda curObj, value:GameObj.SetSuperHitRateReduce(curObj, value), ShareDefine.CDBPlayerRefresh_SuperHitRateReduce, 1, 0],# 暴击概率抗性
+   [lambda curObj:GameObj.GetSuperHitReduce(curObj), lambda curObj, value:GameObj.SetSuperHitReduce(curObj, value), ShareDefine.CDBPlayerRefresh_SuperHitReduce, 1, 0],  # 暴击伤害抗性固定值
    
    [lambda curObj:curObj.GetGreatHitRate(), lambda curObj, value:curObj.SetGreatHitRate(value), 0, 0, 0],                                        # 卓越一击几率
    [lambda curObj:curObj.GetGreatHitVal(), lambda curObj, value:curObj.SetGreatHitVal(value), 0, 0, 0],                                          # 卓越一击伤害倍率
@@ -94,12 +94,12 @@
    [lambda curObj:curObj.GetDamageBackRate(), lambda curObj, value:curObj.SetDamageBackRate(value), IPY_PlayerDefine.CDBPlayerRefresh_DamageBackRate, 1, 0],                                    # 反伤百分比
    [lambda curObj:PlayerControl.GetDamChanceDef(curObj), lambda curObj, value:PlayerControl.SetDamChanceDef(curObj, value), ShareDefine.CDBPlayerRefresh_DamChanceDef, 1, 0],            # 20%的概率抵御伤害比率
    [lambda curObj:PlayerControl.GetShieldMPCostRate(curObj), lambda curObj, value:PlayerControl.SetShieldMPCostRate(curObj, value), 0, 0, 0],    # 魔法盾伤害吸收蓝耗比率
-   [lambda curObj:PlayerControl.GetFaintRate(curObj), lambda curObj, value:PlayerControl.SetFaintRate(curObj, value), ShareDefine.CDBPlayerRefresh_FaintRate, 1, 0],                  # 触发击晕
-   [lambda curObj:PlayerControl.GetFaintDefRate(curObj), lambda curObj, value:PlayerControl.SetFaintDefRate(curObj, value), ShareDefine.CDBPlayerRefresh_FaintDefRate, 1, 0],            # 击晕抵抗
+   [lambda curObj:GameObj.GetFaintRate(curObj), lambda curObj, value:GameObj.SetFaintRate(curObj, value), ShareDefine.CDBPlayerRefresh_FaintRate, 1, 0],                  # 触发击晕
+   [lambda curObj:GameObj.GetFaintDefRate(curObj), lambda curObj, value:GameObj.SetFaintDefRate(curObj, value), ShareDefine.CDBPlayerRefresh_FaintDefRate, 1, 0],            # 击晕抵抗
    [lambda curObj:PlayerControl.GetAtkerFreezed(curObj), lambda curObj, value:PlayerControl.SetAtkerFreezed(curObj, value), 0, 0, 0],            # 触发定身
    [lambda curObj:PlayerControl.GetAddAngry(curObj), lambda curObj, value:PlayerControl.SetAddAngry(curObj, value), 0, 0, 0],                    # 攻击增加额外仇恨
-   [lambda curObj:PlayerControl.GetComboRate(curObj), lambda curObj, value:PlayerControl.SetComboRate(curObj, value), ShareDefine.CDBPlayerRefresh_ComboRate, 0, 0],                  # 连击几率
-   [lambda curObj:PlayerControl.GetComboDamPer(curObj), lambda curObj, value:PlayerControl.SetComboDamPer(curObj, value), ShareDefine.CDBPlayerRefresh_ComboDamPer, 0, 0],              # 连击伤害
+   [lambda curObj:GameObj.GetComboRate(curObj), lambda curObj, value:GameObj.SetComboRate(curObj, value), ShareDefine.CDBPlayerRefresh_ComboRate, 0, 0],                  # 连击几率
+   [lambda curObj:GameObj.GetComboDamPer(curObj), lambda curObj, value:GameObj.SetComboDamPer(curObj, value), ShareDefine.CDBPlayerRefresh_ComboDamPer, 0, 0],              # 连击伤害
 
    [lambda curObj:curObj.GetHPRestorePer(), lambda curObj, value:curObj.SetHPRestorePer(value), IPY_PlayerDefine.CDBPlayerRefresh_HPRestorePer, 1, 0],                                    # 自动回复生命
    [lambda curObj:curObj.GetKillBackHP(), lambda curObj, value:curObj.SetKillBackHP(value), 0, 0, 0],                                        # 击杀回血
@@ -132,7 +132,7 @@
    [lambda curObj:PlayerControl.GetJobCAtkReducePer(curObj), lambda curObj, value:PlayerControl.SetJobCAtkReducePer(curObj, value), 0, 0, 0],    # 弓手攻击伤害减免
    
    [lambda curObj:PlayerControl.GetCommMapExpRate(curObj), lambda curObj, value:PlayerControl.SetCommMapExpRate(curObj, value), 0, 0, 0],    # 常规地图经验倍率加成
-   [lambda curObj:PlayerControl.GetFinalHurtPer(curObj), lambda curObj, value:PlayerControl.SetFinalHurtPer(curObj, value), ShareDefine.CDBPlayerRefresh_FinalHurtPer, 1, 0],        # 最终伤害百分比
+   [lambda curObj:GameObj.GetFinalHurtPer(curObj), lambda curObj, value:GameObj.SetFinalHurtPer(curObj, value), ShareDefine.CDBPlayerRefresh_FinalHurtPer, 1, 0],        # 最终伤害百分比
    [lambda curObj:PlayerControl.GetFuhaoHitRate(curObj), lambda curObj, value:PlayerControl.SetFuhaoHitRate(curObj, value), 0, 0, 0],        # 富豪一击概率
    [lambda curObj:PlayerControl.GetBossIDHurt(curObj), lambda curObj, value:PlayerControl.SetBossIDHurt(curObj, value), 0, 0, 0],            # 对指定boss伤害加成固定值
    [lambda curObj:PlayerControl.GetBossIDHurtAddPer(curObj), lambda curObj, value:PlayerControl.SetBossIDHurtAddPer(curObj, value), 0, 0, 0],# 对指定boss伤害加成倍率
@@ -168,7 +168,7 @@
    [lambda curObj:PlayerControl.GetNormalHurtPer(curObj), lambda curObj, value:PlayerControl.SetNormalHurtPer(curObj, value), ShareDefine.CDBPlayerRefresh_NormalHurtPer, 1, 0],  # 属性普通攻击加成
    [lambda curObj:PlayerControl.GetFabaoHurt(curObj), lambda curObj, value:PlayerControl.SetFabaoHurt(curObj, value), ShareDefine.CDBPlayerRefresh_FabaoHurt, 1, 0],  # 属性法宝技能增伤
    [lambda curObj:PlayerControl.GetFabaoHurtPer(curObj), lambda curObj, value:PlayerControl.SetFabaoHurtPer(curObj, value), ShareDefine.CDBPlayerRefresh_FabaoHurtPer, 1, 0],  # 属性法宝技能加成
-   [lambda curObj:PlayerControl.GetFinalHurtReducePer(curObj), lambda curObj, value:PlayerControl.SetFinalHurtReducePer(curObj, value), ShareDefine.CDBPlayerRefresh_FinalHurtReducePer, 1, 0],      # 最终伤害减少百分比
+   [lambda curObj:GameObj.GetFinalHurtReducePer(curObj), lambda curObj, value:GameObj.SetFinalHurtReducePer(curObj, value), ShareDefine.CDBPlayerRefresh_FinalHurtReducePer, 1, 0],      # 最终伤害减少百分比
    [lambda curObj:PlayerControl.GetLostYinjiTime(curObj), lambda curObj, value:PlayerControl.SetLostYinjiTime(curObj, value), ShareDefine.CDBPlayerRefresh_YinjiTime, 1, 0],    # 每X秒自动消失一个印记 毫秒
    [lambda curObj:PlayerControl.GetTheFBSkillsCD(curObj), lambda curObj, value:PlayerControl.SetTheFBSkillsCD(curObj, value), 0, 0, 0],    # 减少指定技能组CD XX%
    [lambda curObj:PlayerControl.GetBurnValue(curObj), lambda curObj, value:PlayerControl.SetBurnValue(curObj, value), 0, 0, 0],    # 灼烧固定伤害
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py
index 50c5d8a..25f8750 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py
@@ -34,6 +34,7 @@
 import GameObj
 import PassiveBuffEffMng
 import IpyGameDataPY
+import TurnAttack
 #---------------------------------------------------------------------
 
 #---------------------------------------------------------------------
@@ -573,7 +574,7 @@
     lvSummonNPC = curPlayer.GetLV()
     summonNPC.SetLV(lvSummonNPC)
     summonNPC.SetCountry(curPlayer.GetCountry())
-    summonNPC.SetDict(ChConfig.Def_NpcDictKey_CampType, curPlayer.GetFaction())
+    GameObj.SetFaction(summonNPC, curPlayer.GetFaction())
     summonNPC.GetNPCAngry().Init(ChConfig.Def_SummonNPC_Angry_Count)
     #设置召唤兽属性
     SetSummonNPCProperty(curPlayer, summonNPC, curSkill)
@@ -1225,21 +1226,17 @@
 #  @param addValue 添加值
 #  @return None
 #  @remarks 函数详细说明.
-def SkillAddHP(curObj, skillTypeID, addValue, isNotify=True):
+def SkillAddHP(curObj, skillID, addValue, isNotify=True, srcObj=None):
+    if not srcObj:
+        srcObj = curObj
     curObjType = curObj.GetGameObjType()
     
-    #=======================================================================
-    # if curObj.GetMapID() in ChConfig.CanNotRecoverMapIDList and skillTypeID not in ChConfig.ForceRecoverSkillList:
-    #    GameWorld.DebugLog("该地图无法使用技能恢复血量!")
-    #    return
-    #=======================================================================
     if GameObj.GetPyPlayerState(curObj, ChConfig.Def_PlayerState_LimitAddHP):
         #GameWorld.DebugLog("当前禁疗状态下,无法恢复!")
         return
-
     
     if addValue <= 0:
-        GameWorld.Log('###技能回复血量异常,数值错误 = %s,技能类型ID = %s' % (addValue , skillTypeID))
+        GameWorld.Log('###技能回复血量异常,数值错误 = %s,技能ID = %s' % (addValue , skillID))
         return
     
     curHP = GameObj.GetHP(curObj)
@@ -1253,23 +1250,26 @@
         return
     
     # 治疗加成
-    curePer = PlayerControl.GetCurePer(curObj) if curObjType == IPY_GameWorld.gotPlayer else ChConfig.Def_MaxRateValue 
-    addValue = int(addValue*curePer*1.0/ChConfig.Def_MaxRateValue)
+    #curePer = PlayerControl.GetCurePer(curObj) if curObjType == IPY_GameWorld.gotPlayer else ChConfig.Def_MaxRateValue 
+    #addValue = int(addValue*curePer*1.0/ChConfig.Def_MaxRateValue)
     
     maxHP = GameObj.GetMaxHP(curObj)
     
     #血已最大值,不再恢复,跳出
-    if curHP == maxHP:
+    if curHP >= maxHP:
+        GameWorld.DebugLog("满血不用回血,只通知回血表现!")
         #广播加血类型
         if isNotify:
-            AttackCommon.ChangeHPView(curObj, curObj, skillTypeID, addValue, ChConfig.Def_HurtTYpe_Recovery)
-        return
+            AttackCommon.ChangeHPView(curObj, srcObj, skillID, addValue, ChConfig.Def_HurtTYpe_Recovery)
+        return addValue
     
     remainHP = min(curHP + addValue, maxHP)
+    cureHP = remainHP - curHP # 实际治疗量
+    TurnAttack.AddTurnObjCureHP(curObj, srcObj, addValue, cureHP, skillID)
     
     #---玩家处理---
     if curObjType == IPY_GameWorld.gotPlayer:
-        GameObj.SetHP(curObj, remainHP, not isNotify)
+        GameObj.SetHP(curObj, remainHP, False) # 先不通知
         FBLogic.OnFBAddHP(curObj, addValue)
     
     #---NPC处理---
@@ -1279,15 +1279,12 @@
             PetControl.SetPetHP(curObj, remainHP)
         #普通NPC回血
         else:
-            GameObj.SetHP(curObj, remainHP)
-            if not isNotify:
-                #已广播的不重复
-                curObj.Notify_HP()
-           
+            GameObj.SetHP(curObj, remainHP, False) # 先不通知
+            
     #广播加血类型
     if isNotify:
-        AttackCommon.ChangeHPView(curObj, curObj, skillTypeID, addValue, ChConfig.Def_HurtTYpe_Recovery)
-            
+        AttackCommon.ChangeHPView(curObj, srcObj, skillID, addValue, ChConfig.Def_HurtTYpe_Recovery)
+        
     return
 
 ## 直接扣血不走公式
@@ -1355,6 +1352,8 @@
     else:
         # 已广播的不重复
         GameObj.SetHP(curObj, remainHP, not view)
+        
+    lostHP = curObjHP_BeforeAttack - GameObj.GetHP(curObj) # 实际掉血量
     
     AttackCommon.WriteHurtLog(buffOwner, curObj, curSkill, lostValue, hurtType, "持续掉血")
     if view:
@@ -1408,10 +1407,12 @@
         elif curObjType == IPY_GameWorld.gotNPC:
             AttackCommon.NPCAddObjInHurtList(attackerOwner, curObj, curObjHP_BeforeAttack, lostValue)
             
+    TurnAttack.AddTurnObjHurtValue(buffOwner, curObj, hurtType, lostValue, lostHP, curSkill)
+    
     #统一调用攻击结束动作
     if isDoAttackResult:
         BaseAttack.DoLogic_AttackResult(buffOwner, curObj, None, tick)
-    return
+    return lostHP
 
 ## 检查增加淬毒buff
 #  @param skillTypeID 使用的技能typeID
@@ -1968,7 +1969,15 @@
     if not curSkill:
         return ChConfig.Def_BattleRelationType_Comm
     #0通哟  1 PVP类型  2PVE类型  
-    return curSkill.GetHurtType()
+    return curSkill.GetHurtType() % 10
+
+def isXPSkill(curSkill):
+    ## 是否xp怒气技能
+    return curSkill and curSkill.GetXP() > 0
+
+def isNormalAtkSkill(curSkill):
+    ## 是否普攻技能,区别与无技能的普通A一下,该普攻同样可以有各种技能效果,只是他属于普攻
+    return curSkill and curSkill.GetFuncType() == ChConfig.Def_SkillFuncType_NormalAttack
 
 ## 检查技能是否为被动技能, 用于控制不可释放技能
 def isPassiveSkill(curSkill):
@@ -2057,34 +2066,7 @@
 #  @param useSkill 使用的技能
 #  @return 基础治疗值
 #  @remarks 函数详细说明.
-def GetCureBaseValue(attacker, useSkill):
-    return GetAttackerHurtValueByAtkType(attacker, AttackCommon.GetBattleType(attacker, useSkill))
-
-## 获得基础伤害值(目前用于持续性技能的计算)
-#  @param attacker 攻击者
-#  @param useSkill 使用的技能
-#  @return 基础伤害值
-#  @remarks 获得基础伤害值
-def GetHurtBaseValue(attacker, useSkill):
-    return GetAttackerHurtValueByAtkType(attacker, AttackCommon.GetBattleType(attacker, useSkill))
-
-#---------------------------------------------------------------------
-## 通过攻击类型获得攻击方伤害值
-#  @param attacker 攻击者
-#  @param atkType 攻击类型(物理攻击、摩法攻击)
-#  @return 攻击方伤害值
-#  @remarks 通过攻击类型获得攻击方伤害值
-def GetAttackerHurtValueByAtkType(attacker, atkType):
-    
-    return (attacker.GetMinAtk() + random.random() * (attacker.GetMaxAtk() - attacker.GetMinAtk()))
-    #===========================================================================
-    # if atkType == IPY_GameWorld.ghtPhy:
-    #    #(最小攻击 + rand()*( 最大攻击伤害-最小攻击伤害 )
-    #    return (attacker.GetMinAtk() + random.random() * (attacker.GetMaxAtk() - attacker.GetMinAtk()))
-    # 
-    # #(最小剑气伤害 + rand()*( 最大剑气伤害-最小剑气伤害 )
-    # return (attacker.GetMAtkMin() + random.random() * (attacker.GetMAtkMax() - attacker.GetMAtkMin()))
-    #===========================================================================
+def GetCureBaseValue(attacker, useSkill): return attacker.GetMaxAtk()
 
 #---------------------------------------------------------------------
 ## 获得某技能管理器是否有特定技能
@@ -2202,19 +2184,6 @@
     curePercent = 1.0     #治疗加成值
     cureBaseValue = 0     #治疗基础值
     
-    #passiveSkill = GetSkillFromOtherSkillByEffectID(userObj, curSkill, ChConfig.Def_Skill_Effect_PassiveSkillID)
-    
-    #该被动技能已学, 则处理被动影响的效果
-    #===========================================================================
-    # if passiveSkill != None and isPassiveSkill(passiveSkill):
-    #    cureEffect = GetSkillEffectByEffectID(passiveSkill, ChConfig.Def_Skill_Effect_CureUpper)
-    # 
-    #    #计算治疗加成,按几率触发
-    #    if GameWorld.CanHappen(passiveSkill.GetHappenRate(), ChConfig.Def_MaxRateValue) \
-    #    and cureEffect != None:
-    #        curePercent += cureEffect.GetEffectValue(0) / float(ChConfig.Def_MaxRateValue)
-    #===========================================================================
-    
     #特殊技能的附加值
     addExValue = 0 
     
@@ -2233,27 +2202,46 @@
         cureBaseValue = GameObj.GetLastHurtValue(userObj)
     elif cureType == ChConfig.Def_Cure_TagMaxHP:
         cureBaseValue = 0 if not tagObj else GameObj.GetMaxHP(tagObj)
-        
-        
-    #这边写死了效果1,基本已经定型
-    #获得技能的计算参数值
-    if cureType == ChConfig.Def_Cure_PHY:
-        # 根据敏捷,力量差值及效果系数计算恢复比例
-        skillPer = max(0, userObj.GetPHY() - userObj.GetSTR()) / float(curSkill.GetEffect(0).GetEffectValue(0))
-        GameWorld.DebugLog("英勇复苏: 敏=%s,力=%s,skillPer=%s" % (userObj.GetPHY(), userObj.GetSTR(), skillPer))
-    else: 
-        skillPer = curSkill.GetEffect(0).GetEffectValue(0) / float(ChConfig.Def_MaxRateValue)
-    #技能附加
+    #elif cureType == ChConfig.Def_Cure_TagAtk:
+    #    cureBaseValue = 0 if not tagObj else GetCureBaseValue(tagObj, curSkill)
+    #elif cureType == ChConfig.Def_Cure_LostHP:
+    #    cureBaseValue = max(0, GameObj.GetMaxHP(userObj) - GameObj.GetHP(userObj))
+    #elif cureType == ChConfig.Def_Cure_BeHurtValue:
+    #    cureBaseValue = GameObj.GetLastBeHurtValue(userObj)
+    
+    skillPer = curSkill.GetEffect(0).GetEffectValue(0)
+    #技能附加固定值
     skillValue = curSkill.GetEffect(0).GetEffectValue(1)
     
-    skillPer += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(userObj, None, curSkill, ChConfig.TriggerType_AddHP)/float(ChConfig.Def_MaxRateValue)
+    skillPer += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(userObj, None, curSkill, ChConfig.TriggerType_AddHP)
+    skillPer /= float(ChConfig.Def_MaxRateValue)
+    
+    # 回合制
+    curePer = 0 # 治疗加成
+    cureDefPer = 0 # 敌方的弱化治疗
+    angerOverflow = 0 # 怒气溢出值
+    if userObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
+        if isXPSkill(curSkill):
+            angerOverflow = max(GameObj.GetXP(userObj) - IpyGameDataPY.GetFuncCfg("AngerXP", 2), 0)
+        #enemyObj = TurnAttack.GetEnemyObj(userObj)
+        #curePer += GameObj.GetCurePer(userObj)
+        #if enemyObj:
+        #    cureDefPer += GameObj.GetCureDefPer(enemyObj)
+        
+    curePer /= float(ChConfig.Def_MaxRateValue)
+    cureDefPer /= float(ChConfig.Def_MaxRateValue)
+    
+    baseValue = max(0, cureBaseValue) # 防止基值被弱化为负值,在恢复比例也是负值的情况下负负得正导致可以恢复血量
+    
     #公式计算治疗值 
-    cureHP = int((cureBaseValue * skillPer + skillValue + addExValue) * curePercent)
+    cureHP = eval(IpyGameDataPY.GetFuncCompileCfg("CureFormula", 1))
+    #cureHP = (cureHP + skillValue + addExValue) * curePercent # 策划没有要求,但是支持的,先屏蔽
     if not largeNum:
         cureHP = min(cureHP, ChConfig.Def_UpperLimit_DWord)
+    cureHP = max(1, int(cureHP)) # 保底1点
     
-    #GameWorld.DebugLog("获取治疗值(%s):cureType=%s,cureBaseValue=%s,skillPer=%s,skillValue=%s" 
-    #                   % (cureHP, cureType, cureBaseValue, skillPer, skillValue))
+    GameWorld.DebugLog("获取治疗值(%s):skillID=%s,cureType=%s,baseValue=%s,skillPer=%s,curePer=%s,cureDefPer=%s,angerOverflow=%s" 
+                       % (cureHP, curSkill.GetSkillID(), cureType, baseValue, skillPer, curePer, cureDefPer, angerOverflow))
     return cureHP
 
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_18.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_18.py
index 6d19178..dc74ab0 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_18.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_18.py
@@ -71,7 +71,7 @@
         addValue = SkillCommon.GetCureHP(attacker, cureObj, curSkill, cureType, largeNum=True)
         #GameWorld.DebugLog("加血: curHP=%s,maxHP=%s,addHP=%s" % (GameObj.GetHP(obj), GameObj.GetMaxHP(obj), addValue))
         #总量 最大生命值的百分比 回血 附加多少 
-        SkillCommon.SkillAddHP(cureObj, curSkill.GetSkillTypeID(), addValue, False)
+        SkillCommon.SkillAddHP(cureObj, curSkill.GetSkillID(), addValue, False, attacker)
         targetList.append(cureObj)
         valueList.append(addValue)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_8.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_8.py
index 250699c..4cabfcb 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_8.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_8.py
@@ -50,7 +50,7 @@
 
     if addValue <= 0:
         return
-    SkillCommon.SkillAddHP(defender, curSkill.GetSkillTypeID(), addValue, isEnhanceSkill)
+    SkillCommon.SkillAddHP(defender, curSkill.GetSkillID(), addValue, False, attacker)
     if isEnhanceSkill:
         return True
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PassiveBuff/PassiveSkill_4075.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PassiveBuff/PassiveSkill_4075.py
index b70950c..2accc52 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PassiveBuff/PassiveSkill_4075.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PassiveBuff/PassiveSkill_4075.py
@@ -13,7 +13,7 @@
 #
 #---------------------------------------------------------------------
 
-import PlayerControl
+import GameObj
 import AttackCommon
 
 def CheckCanHappen(attacker, defender, effect, curSkill):
@@ -25,5 +25,5 @@
     
 
 def GetValue(attacker, defender, effect):
-    return max(PlayerControl.GetAtkSpeed(attacker) - 10000, 0)/100*effect.GetEffectValue(0)
+    return max(GameObj.GetAtkSpeed(attacker) - 10000, 0)/100*effect.GetEffectValue(0)
 

--
Gitblit v1.8.0