129 【战斗】战斗系统-服务端(初版战斗,支持基础的三维属性战斗,支持简单的普攻技能、怒气技能、回血技能;主线章节关卡过关支持;阵容保存支持多阵容;)
32个文件已修改
1个文件已删除
2个文件已添加
5183 ■■■■ 已修改文件
PySysDB/PySysDBPY.h 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py 419 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_SummonNPC.py 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_SummonNPC.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py 1905 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChNetSendPack.py 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py 164 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py 628 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameObj.py 314 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldEvent.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/ChNPC.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCHurtManager.py 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py 119 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py 150 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_TurnFight.py 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/EffGetSet.py 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py 148 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_18.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillModule_8.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PassiveBuff/PassiveSkill_4075.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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":值, ...}
};
//成长型境界怪物表
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
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
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
        #副本
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()
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)
    
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()
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()
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
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],
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
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):
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
                                )
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py
New file
@@ -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
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
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
    
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)
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)
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
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)
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:
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):
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):
    ## 清空伤血列表
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
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):
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
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py
New file
@@ -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
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_TurnFight.py
File was deleted
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, ...}}
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, # 无
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],    # 灼烧固定伤害
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
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)
        
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
    
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)