129 【战斗】战斗系统-服务端(NPC、技能使用新表N.NPC表卡牌、J.技能表卡牌;重构战斗系统;优化战利品掉落;)
20个文件已修改
17个文件已删除
4个文件已添加
6976 ■■■■ 已修改文件
PySysDB/PySysDBPY.h 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_Pet.py 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_Player.py 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_SummonNPC.py 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_NormalNPC.py 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_Pet.py 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_Player.py 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_SummonNPC.py 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_NormalNPC.py 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_Pet.py 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_Player.py 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_SummonNPC.py 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_Pet.py 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_Player.py 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_SummonNPC.py 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BattleObj.py 578 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py 1164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py 477 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Hero.py 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameObj.py 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevel.py 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldEvent.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NetPackCommon.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ObjPool.py 498 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PySkillManager.py 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnBuff.py 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnSkill.py 899 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PySysDB/PySysDBPY.h
@@ -26,10 +26,71 @@
    BYTE        OPLimitInAct;    //活动期间限制队伍操作
};
//NPC表
struct NPC
{
    DWORD        _NPCID;    //NPCID
    char        NPCName;    //名称
    BYTE        Country;    //国家
    BYTE        AtkDistType;    //远近类型;1-近战;2-远程
    WORD        LV;    //等级
    DWORD        Atk;    //攻击力
    DWORD        Def;    //防御值
    DWORD        MaxHP;    //最大生命值,可超过20E
    list        SkillIDList;    //技能ID列表
    DWORD        FinalDamPer;    //最终增伤
    DWORD        FinalDamPerDef;    //最终减伤
    DWORD        MissRate;    //闪避概率
    DWORD        MissRateDef;    //抗闪避概率
    DWORD        SuperHitRate;    //暴击概率
    DWORD        SuperHitRateDef;    //抗暴击概率
    DWORD        StunRate;    //击晕概率
    DWORD        StunRateDef;    //抗击晕概率
    DWORD        ComboRate;    //连击概率
    DWORD        ComboRateDef;    //抗连击概率
    DWORD        ParryRate;    //格挡概率
    DWORD        ParryRateDef;    //抗格挡概率
    DWORD        SuckHPPer;    //吸血比率
    DWORD        SuckHPPerDef;    //抗吸血比率
    dict        SpecAttrInfo;    //特殊属性信息 {"属性ID":值, ...}
};
//技能表
struct    Skill
{
    DWORD        _SkillID;    //技能ID
    DWORD        SkillTypeID;    //技能TypeID
    WORD        SkillMaxLV;    //最高等级
    char        SkillName;    //技能名
    BYTE        FuncType;    //功能分类
    BYTE        SkillType;    //技能类型
    BYTE        HurtType;    //伤害类型
    BYTE        AtkType;    //释放方式
    BYTE        TagAim;        //瞄准位置
    BYTE        TagFriendly;    //敌我目标
    BYTE        TagAffect;    //目标细分
    BYTE        TagCount;    //目标个数
    WORD        HappenRate;    //释放或添加几率
    WORD        LastTime;    //持续时间
    WORD        CoolDownTime;    //冷却时间
    WORD        Priority;    //优先级
    DWORD        EffectID1;    //效果ID1
    list        EffectValues1;    //效果值列表1
    DWORD        EffectID2;    //效果ID2
    list        EffectValues2;    //效果值列表2
    DWORD        EffectID3;    //效果ID3
    list        EffectValues3;    //效果值列表3
    DWORD        ConnSkill;    //关联技能
    list        EnhanceSkillList;    //触发技能ID列表
    DWORD        FightPower;    //技能战斗力
};
//武将表
struct    Hero
{
    DWORD        _HeroID;    //英雄ID
    char        Name;    //名称
    BYTE        Country;    // 国家
    BYTE        Quality;    // 品质
    BYTE        AtkDistType;    //远近类型;1-近战;2-远程
@@ -157,7 +218,6 @@
{
    BYTE        _ChapterID;    //章节ID
    list        DailyBootyUpperList;    // 每日战利品掉落上限,[[物品ID,每日上限], ...]
    list        BootyWeightList;    // 战利品掉落权重,[[权重,物品ID,掉落个数下限, 上限], ...]
};
//主线关卡表
@@ -917,36 +977,6 @@
    float        AttrPer;    //对应等级表中的比例
    dict        AttrSpecDict;    //特殊属性值字典 {attrKey:value, ...}
    dict        AttrExDict;    //特殊属性值字典 {attrKey:value, ...}
};
//NPC表扩展
struct tagNPCEx
{
    DWORD        _NPCID;    //NPCID
    BYTE        FightPowerLackAtkLimit;    //战力不足限制攻击
    DWORD        SuppressFightPower;    //推荐/压制战力
    BYTE        AtkDistType;    //远近类型;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/Script/Attack/AttackLogic/AttackCommon.py
@@ -454,7 +454,7 @@
            return heroIpyData.GetAtkDistType()
        
    npcID = curObj.GetNPCID()
    npcDataEx = NPCCommon.GetNPCDataEx(npcID)
    npcDataEx = NPCCommon.GetNPCDataPy(npcID)
    if npcDataEx:
        return npcDataEx.GetAtkDistType()
    
@@ -872,14 +872,14 @@
                PlayerControl.NotifyCode(atkPlayer, sysMark)
        return False
    
    npcDataEx = NPCCommon.GetNPCDataEx(npcID)
    if npcDataEx and npcDataEx.GetFightPowerLackAtkLimit():
        if npcDataEx.GetSuppressFightPower() > PlayerControl.GetFightPower(atkPlayer):
            if isNotify:
                PlayerControl.NotifyCode(atkPlayer, "BossFightPowerHint")
            #GameWorld.DebugLog("战力不足,无法攻击boss! npcID=%s,SuppressFightPower=%s > playerFightPower=%s"
            #                   % (npcID, npcDataEx.GetSuppressFightPower(), PlayerControl.GetFightPower(atkPlayer)))
            return False
    #npcDataEx = NPCCommon.GetNPCDataPy(npcID)
    #if npcDataEx and npcDataEx.GetFightPowerLackAtkLimit():
    #    if npcDataEx.GetSuppressFightPower() > PlayerControl.GetFightPower(atkPlayer):
    #        if isNotify:
    #            PlayerControl.NotifyCode(atkPlayer, "BossFightPowerHint")
    #        #GameWorld.DebugLog("战力不足,无法攻击boss! npcID=%s,SuppressFightPower=%s > playerFightPower=%s"
    #        #                   % (npcID, npcDataEx.GetSuppressFightPower(), PlayerControl.GetFightPower(atkPlayer)))
    #        return False
        
    return True
@@ -2150,9 +2150,6 @@
        aBurnValue = atkwargs.get('burnValue', 0)
        aBurnPer = atkwargs.get('burnPer', 0)
        hurtFormulaKey = atkwargs.get('hurtFormulaKey', None)
        #if hurtFormulaKey == "Burn":
        #    pass
        #else:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("DOTFormula", 1))
    elif not curSkill:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 3))
@@ -2240,7 +2237,7 @@
        else:
            defObj.SetDict(ChConfig.Def_PlayerKey_LastAttackerObjID, atkObj.GetID())
            
    TurnAttack.AddTurnObjHurtValue(atkObj, defObj, resultHurtType.HurtType, resultHurtType.RealHurtHP, resultHurtType.LostHP, curSkill)
    #TurnAttack.AddTurnObjHurtValue(atkObj, defObj, resultHurtType.HurtType, resultHurtType.RealHurtHP, resultHurtType.LostHP, curSkill)
    
    #if resultHurtType.RealHurtHP:
    #    PassiveBuffEffMng.OnPassiveSkillTrigger(defObj, atkObj, None, ChConfig.TriggerType_BeHurt, tick)
@@ -2708,8 +2705,8 @@
    if GameObj.GetHP(curObjDetel) > 0:
        return
    
    if TurnAttack.SetTurnObjKilled(curObjDetel, atkObj):
        return
    #if TurnAttack.SetTurnObjKilled(curObjDetel, atkObj):
    #    return
        
    #---玩家处理---
    if curObjDetel.GetGameObjType() == IPY_GameWorld.gotPlayer:
@@ -2787,7 +2784,7 @@
    srcID, srcType = 0, 0
    if srcObj:
        srcID, srcType = srcObj.GetID(), srcObj.GetGameObjType()
    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(curObj.GetID())
    turnFight = TurnAttack.GetTurnFightMgr().getTurnFight(curObj.GetTFGUID())
    if turnFight:
        clientPack = ChNetSendPack.tagObjPropertyRefreshView()
        clientPack.ObjID = curObj.GetID()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_NormalNPC.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_Pet.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_Player.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/NormalNPC_Attack_SummonNPC.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_NormalNPC.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_Pet.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_Player.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Pet_Attack_SummonNPC.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_NormalNPC.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_Pet.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_Player.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/Player_Attack_SummonNPC.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_Pet.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_Player.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_SummonNPC.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BaseAttack.py
@@ -340,7 +340,7 @@
        
    DoLogic_AttackResult(attacker, defender, useSkill, tick)
    
    TurnAttack.OnTurnfightAttackResult(attacker, defender, useSkill)
    #TurnAttack.OnTurnfightAttackResult(attacker, defender, useSkill)
    return True
#---------------------------------------------------------------------
@@ -666,7 +666,7 @@
    
    OnHurtTypeTriggerPassiveSkill(attacker, defender, curSkill, tick)
    DoLogic_AttackResult(attacker, defender, curSkill, tick)
    TurnAttack.OnTurnfightAttackResult(attacker, defender, curSkill)
    #TurnAttack.OnTurnfightAttackResult(attacker, defender, curSkill)
    return True
@@ -878,7 +878,7 @@
        DoLogic_AttackResult(attacker, defObj, curSkill, tick)
        
    TurnAttack.OnTurnfightAttackResult(attacker, defender, curSkill)
    #TurnAttack.OnTurnfightAttackResult(attacker, defender, curSkill)
    return
## 执行群攻攻击
@@ -1882,7 +1882,7 @@
    #通知客户端攻击结果
    __Sync_AttackResult(curNPC, target, curSkill)
    
    TurnAttack.OnTurnfightAttackSuccess(curNPC, target, curSkill)
    #TurnAttack.OnTurnfightAttackSuccess(curNPC, target, curSkill)
    
    #技能使用成功
    if curSkill:
@@ -2671,7 +2671,7 @@
        DoLogic_AttackResult(attacker, defObj, curSkill, tick)
        
    TurnAttack.OnTurnfightAttackResult(attacker, None, curSkill)
    #TurnAttack.OnTurnfightAttackResult(attacker, None, curSkill)
    return True
    
    
@@ -2781,7 +2781,7 @@
    sendPack.ValueEx = curHurt.GetHurtHPEx()
    sendPack.RemainHP = curHurt.GetCurHP()
    sendPack.RemainHPEx = curHurt.GetCurHPEx()
    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(attacker.GetID())
    turnFight = TurnAttack.GetTurnFightMgr().getTurnFight(attacker.GetTFGUID())
    if turnFight:
        turnFight.addBatPack(sendPack)
        return
@@ -2797,7 +2797,7 @@
    # m_LastBattleTick = GetGameWorldManager()->GetTick();
    #===========================================================================
    
    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(attacker.GetID())
    turnFight = TurnAttack.GetTurnFightMgr().getTurnFight(attacker.GetTFGUID())
    if turnFight:
        sendPack = ChNetSendPack.tagUseSkillAttack()
        sendPack.ObjID = attacker.GetID()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BattleObj.py
New file
@@ -0,0 +1,578 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package BattleObj
#
# @todo:战斗对象
# @author hxp
# @date 2025-08-06
# @version 1.0
#
# 详细描述: py自定义的战斗对象,可以是玩家、NPC通用的,废弃原c++的Player及NPC,完全由py自行处理
# MMO项目可以curPlayer绑定一个战斗对象,curPlayer仅做为玩家的数据对象,战斗相关统一使用战斗对象
#
#-------------------------------------------------------------------------------
#"""Version = 2025-08-06 18:30"""
#-------------------------------------------------------------------------------
import GameWorld
import PyGameData
import TurnAttack
import IpyGameDataPY
import ChNetSendPack
import ShareDefine
import ChConfig
import ObjPool
class HurtObj():
    ## 伤血统计
    def __init__(self):
        self.Clear()
        return
    def Clear(self):
        self._objID = 0
        self._hurtTypes = 0 # 本次伤血类型,如闪避、暴击、格挡等,通过二进制或运算得到最终值,支持多种同时出现,如暴击的同时被格挡
        self._hurtHP = 0 # 伤血值
        self._lostHP = 0 # 实际掉血值
        self._curHP = 0 # 更新血量
        self._suckHP = 0 # 吸血量
        self._bounceHP = 0 # 反弹血量
        return
    def GetObjID(self): return self._objID
    def SetObjID(self, objID): self._objID = objID
    def GetHurtTypes(self): return self._hurtTypes
    def SetHurtTypes(self, hurtTypes): self._hurtTypes = hurtTypes
    def AddHurtType(self, hurtType):
        ## 添加伤血类型,单次伤害支持多种类型同时出现
        self._hurtTypes |= pow(2, hurtType)
        return
    def HaveHurtType(self, hurtType):
        ## 判断是否存在某种伤血类型
        return self._hurtTypes&pow(2, hurtType)
    def GetHurtHP(self): return self._hurtHP
    def SetHurtHP(self, hurtHP): self._hurtHP = hurtHP
    def GetLostHP(self): return self._lostHP
    def SetLostHP(self, lostHP): self._lostHP = lostHP
    def GetCurHP(self): return self._curHP
    def SetCurHP(self, curHP): self._curHP = curHP
    def GetSuckHP(self): return self._suckHP
    def SetSuckHP(self, suckHP): self._suckHP = suckHP
    def GetBounceHP(self): return self._bounceHP
    def SetBounceHP(self, bounceHP): self._bounceHP = bounceHP
class PyBuff():
    def __init__(self, skillData):
        self._skillData = skillData
        self._buffID = 0
        self._ownerID = 0
        self._layer = 0
        self._remainTime = 0
        self._valueList = []
        return
    def GetSkillData(self): return self._skillData
    def GetSkillID(self): return self._skillData.GetSkillID()
    def GetBuffID(self): return self._buffID
    def SetBuffID(self, buffID): self._buffID = buffID
    def GetOwnerID(self): return self._ownerID
    def SetOwnerID(self, ownerID): self._ownerID = ownerID
    def GetLayer(self): return self._layer
    def SetLayer(self, layer): self._layer = layer
    def GetRemainTime(self): return self._remainTime
    def SetRemainTime(self, remainTime): self._remainTime = remainTime
    def SetValueList(self, valueList): self._valueList = valueList
    def GetValue(self, index):
        return self._valueList[index] if len(self._valueList) > index else 0
class BuffManager():
    ## 战斗对象buff管理器
    def __init__(self):
        self._buffList = [] # [PyBuff, ...]
        self._buffIDDict = {} # {buffID:PyBuff, ...}
        self._skillTypeIDBuffIDs = {} # 技能TypeID对应的buff {skillTypeID:[buffID, ...], ...}
        self._buffID = 0 # 该对象的唯一buffID,递增,不同对象buffID可重复,buffID非skillID,不同buffID的skillID可能一样
        # 该项目设定同一个对象可能同时存在多个相同skillID的buff,独立算CD
        return
    def ClearBuff(self):
        poolMgr = ObjPool.GetPoolMgr()
        for buff in self._buffList:
            poolMgr.release(buff)
        self._buffList = []
        self._buffIDDict = {}
        self._skillTypeIDBuffIDs = {}
        self._buffID = 0
        return
    def GetBuffCount(self): return len(self._buffList)
    def GetBuffByIndex(self, index):
        buff = self._buffList[index]
        #if False:
        #    buff = PyBuff()
        return buff
    def AddBuff(self, skillID):
        buff = None
        skillData = IpyGameDataPY.GetIpyGameData("Skill", skillID)
        if not skillData:
            return buff
        skillTypeID = skillData.GetSkillTypeID()
        self._buffID += 1
        buff = ObjPool.GetPoolMgr().acquire(PyBuff, skillData)
        buff.SetBuffID(self._buffID)
        self._buffList.append(buff)
        self._buffIDDict[self._buffID] = buff
        if skillTypeID not in self._skillTypeIDBuffIDs:
            self._skillTypeIDBuffIDs[skillTypeID] = []
        buffIDs = self._skillTypeIDBuffIDs[skillTypeID]
        if self._buffID not in buffIDs:
            buffIDs.append(self._buffID)
        #if False:
        #    buff = PyBuff()
        return buff
    def GetBuff(self, buffID):
        buff = None
        if buffID in self._buffIDDict:
            buff = self._buffIDDict[buffID]
        #if False:
        #    buff = PyBuff()
        return buff
    def FindBuffIDBySkillID(self, skillID):
        ## 返回该技能ID的所有buffID列表
        skillData = IpyGameDataPY.GetIpyGameData("Skill", skillID)
        if not skillData:
            return []
        return self.FindBuffIDBySkillTypeID(skillData.GetSkillTypeID())
    def FindBuffIDBySkillTypeID(self, skillTypeID):
        ## 返回该技能TypeID的所有buffID列表
        if skillTypeID not in self._skillTypeIDBuffIDs:
            return []
        buffs = []
        for buffID in self._skillTypeIDBuffIDs[skillTypeID]:
            if buffID not in self._buffIDDict:
                continue
            buffs.append(self._buffIDDict[buffID])
        return buffs
class PySkill():
    def __init__(self, skillData):
        self._skillData = skillData
        self._remainTime = 0
        self._batType = 0 # 战斗类型,普通、连击、反击、追击等
        self._enhanceBySkill = None # 由哪个主技能触发的
        self._tagObjList = [] # 本次技能目标列表 [BatObj, ...]
        self._hurtList = [] # 本次伤血列表 [HurtObj, ...]
        return
    def SetSkillData(self, skillData): self._skillData = skillData
    def GetSkillID(self): return self._skillData.GetSkillID()
    def GetSkillTypeID(self): return self._skillData.GetSkillTypeID()
    def GetSkillMaxLV(self): return self._skillData.GetSkillMaxLV()
    def GetSkillName(self): return self._skillData.GetSkillName()
    def GetFuncType(self): return self._skillData.GetFuncType()
    def GetSkillType(self): return self._skillData.GetSkillType()
    def GetHurtType(self): return self._skillData.GetHurtType()
    def GetAtkType(self): return self._skillData.GetAtkType()
    def GetTagAim(self): return self._skillData.GetTagAim() # 瞄准位置
    def GetTagFriendly(self): return self._skillData.GetTagFriendly() # 敌我目标
    def GetTagSelf(self): return self._skillData.GetTagSelf() # 是否含自己
    def GetTagAffect(self): return self._skillData.GetTagAffect() # 目标细分
    def GetTagCount(self): return self._skillData.GetTagCount() # 目标个数
    def GetHappenRate(self): return self._skillData.GetHappenRate() # 释放或添加几率
    def GetLastTime(self): return self._skillData.GetLastTime() # 持续时间
    def GetCoolDownTime(self): return self._skillData.GetCoolDownTime()
    def FindEffectID(self, effID):
        ## 查找是否有某个效果ID
        # @return: 大于0该ID所在的效果ID编号; 0-不存在该效果ID
        for idNum in range(1, 1 + 3):
            if self.GetEffectID(idNum) == effID:
                return idNum
        return 0
    def GetEffectID(self, idNum):
        ## 获取效果ID
        # @param idNum: 效果ID编号,从1开始
        if idNum == 1:
            return self._skillData.GetEffectID1()
        if idNum == 2:
            return self._skillData.GetEffectID2()
        if idNum == 3:
            return self._skillData.GetEffectID3()
        return 0
    def GetEffectValue(self, idNum, index):
        ## 获取效果对应值
        # @param idNum: 效果ID编号,从1开始
        # @param index: 值索引,从0开始代表第1个值
        if idNum <= 0:
            return 0
        if idNum == 1:
            values = self._skillData.GetEffectValues1()
        elif idNum == 2:
            values = self._skillData.GetEffectValues2()
        elif idNum == 3:
            values = self._skillData.GetEffectValues3()
        else:
            return 0
        return values[index] if len(values) > index else 0
    def GetConnSkill(self): return self._skillData.GetConnSkill() # 关联技能
    def GetEnhanceSkillList(self): return self._skillData.GetEnhanceSkillList() # 额外触发的技能ID列表
    def GetFightPower(self): return self._skillData.GetFightPower()
    ## ---------------------------------- 非技能表内容 ----------------------------------
    def GetRemainTime(self): return self._remainTime
    def SetRemainTime(self, remainTime): self._remainTime = remainTime
    def GetBatType(self): return self._batType
    def SetBatType(self, batType): self._batType = batType
    def GetEnhanceBySkill(self): return self._enhanceBySkill
    def SetEnhanceBySkill(self, enhanceBySkill): self._enhanceBySkill = enhanceBySkill
    def GetTagObjList(self): return self._tagObjList # 技能目标列表
    def SetTagObjList(self, tagObjList): self._tagObjList = tagObjList
    def ClearHurtObj(self):
        ## 清空伤血统计
        poolMgr = ObjPool.GetPoolMgr()
        for hurtObj in self._hurtList:
            poolMgr.release(hurtObj)
        self._hurtList = []
        return
    def AddHurtObj(self, tagID):
        ## 添加某个伤血
        hurtObj = ObjPool.GetPoolMgr().acquire(HurtObj)
        hurtObj.SetObjID(tagID)
        self._hurtList.append(hurtObj)
        return hurtObj
    def GetHurtObjList(self): return self._hurtList
    def GetHurtObj(self, tagID):
        ## 获取某个伤血,如果目标没有在伤血列表里则返回None
        for hurtObj in self._hurtList:
            if hurtObj.GetObjID() == tagID:
                return hurtObj
        return
class SkillManager():
    ## 战斗对象技能管理器
    def __init__(self):
        self._skillList = [] # 技能列表 [PySkill, ...]
        self._skillDict = {} # {skillID:PySkill, ...}
        return
    def SkillReset(self):
        poolMgr = ObjPool.GetPoolMgr()
        for skill in self._skillList:
            poolMgr.release(skill)
        self._skillList = []
        self._skillDict = {}
        return
    def GetSkillCount(self): return len(self._skillList)
    def GetSkillByIndex(self, index):
        skill = self._skillList[index]
        #if False:
        #    skill = PySkill()
        return skill
    def FindSkillByID(self, skillID):
        skill = self._skillDict.get(skillID, None)
        #if False:
        #    skill = PySkill()
        return skill
    def FindSkillByTypeID(self, skillTypeID):
        skill = None
        for s in self._skillList:
            if s.GetSkillTypeID() == skillTypeID:
                skill = s
                break
        #if False:
        #    skill = PySkill()
        return skill
    def LearnSkillByID(self, skillID):
        skillData = IpyGameDataPY.GetIpyGameData("Skill", skillID)
        if not skillData:
            return
        if skillID in self._skillDict:
            return
        skillTypeID = skillData.GetSkillTypeID()
        curSkill = self.FindSkillByTypeID(skillTypeID)
        if curSkill:
            if curSkill.GetSkillID() >= skillID:
                return
            # 升级技能
            curSkill.SetSkillData(skillData)
        else:
            # 学新技能
            curSkill = ObjPool.GetPoolMgr().acquire(PySkill, skillData)
            self._skillDict[skillID] = curSkill
            self._skillList.append(curSkill)
        return curSkill
class BatObj():
    ## 战斗实体对象数据,目前与某个NPCObj绑定
    def __init__(self):
        self.tfGUID = "" # 所属的某场回合战斗的guid
        self.objID = 0
        self.objName = ""
        self.npcID = 0
        self.ownerID = 0 # 所属玩家ID,可能为0,0代表非玩家的战斗实体
        self.heroID = 0
        self.skinID = 0
        self.lv = 1
        self.fightPower = 0
        self.faction = 0 # 所属阵营,一般只有双方阵营, 1 或 2,发起方默认1
        self.lineupNum = 1 # 阵容位置编号,一般多V多时有用,通常默认1
        self.posNum = 0 # 所在阵容站位
        self._initAttrDict = {} # 初始化时的属性,固定不变,初始化时已经算好的属性  {attrID:value, ...}
        self._batAttrDict = {} # 实际战斗属性,包含buff层级的实际属性
        self._skillTempAttrDict = {} # 某次技能释放中临时的属性增减 {attrID:+-value, ...}
        self._kvDict = {} # 自定义kv字典
        self._skillUseCntDict = {} # 技能累计使用次数 {skillID:useCnt, ...}
        self._skillTurnUseCntDict = {} # 技能单回合累计使用次数 {skillID:useCnt, ...}
        self._skillMgr = ObjPool.GetPoolMgr().acquire(SkillManager)
        self._buffMgr = ObjPool.GetPoolMgr().acquire(BuffManager)
        # 统计
        self.hurtStat = 0 # 输出统计
        self.defStat = 0 # 承伤统计
        self.cureStat = 0 # 治疗统计
        return
    def InitBatAttr(self, initAttrDict, initXP=0):
        ## 初始化战斗属性
        self._initAttrDict = initAttrDict
        self._batAttrDict = {}
        self._batAttrDict.update(initAttrDict)
        self._batAttrDict[ChConfig.AttrID_XP] = initXP
        self._batAttrDict[ChConfig.AttrID_HP] = initAttrDict.get(ChConfig.AttrID_MaxHP, 1)
        self._skillTempAttrDict = {}
        return
    def GetTFGUID(self): return self.tfGUID # 所属的某场战斗
    def SetTFGUID(self, tfGUID): self.tfGUID = tfGUID
    def GetTurnFight(self): return TurnAttack.GetTurnFightMgr().getTurnFight(self.tfGUID)
    def GetID(self): return self.objID
    def GetName(self): return self.objName
    def SetName(self, name): self.objName = name
    def GetNPCID(self): return self.npcID # 如果是NPC战斗单位,则该值非0
    def SetNPCID(self, npcID): self.npcID = npcID # 设置所属的NPCID
    def GetOwnerID(self): return self.ownerID # 如果是玩家战斗单位,则该值非0,为所属玩家ID
    def GetHeroID(self): return self.heroID # 仅玩家有值,某个武将ID
    def GetSkinID(self): return self.heroID # 仅玩家有值,武将皮肤
    def SetOwnerHero(self, ownerID, heroID, skinID): # 设置所属的玩家及武将
        self.ownerID = ownerID
        self.heroID = heroID
        self.skinID = skinID
    def SetLineupPos(self, posNum, lineupNum=1):
        ## 设置阵容所在位置
        # @param posNum: 在本阵容中的站位
        # @param lineupNum: 本阵容在本阵营中的站位,一般多V多时有用,默认1
        self.posNum = posNum
        self.lineupNum = lineupNum
    def GetLineupNum(self): return self.lineupNum
    def GetPosNum(self): return self.posNum
    def GetFaction(self): return self.faction
    def SetFaction(self, faction): self.faction = faction
    def GetFightPower(self): return self.fightPower
    def SetFightPower(self, fightPower): self.fightPower = fightPower
    def GetLV(self): return self.lv
    def SetLV(self, lv): self.lv = lv
    def GetDictByKey(self, key): return self._kvDict.get(key, 0)
    def SetDict(self, key, value): self._kvDict[key] = value
    def GetSkillManager(self): return self._skillMgr
    def GetBuffManager(self):return self._buffMgr
    def GetCanAttack(self):
        ## 可否被攻击
        # 无敌buff
        #if 无敌:
        #    return False
        return True
    # 战斗属性
    def GetMaxHP(self): return self._batAttrDict.get(ChConfig.AttrID_MaxHP, 0)
    def SetMaxHP(self, maxHP): self._batAttrDict[ChConfig.AttrID_MaxHP] = maxHP
    def GetHP(self): return self._batAttrDict.get(ChConfig.AttrID_HP, 0)
    def SetHP(self, hp, isNotify=False):
        self._batAttrDict[ChConfig.AttrID_HP] = hp
        if isNotify:
            NotifyObjInfoRefresh(self, ChConfig.AttrID_HP, hp)
        return
    def SetHPFull(self, isNotify=True): self.SetHP(self.GetMaxHP())
    def GetXP(self): return self._batAttrDict.get(ChConfig.AttrID_XP, 0)
    def SetXP(self, xp, isNotify=True):
        self._batAttrDict[ChConfig.AttrID_XP] = xp
        if isNotify:
            NotifyObjInfoRefresh(self, ChConfig.AttrID_XP, xp)
        return
    def GetAtk(self): return self.GetAttrValue(ChConfig.AttrID_Atk)
    def GetDef(self): return self.GetAttrValue(ChConfig.AttrID_Def)
    def GetAttrValue(self, attrID):
        value = self._batAttrDict.get(attrID, 0)
        if attrID in self._skillTempAttrDict:
            value += self._skillTempAttrDict[attrID] # 支持正负值
            value = max(1, value)
        return value
    def AddSkillTempAttr(self, attrID, value):
        ## 增加技能临时属性,支持正负值
        # @param value: 正值-加属性;负值-减属性
        self._skillTempAttrDict[attrID] = self._skillTempAttrDict.get(attrID, 0) + value
    def ClearSkillTempAttr(self): self._skillTempAttrDict = {}
    def GetSkillUseCnt(self, skillID): return self._skillUseCntDict.get(skillID, 0)
    def GetSkillTurnUseCnt(self, skillID): return self._skillTurnUseCntDict.get(skillID, 0)
    def AddSkillUseCnt(self, skillID):
        self._skillUseCntDict[skillID] = self._skillUseCntDict.get(skillID, 0) + 1
        self._skillTurnUseCntDict[skillID] = self._skillTurnUseCntDict.get(skillID, 0) + 1
    def StatHurtValue(self, hurtValue):
        ## 统计输出
        self.hurtStat += hurtValue
        return self.hurtStat
    def StatDefValue(self, lostHP):
        ## 统计承伤
        self.defStat += lostHP
        return self.defStat
    def StatCureValue(self, cureValue):
        ## 统计治疗
        self.cureStat += cureValue
        return self.cureStat
    def TurnReset(self):
        ## 回合重置
        self._skillTurnUseCntDict = {}
    def ResetBatObj(self, isReborn=True):
        ## 重置战斗相关
        # @param isReborn: 死亡的是否复活
        # 重置统计
        self.hurtStat = 0
        self.defStat = 0
        self.cureStat = 0
        if self.GetHP() <= 0 and not isReborn:
            return
        # 清除buff
        # 回满血
        self.SetHPFull()
        # 重置怒气
        initXP = IpyGameDataPY.GetFuncCfg("AngerXP", 1)
        self.SetXP(initXP)
        return
class BattleObjMgr():
    ## 战斗对象管理器
    def __init__(self):
        self._newID = 0 # 管理创建新的实例ID
        self._freeIDList = []
        self.batObjDict = {} # 战斗单位 {objID:BatObj, ...}
        return
    def __getNewObjID(self):
        while self._freeIDList:
            objID = self._freeIDList.pop(0)
            if objID not in self.batObjDict:
                return objID
        maxID = 100000
        if self._newID >= maxID:
            self._newID = 0
        while self._newID < maxID:
            self._newID += 1
            if self._newID not in self.batObjDict:
                return self._newID
        GameWorld.ErrLog("__getNewObjID error.")
        return 0
    def addBatObj(self):
        ## 添加战斗单位
        newBatObj = None
        newObjID = self.__getNewObjID()
        if not newObjID:
            return newBatObj
        newBatObj = ObjPool.GetPoolMgr().acquire(BatObj)
        newBatObj.objID = newObjID
        self.batObjDict[newObjID] = newBatObj
        GameWorld.DebugLog("添加战斗单位: objID=%s" % (newObjID))
        if False:
            newBatObj = BatObj(None, 0)
        return newBatObj
    def getBatObj(self, objID):
        batObj = None
        if objID in self.batObjDict:
            batObj = self.batObjDict[objID]
        elif False:
            batObj = BatObj(None, 0)
        return batObj
    def delBatObj(self, objID):
        if objID not in self.batObjDict:
            return
        batObj = self.batObjDict.pop(objID)
        if not batObj:
            return
        objID = batObj.objID
        GameWorld.DebugLog("回收战斗单位: objID=%s" % (objID))
        turnFight = batObj.GetTurnFight()
        if turnFight:
            # //04 07 NPC消失#tagNPCDisappear 此处通知消失,与回合制死亡区分
            clientPack = ChNetSendPack.tagNPCDisappear()
            clientPack.NPCID = [objID]
            clientPack.Count = len(clientPack.NPCID)
            turnFight.addBatPack(clientPack)
        # 最后回收对象
        ObjPool.GetPoolMgr().release(batObj)
        if objID not in self._freeIDList: # 回收ID,重复利用
            self._freeIDList.append(objID)
        return
def GetBatObjMgr():
    batObjMgr = PyGameData.g_batObjMgr
    if not batObjMgr:
        batObjMgr = BattleObjMgr()
        PyGameData.g_batObjMgr = batObjMgr
    return batObjMgr
def OnMinute():
    GameWorld.Log("战斗单位数量: %s" % len(GetBatObjMgr().batObjDict))
    return
def NotifyObjInfoRefresh(batObj, attrID, value):
    ##0418通知对象属性刷新
    if attrID not in ChConfig.CDBRefresh_AttrIDDict:
        return
    refreshType, isBig = ChConfig.CDBRefresh_AttrIDDict[attrID]
    turnFight = TurnAttack.GetTurnFightMgr().getTurnFight(batObj.GetTFGUID())
    if not turnFight:
        return
    clientPack = ObjPool.GetPoolMgr().acquire(ChNetSendPack.tagObjInfoRefresh)
    clientPack.ObjID = batObj.GetID()
    clientPack.RefreshType = refreshType
    if isBig:
        clientPack.Value = value % ShareDefine.Def_PerPointValue
        clientPack.ValueEx = value / ShareDefine.Def_PerPointValue
    else:
        clientPack.Value = value
        clientPack.ValueEx = 0
    turnFight.addBatPack(clientPack)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -31,12 +31,13 @@
import NPCCommon
import ShareDefine
import PyGameData
import IPY_GameWorld
import ItemControler
import SkillCommon
import SkillShell
import AttackCommon
import FBLogic
import BattleObj
import TurnSkill
import TurnBuff
import ObjPool
import random
import time
@@ -55,129 +56,61 @@
FightState_Award, # 4 结算奖励
FightState_Over, # 5 结束状态,无特殊意义,仅代表所有处理结束了,与Start对应
) = range(6)
Def_FactionA = 1
Def_FactionB = 2
class BatLineup():
    ## 战斗阵容
    
    def __init__(self, faction, num, fightMgr):
        self.fightMgr = fightMgr
    def __init__(self, faction, num, turnFight):
        self.turnFight = turnFight # TurnFight
        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, ...}
        self.posObjIDDict = {} # 站位对应战斗实体 {站位编号:batObjID, ...}, 站位编号小于0为非主战单位,如主公、红颜等
        self.actionNum = ActionNumStart # 行动位置,从1开始
        return
    
    def getGUID(self): return self.fightMgr.guid
    def getPlayerID(self): return self.fightMgr.playerID # 发起的玩家ID
    def getPlayerID(self): return self.turnFight.playerID # 发起的玩家ID
    
    def isEmpty(self): return not self.npcPosDict
    def isEmpty(self): return not self.posObjIDDict
    
    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", {})
        self.shapeType = lineupInfo.get("ShapeType", 0)
        self.fightPower = lineupInfo.get("FightPower", 0)
        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()
        batObjMgr = BattleObj.GetBatObjMgr()
        for objID in self.posObjIDDict.values():
            batObj = batObjMgr.getBatObj(objID)
            if not batObj:
                continue
            batObj.ResetBatObj(isReborn)
        return
    
    def clearLineup(self):
        ## 清除阵容
        if not self.npcPosDict:
        if not self.posObjIDDict:
            return
        for curNPC in self.npcPosDict.values():
            if not curNPC:
                continue
            NPCCommon.SetDeadEx(curNPC)
        self.npcPosDict = {}
        self.npcObjIDDict = {}
        self.npcAttrDict = {}
        batObjMgr = BattleObj.GetBatObjMgr()
        for objID in self.posObjIDDict.values():
            batObjMgr.delBatObj(objID)
        self.posObjIDDict = {}
        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
    def __init__(self, faction, turnFight):
        self.turnFight = turnFight # TurnFight
        self.faction = faction
        self.lineupDict = {} # 该阵营所有阵容信息 {编号:BatLineup, ...}
        return
@@ -188,7 +121,7 @@
        if num in self.lineupDict:
            lineup = self.lineupDict[num]
        else:
            lineup = BatLineup(self.faction, num, self.fightMgr)
            lineup = BatLineup(self.faction, num, self.turnFight)
            self.lineupDict[num] = lineup
        return lineup
    
@@ -237,9 +170,6 @@
        
        self.startTime = 0 # 开始时间戳,支持毫秒小数
        self.costTime = 0 # 单场战斗总耗时,支持毫秒小数
        #玩家武将行动后击杀目标
        self.playerKillObjIDList = [] # [objID, ...]
        return
    
    def setTurn(self, mapID, funcLineID, turnMax, isNeedReport=False, msgDict={}):
@@ -281,7 +211,6 @@
            if not lineupInfo:
                continue
            batLineup.setLineup(lineupInfo)
            batLineup.refreshFightPower()
        return
    
    def sortActionQueue(self):
@@ -290,7 +219,7 @@
        for batFaction in self.factionDict.values():
            faction = batFaction.faction
            for num, batLineup in batFaction.lineupDict.items():
                fightPower = batLineup.refreshFightPower()
                fightPower = batLineup.fightPower
                sortValue = -(faction * 10 + num)
                sortList.append([fightPower, sortValue, faction, num])
        sortList.sort(reverse=True) # 战力高的先手
@@ -304,7 +233,7 @@
        GameWorld.DebugLog("阵容行动顺序[f, n]: %s" % self.actionSortList)
        return
    
    def getBatFaction(self, faction=Def_FactionA):
    def getBatFaction(self, faction=ChConfig.Def_FactionA):
        ## 默认阵营1
        batFaction = None
        if faction in self.factionDict:
@@ -321,21 +250,25 @@
        if self.winFaction:
            return self.winFaction
        
        batObjMgr = BattleObj.GetBatObjMgr()
        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():
                for posNum, objID in batLineup.posObjIDDict.items():
                    if posNum <= 0:
                        # 非主战位置不判断
                        continue
                    if GameObj.GetHP(curNPC) > 0:
                    batObj = batObjMgr.getBatObj(objID)
                    if not batObj:
                        continue
                    if batObj.GetHP() > 0:
                        allKilled = False
                        break
                    
            if allKilled:
                self.winFaction = Def_FactionA if faction == Def_FactionB else Def_FactionA
                self.winFaction = ChConfig.Def_FactionA if faction == ChConfig.Def_FactionB else ChConfig.Def_FactionA
                DoTurnFightOver(self.guid)
                return self.winFaction
            
@@ -363,6 +296,8 @@
        clientPack.Msg = msg
        clientPack.Len = len(clientPack.Msg)
        clientPack.FactionList = []
        batObjMgr = BattleObj.GetBatObjMgr()
        for faction in self.factionDict.keys():
            batFaction = self.getBatFaction(faction)
            tfFaction = ChPyNetSendPack.tagSCTurnFightFaction()
@@ -375,19 +310,22 @@
                tfLineup.OwnerID = batLineup.ownerID
                tfLineup.ShapeType = batLineup.shapeType
                tfLineup.ObjList = []
                for posNum, curNPC in batLineup.npcPosDict.items():
                for posNum, objID in batLineup.posObjIDDict.items():
                    batObj = batObjMgr.getBatObj(objID)
                    if not batObj:
                        continue
                    tfObj = ChPyNetSendPack.tagSCTurnFightObj()
                    tfObj.ObjID = curNPC.GetID()
                    tfObj.NPCID = curNPC.GetNPCID()
                    tfObj.HeroID = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_HeroID)
                    tfObj.SkinID = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_SkinID)
                    tfObj.HP = curNPC.GetHP()
                    tfObj.HPEx = curNPC.GetHPEx()
                    tfObj.MaxHP = curNPC.GetMaxHP()
                    tfObj.MaxHPEx = curNPC.GetMaxHPEx()
                    tfObj.LV = NPCCommon.GetNPCLV(curNPC)
                    tfObj.ObjID = batObj.GetID()
                    tfObj.NPCID = batObj.GetNPCID()
                    tfObj.HeroID = batObj.GetHeroID()
                    tfObj.SkinID = batObj.GetSkinID()
                    tfObj.HP = batObj.GetHP() % ChConfig.Def_PerPointValue
                    tfObj.HPEx = batObj.GetHP() / ChConfig.Def_PerPointValue
                    tfObj.MaxHP = batObj.GetMaxHP() % ChConfig.Def_PerPointValue
                    tfObj.MaxHPEx = batObj.GetMaxHP() / ChConfig.Def_PerPointValue
                    tfObj.LV = batObj.GetLV()
                    tfObj.PosNum = posNum
                    tfObj.AngreXP = GameObj.GetXP(curNPC)
                    tfObj.AngreXP = batObj.GetXP()
                    tfLineup.ObjList.append(tfObj)
                tfLineup.ObjCnt = len(tfLineup.ObjList)
                tfFaction.LineupList.append(tfLineup)
@@ -411,7 +349,7 @@
        self.addBatPack(clientPack)
        return
    
    def syncObjAction(self, turnNum, objType, objID):
    def syncObjAction(self, turnNum, objID):
        clientPack = ChPyNetSendPack.tagMCTurnFightObjAction()
        clientPack.Clear()
        clientPack.TurnNum = turnNum
@@ -432,6 +370,8 @@
        # 有玩家的统一每个包单独发送,同样也支持战报统计
        if self.curPlayer:
            NetPackCommon.SendFakePack(self.curPlayer, clientPack)
        else:
            ObjPool.GetPoolMgr().release(clientPack)
        return
    
class TurnFightMgr():
@@ -439,7 +379,6 @@
    
    def __init__(self):
        self.turnFightDict = {} # {guid:TurnFight, ...}
        self.npcGUIDDict = {} # npc所属的某场战斗guid {objID:guid, ...}
        return
    
    def addTurnFight(self, mapID, funcLineID=0, playerID=0):
@@ -462,27 +401,6 @@
        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
@@ -513,10 +431,6 @@
        ## 当前战斗是否关卡boss
        return self.turnFight.mapID == ChConfig.Def_FBMapID_MainBoss
    
    def clear(self):
        self.turnFight.clearFight()
        return
def GetMainFightMgr(curPlayer):
    ## 获取主线战斗管理
    olPlayer = PlayerOnline.GetOnlineMgr().GetOnlinePlayer(curPlayer)
@@ -532,71 +446,50 @@
                          % (chapterID, levelNum, nowChapterID, fixNowValue), curPlayer.GetPlayerID())
    return
def GetPlayerLineupByCache(playerID, lineupID):
def GetPlayerLineupInfoByCache(playerID, lineupID):
    ## 获取玩家阵容信息 - 根据玩家查看缓存
    return {}
    lineupInfo = {}
    return lineupInfo
def GetPlayerLineup(curPlayer, lineupID):
    ## 获取玩家阵容信息
def GetPlayerLineupInfo(curPlayer, lineupID):
    ## 获取玩家阵容信息,可用于战斗或查看缓存,因为可能取玩家的缓存进行对战,所以统一使用json格式,前端通用
    # @param lineupID: 阵容ID
    # @return: 阵容全部信息json字典,前端通用格式
    
    playerID = curPlayer.GetPlayerID()
    lineup = PlayerOnline.GetOnlinePlayer(curPlayer).GetLineup(lineupID)
    
    # 武将
    lineupInfo = {"PlayerID":playerID, "FightPower":lineup.fightPower, "ShapeType":lineup.shapeType}
    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
        skinIDList = heroIpyData.GetSkinIDList()
        skinIndex = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroSkin)
        if skinIndex < 0 or skinIndex >= len(skinIDList):
            continue
        skinID = skinIDList[skinIndex]
    for posNum in lineup.lineupHeroDict.keys():
        hero = lineup.GetLineupHero(posNum)
        heroID = hero.heroID
        itemIndex = hero.itemIndex
        userData = ""
        heroLV = 1
        if itemIndex >= 0 and itemIndex < curPack.GetCount():
            heroItem = curPack.GetAt(itemIndex)
            if heroItem and not heroItem.IsEmpty():
                userData = heroItem.GetUserData()
                heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
        skillIDlist = []
        skillIDlist += hero.heroSkillIDList
        heroDict[str(posNum)] = {
                                 "HeroID":heroID,
                                 "SkinID":skinID,
                                 "Data":heroItem.GetUserData(),
                                 "SkinID":hero.skinID,
                                 "LV":heroLV,
                                 "Data":userData,
                                 "FightPower":hero.fightPower,
                                 "AttrDict":{str(k):v for k, v in hero.heroBatAttrDict.items() if v > 0},
                                 "SkillIDList":skillIDlist,
                                 }
        heroCount += 1
        if heroCount >= ShareDefine.LineupObjMax:
            break
    if not heroDict:
        return {}
    lineupInfo.update({"Hero":heroDict})
    
    # 主公属性
    lordAttrDict = PlayerControl.GetLordAttr(curPlayer)
    # 其他
    lineupInfo = {"PlayerID":playerID, "LordAttrDict":lordAttrDict, "Hero":heroDict}
    return lineupInfo
def GetNPCLineup(lineupID):
def GetNPCLineupInfo(lineupID):
    ## 获取NPC阵容信息
    # @param lineupID: 阵容ID
    # @return: 阵容全部信息json字典,前端通用格式
@@ -611,10 +504,26 @@
        npcID = getattr(ipyData, "GetPosNPCID%s" % posNum)()
        if not npcID:
            continue
        npcData = NPCCommon.GetNPCDataPy(npcID)
        if not npcData:
            continue
        batAttrDict = {ChConfig.AttrID_Atk:npcData.GetAtk(), ChConfig.AttrID_Def:npcData.GetDef(), ChConfig.AttrID_MaxHP:npcData.GetMaxHP(),
                       ChConfig.AttrID_FinalDamPer:npcData.GetFinalDamPer(), ChConfig.AttrID_FinalDamPerDef:npcData.GetFinalDamPerDef(),
                       ChConfig.AttrID_MissRate:npcData.GetMissRate(), ChConfig.AttrID_MissRateDef:npcData.GetMissRateDef(),
                       ChConfig.AttrID_SuperHitRate:npcData.GetSuperHitRate(), ChConfig.AttrID_SuperHitRateDef:npcData.GetSuperHitRateDef(),
                       ChConfig.AttrID_StunRate:npcData.GetStunRate(), ChConfig.AttrID_StunRateDef:npcData.GetStunRateDef(),
                       ChConfig.AttrID_ComboRate:npcData.GetComboRate(), ChConfig.AttrID_ComboRateDef:npcData.GetComboRateDef(),
                       ChConfig.AttrID_ParryRate:npcData.GetParryRate(), ChConfig.AttrID_ParryRateDef:npcData.GetParryRateDef(),
                       ChConfig.AttrID_SuckHPPer:npcData.GetSuckHPPer(), ChConfig.AttrID_SuckHPPerDef:npcData.GetSuckHPPerDef(),
                       }
        batAttrDict.update(npcData.GetSpecAttrInfo())
        skillIDList = [] + npcData.GetSkillIDList()
        heroDict[str(posNum)] = {"NPCID":npcID,
                                 "AttrDict":{str(k):v for k, v in batAttrDict.items() if v > 0},
                                 "SkillIDList":skillIDList
                                 }
        
        heroDict[str(posNum)] = {"NPCID":npcID}
    lineupInfo = {"Hero":heroDict}
    lineupInfo = {"NPCLineupID":lineupID, "Hero":heroDict}
    return lineupInfo
def SummonLineupObjs(batLineup, faction, num, lineupInfo, playerID=0):
@@ -624,115 +533,79 @@
    @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)
    tfGUID = batLineup.turnFight.guid
    lineupPlayerID = lineupInfo.get("PlayerID", 0) # 阵容所属玩家ID
    heroDict = lineupInfo.get("Hero", {})
    tick = GameWorld.GetGameWorld().GetTick()
    
    batObjMgr = BattleObj.GetBatObjMgr()
    initXP = IpyGameDataPY.GetFuncCfg("AngerXP", 1)
    baseAtkSkillIDList = IpyGameDataPY.GetFuncEvalCfg("ParryCfg", 3)
    tfMgr = GetTurnFightMgr()
    space = 3
    atkBackSkillIDList = IpyGameDataPY.GetFuncEvalCfg("ParryCfg", 3)
    for posNumKey, heroInfo in heroDict.items():
        posNum = int(posNumKey)
        
        heroID, skinID = 0, 0
        baseAtkSkillID = 0 # 基础普攻ID
        skillIDList = []
        npcID, heroID, skinID = 0, 0, 0
        atkBackSkillID = 0 # 反击技能ID
        fightPower = 0
        skillIDList = [] # 战斗对象可能改变属性或技能,重新创建,防止误修改来源值
        attrDict = {}
        skillIDList += heroInfo.get("SkillIDList", [])
        attrDict.update(heroInfo.get("AttrDict", {}))
        objName = ""
        if lineupPlayerID:
            heroID = heroInfo.get("HeroID", 0)
            skinID = heroInfo.get("SkinID", 0)
            npcID = FighterNPCID
            lv = heroInfo.get("LV", 1)
            fightPower = heroInfo.get("FightPower", 0)
            heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
            if not heroIpyData:
                continue
            normalSkillID = heroIpyData.GetNormalSkillID()
            angerSkillID = heroIpyData.GetAngerSkillID()
            skillIDList += [normalSkillID, angerSkillID]
            atkDistType = heroIpyData.GetAtkDistType()
            objName = heroIpyData.GetName()
        else:
            npcID = heroInfo.get("NPCID", 0)
            npcDataEx = NPCCommon.GetNPCDataEx(npcID)
            npcDataEx = NPCCommon.GetNPCDataPy(npcID)
            if not npcDataEx:
                continue
            skillIDList += npcDataEx.GetSkillIDList()
            objName = npcDataEx.GetNPCName()
            atkDistType = npcDataEx.GetAtkDistType()
            lv = npcDataEx.GetLV()
            
        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)
        curSummon.SetDict(ChConfig.Def_Obj_Dict_HeroID, heroID)
        curSummon.SetDict(ChConfig.Def_Obj_Dict_SkinID, skinID)
        GameObj.SetFaction(curSummon, faction)
        GameObj.SetXP(curSummon, initXP, False)
        batObj = batObjMgr.addBatObj()
        if not batObj:
            break
        objID = batObj.GetID()
        batObj.SetTFGUID(tfGUID)
        batObj.SetName(objName)
        batObj.SetFaction(faction)
        batObj.SetLineupPos(posNum, num)
        batObj.SetFightPower(fightPower)
        batObj.SetLV(lv)
        if npcID:
            batObj.SetNPCID(npcID)
        elif lineupPlayerID:
            batObj.SetOwnerHero(lineupPlayerID, heroID, skinID)
        batObj.InitBatAttr({int(k):v for k, v in attrDict.items()}, initXP)
        
        if atkDistType == ChConfig.AtkDistType_Short:
            baseAtkSkillID = baseAtkSkillIDList[0] if len(baseAtkSkillIDList) > 0 else 0
            atkBackSkillID = atkBackSkillIDList[0] if len(atkBackSkillIDList) > 0 else 0
        elif atkDistType == ChConfig.AtkDistType_Long:
            baseAtkSkillID = baseAtkSkillIDList[1] if len(baseAtkSkillIDList) > 1 else 0
            atkBackSkillID = atkBackSkillIDList[1] if len(atkBackSkillIDList) > 1 else 0
        if atkBackSkillID:
            skillIDList.append(atkBackSkillID)
        skillManager = batObj.GetSkillManager()
        skillManager.SkillReset()
        for skillID in skillIDList:
            skillManager.LearnSkillByID(skillID)
            
        skillManager = curSummon.GetSkillManager()
        #有指定的技能,重新学习
        if skillIDList:
            skillManager.ResetSkill()
            for skillID in skillIDList:
                skillManager.LVUPSkillByID(skillID)
        if baseAtkSkillID:
            skillManager.LVUPSkillByID(baseAtkSkillID)
        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("SummonNPC ID:%s,faction:%s,num=%s,posNum=%s,baseAtkSkillID=%s,%s" % (curSummon.GetID(), faction, num, posNum, baseAtkSkillID, skillIDList))
        curSummon.Reborn(rebornX, rebornY, False)
        NPCCommon.NPCControl(curSummon).DoNPCRebornCommLogic(tick)
        batLineup.posObjIDDict[posNum] = objID
        GameWorld.DebugLog("AddBatObj ID:%s,faction:%s,num=%s,posNum=%s,skill=%s,atk=%s,def=%s,hp=%s"
                           % (objID, faction, num, posNum, skillIDList, batObj.GetAtk(), batObj.GetDef(), batObj.GetHP()))
        
    return
@@ -841,7 +714,7 @@
    teamNum = 1
    lineupID = waveLineupList[teamNum - 1] # NPC阵容ID
    
    lineupMainInfo = GetPlayerLineup(curPlayer, ShareDefine.Lineup_Main)
    lineupMainInfo = GetPlayerLineupInfo(curPlayer, ShareDefine.Lineup_Main)
    if not lineupMainInfo:
        GameWorld.DebugLog("没有设置主阵容!", playerID)
        return
@@ -862,8 +735,8 @@
    
    turnFight = mainFightMgr.turnFight
    turnFight.setTurn(mapID, funcLineID, turnMax, False, {"teamNum":teamNum, "teamMax":teamMax})
    turnFight.setFactionLineup(Def_FactionA, {1:lineupMainInfo}, True)
    turnFight.setFactionLineup(Def_FactionB, {1:GetNPCLineup(lineupID)})
    turnFight.setFactionLineup(ChConfig.Def_FactionA, {1:lineupMainInfo}, True)
    turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID)})
    turnFight.sortActionQueue()
    turnFight.syncInit()
    return
@@ -915,7 +788,7 @@
    
    wave = waveMax = 1 # 关卡boss固定只有一波
    
    lineupMainInfo = GetPlayerLineup(curPlayer, ShareDefine.Lineup_Main)
    lineupMainInfo = GetPlayerLineupInfo(curPlayer, ShareDefine.Lineup_Main)
    if not lineupMainInfo:
        GameWorld.DebugLog("没有设置主阵容!", playerID)
        return
@@ -936,8 +809,8 @@
    
    turnFight = mainFightMgr.turnFight
    turnFight.setTurn(mapID, funcLineID, turnMax, False, {"teamNum":teamNum, "teamMax":teamMax})
    turnFight.setFactionLineup(Def_FactionA, {1:lineupMainInfo}, True)
    turnFight.setFactionLineup(Def_FactionB, {1:GetNPCLineup(lineupID)})
    turnFight.setFactionLineup(ChConfig.Def_FactionA, {1:lineupMainInfo}, True)
    turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID)})
    turnFight.sortActionQueue()
    turnFight.syncInit()
    
@@ -965,7 +838,7 @@
            mainFightMgr.nextTeam = False
            turnFight.resetTurn({"teamNum":teamNum})
            # 切换小队时,玩家阵容不需要处理,保留状态
            turnFight.setFactionLineup(Def_FactionB, {1:GetNPCLineup(lineupID)})
            turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID)})
            turnFight.sortActionQueue()
            turnFight.syncInit()
            
@@ -993,6 +866,7 @@
    doMax = (PosNumMax + 2) * len(turnFight.actionSortList) # 防止死循环,做最大循环次数限制 = (最大位置数 + 主公、红颜位置)*行动阵容数
    overLineupList = [] # 本回合已经结束行动的阵容列表 [(faction, num), ...], 所有阵容全部结束代表本回合结束
    
    batObjMgr = BattleObj.GetBatObjMgr()
    turnNum = turnFight.turnNum
    GameWorld.DebugLog("turnNum=%s,doMax=%s,actionIndex=%s,%s" % (turnNum, doMax, turnFight.actionIndex, turnFight.actionSortList))
    while doCnt < doMax and len(overLineupList) < len(turnFight.actionSortList):
@@ -1011,8 +885,9 @@
                batFaction = turnFight.getBatFaction(faction)
                batLineup = batFaction.getBatlineup(num)
                batLineup.actionNum = ActionNumStart
                for curNPC in batLineup.npcPosDict.values():
                    TurnFightObjPerTurnStart(curNPC, None, turnNum, tick)
                for objID in batLineup.posObjIDDict.values():
                    batObj = batObjMgr.getBatObj(objID)
                    TurnFightObjPerTurnStart(turnFight, batObj, turnNum)
                    
        if turnFight.actionIndex >= len(turnFight.actionSortList):
            turnFight.actionIndex = 0
@@ -1021,14 +896,14 @@
        faction, num = turnFight.actionSortList[turnFight.actionIndex]
        batFaction = turnFight.getBatFaction(faction)
        batLineup = batFaction.getBatlineup(num)
        if batLineup.actionNum > max(batLineup.npcPosDict):
        if batLineup.actionNum > max(batLineup.posObjIDDict):
            if (faction, num) not in overLineupList:
                overLineupList.append((faction, num))
                
            turnFight.actionIndex += 1
            continue
        
        if faction == Def_FactionA:
        if faction == ChConfig.Def_FactionA:
            if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
                GameWorld.DebugLog("战锤不足!")
                return
@@ -1046,31 +921,21 @@
        elif batLineup.actionNum > 0:
            for posNum in range(batLineup.actionNum, PosNumMax + 1):
                batLineup.actionNum = posNum
                if posNum not in batLineup.npcPosDict:
                if posNum not in batLineup.posObjIDDict:
                    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("        攻击失败")
                objID = batLineup.posObjIDDict[posNum]
                batObj = batObjMgr.getBatObj(objID)
                if not OnObjAction(turnFight, batObj):
                    continue
                
                if faction == Def_FactionA:
                if faction == ChConfig.Def_FactionA:
                    playerHeroAtk = True
                    
                break
            
        turnFight.actionIndex += 1
        batLineup.actionNum += 1
        if batLineup.actionNum > max(batLineup.npcPosDict):
        if batLineup.actionNum > max(batLineup.posObjIDDict):
            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))
@@ -1097,7 +962,7 @@
            for faction, num in turnFight.actionSortList:
                batFaction = turnFight.getBatFaction(faction)
                batLineup = batFaction.getBatlineup(num)
                for curNPC in batLineup.npcPosDict.values():
                for objID in batLineup.posObjIDDict.values():
                    pass
                
            if turnFight.checkOverByKilled():
@@ -1116,6 +981,7 @@
    curPlayer = turnFight.curPlayer
    turnMax = turnFight.turnMax
    EntryLogic(turnFight)
    batObjMgr = BattleObj.GetBatObjMgr()
    for turnNum in range(1, turnMax + 1):
        turnFight.turnNum = turnNum
        GameWorld.DebugLog("【----- 回合制战斗轮次: %s -----】" % turnNum)
@@ -1128,8 +994,9 @@
            batFaction = turnFight.getBatFaction(faction)
            batLineup = batFaction.getBatlineup(num)
            batLineup.actionNum = 1
            for curNPC in batLineup.npcPosDict.values():
                TurnFightObjPerTurnStart(curNPC, None, turnNum, tick)
            for objID in batLineup.posObjIDDict.values():
                batObj = batObjMgr.getBatObj(objID)
                TurnFightObjPerTurnStart(turnFight, batObj, turnNum)
                
        # 主公
        for faction, num in turnFight.actionSortList:
@@ -1156,20 +1023,11 @@
            batLineup = batFaction.getBatlineup(num)
            for posNum in range(batLineup.actionNum, PosNumMax + 1):
                batLineup.actionNum = posNum + 1
                if posNum not in batLineup.npcPosDict:
                if posNum not in batLineup.posObjIDDict:
                    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):
                objID = batLineup.posObjIDDict[posNum]
                batObj = batObjMgr.getBatObj(objID)
                if not OnObjAction(turnFight, batObj):
                    continue
                
                break
@@ -1184,7 +1042,7 @@
            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():
            for objID in batLineup.posObjIDDict.values():
                pass
            
        if turnFight.checkOverByKilled():
@@ -1212,14 +1070,15 @@
    #funcLineID = clientData.FuncLineID
    return
def GetObjName(gameObj):
    objName = gameObj.GetName()
    faction = GameObj.GetFaction(gameObj)
    posInfo = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo)
    heroID = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_HeroID)
def GetObjName(batObj):
    faction = batObj.faction
    num = batObj.lineupNum
    posNum = batObj.posNum
    heroID = batObj.heroID
    if not heroID:
        heroID = gameObj.GetNPCID()
    return "%s%s %s[%s-%s]" % ("A" if faction == Def_FactionA else "B", posInfo, objName, gameObj.GetID(), heroID)
        heroID = batObj.npcID
    objName = GameWorld.CodeToGbk(batObj.GetName())
    return "%s%s-%s [%s-%s] %s" % ("A" if faction == ChConfig.Def_FactionA else "B", num, posNum, batObj.GetID(), heroID, objName)
def EntryLogic(turnFight):
    ## 执行进场逻辑
@@ -1230,11 +1089,32 @@
    turnFight.enterLogic = True
    return
def TurnFightObjPerTurnStart(gameObj, tagObj, turnNum, tick):
def TurnFightObjPerTurnStart(turnFight, batObj, turnNum):
    ## 回合制战斗实例 - 每回合开始时处理
    if not gameObj:
    if not batObj:
        return
    
    if batObj.GetHP() <= 0:
        return
    curID = batObj.GetID()
    buffMgr = batObj.GetBuffManager()
    GameWorld.DebugLog("更新buff: curID=%s,buffCount=%s" % (curID, buffMgr.GetBuffCount()))
    for index in range(buffMgr.GetBuffCount()):
        buff = buffMgr.GetBuffByIndex(index)
        curRemainTime = buff.GetRemainTime()
        if not curRemainTime:
            # 永久buff不处理
            continue
        buffID = buff.GetBuffID()
        updRemainTime = curRemainTime - 1
        GameWorld.DebugLog("    更新buff剩余回合数: buffID=%s,updRemainTime=%s" % (buffID, updRemainTime))
        if updRemainTime > 0:
            buff.SetRemainTime(curRemainTime - 1)
            TurnBuff.SyncBuffRefresh(turnFight, batObj, buff)
        else:
            TurnBuff.SyncBuffDel(turnFight, batObj, buffID)
#    SetTimeline(gameObj, turnNum, 0)
#    # 重置连击、反击数
#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnComboNum, 0)
@@ -1272,190 +1152,201 @@
def AddTurnObjCureHP(curObj, srcObj, addValue, cureHP, skillID=0):
    ## 回合对象添加治疗值
    if not curObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
    # @param curObj: 获得治疗的对象
    # @param srcObj: 来自谁的治疗
    # @param addValue: 治疗值
    # @param cureHP: 实际回血量
    if not srcObj:
        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
    updStatValue = srcObj.StatCureValue(cureHP)
    GameWorld.DebugLog("        统计治疗: curID=%s,srcID=%s,skillID=%s,addValue=%s,cureHP=%s,updStatValue=%s"
                   % (curID, srcID, skillID, addValue, cureHP, updStatValue))
    return
def AddTurnObjHurtValue(curObj, tagObj, hurtType, hurtValue, lostHP, curSkill=None):
def AddTurnObjHurtValue(curBatObj, tagBatObj, hurtValue, lostHP, curSkill=None, isBounce=False):
    ## 回合对象添加伤害值
    if not curObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
        return
    curID = curObj.GetID()
    tagID = tagObj.GetID()
    # @param isBounce: 是否反弹伤害
    curID = curBatObj.GetID()
    tagID = tagBatObj.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
        updStatValue = curBatObj.StatHurtValue(hurtValue)
        GameWorld.DebugLog("        统计伤血: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,tagHP=%s,isBounce=%s"
                       % (curID, tagID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), isBounce))
        
        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
        if tagBatObj:
            updStatValue = tagBatObj.StatDefValue(lostHP)
            GameWorld.DebugLog("        统计承伤: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,curHP=%s,isBounce=%s"
                           % (tagID, curID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), isBounce))
    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 > 0 and curID != tagID:
        addXP = IpyGameDataPY.GetFuncCfg("AngerXP", 4)
        AddTurnFightXP(tagObj, addXP, "skillID:%s" % skillID)
        GameWorld.DebugLog("        自残: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,curHP=%s"
                           % (curID, tagID, skillID, hurtValue, lostHP, curBatObj.GetHP()))
    return
def AddTurnFightXP(gameObj, addXP, reason=""):
    ## 回合战斗增加XP
    if addXP <= 0 or not addXP:
        #GameWorld.DebugLog("        没有增加XP! curID=%s" % (gameObj.GetID()))
def OnObjAction(turnFight, curBatObj):
    ## 战斗单位行动
    if not curBatObj:
        return
    posNum = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo) % 10
    if posNum <= 0:
        #非主战单位不加
    curHP = curBatObj.GetHP()
    objID = curBatObj.GetID()
    if curHP <= 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))
    turnNum = turnFight.turnNum
    objName = GetObjName(curBatObj)
    # 是否可行动状态判断
    canAction = True
    if not canAction:
        GameWorld.DebugLog("★回合%s %s 当前状态不可行动!" % (turnNum, objName))
        return
    GameWorld.DebugLog("★回合%s %s 行动 : curHP=%s" % (turnNum, objName, curHP))
    turnFight.syncObjAction(turnNum, objID)
    curXP = curBatObj.GetXP()
    xpMax = IpyGameDataPY.GetFuncCfg("AngerXP", 2)
    skillManager = curBatObj.GetSkillManager()
    useSkillList = []
    #GameWorld.DebugLog('skillCount=%s' % skillManager.GetSkillCount(), npcID)
    for index in range(0, skillManager.GetSkillCount()):
        useSkill = skillManager.GetSkillByIndex(index)
        if not useSkill:
            continue
        if useSkill.GetFuncType() in [ChConfig.Def_SkillFuncType_AtkbackSkill]:
            #基础普攻不能主动释放,目前仅用于反击
            continue
        #被动技能无法使用
        if SkillCommon.isPassiveSkill(useSkill):
            continue
        #还在冷却时间内无法释放
        if useSkill.GetRemainTime():
            continue
        skillID = useSkill.GetSkillID()
        # 常规攻击优先xp
        if SkillCommon.isAngerSkill(useSkill):
            if curXP < xpMax:
                continue
            useCnt = -1 # xp技能优先释放
        else:
            useCnt = curBatObj.GetSkillUseCnt(skillID)
        useSkillList.append([useCnt, skillID, useSkill])
    useSkillList.sort() # 按使用次数优先升序排,使用次数低的优先判断使用
    #GameWorld.DebugLog('    技能使用顺序 = useSkillList%s' % str(useSkillList), npcID)
    for useInfo in useSkillList:
        useSkill = useInfo[-1]
        if TurnSkill.OnUseSkill(turnFight, curBatObj, useSkill):
            return True
    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
        skillManager = curNPC.GetSkillManager()
        for index in range(0, skillManager.GetSkillCount()):
            skill = skillManager.GetSkillByIndex(index)
            #已经到尾部了
            if not skill or skill.GetSkillTypeID() == 0:
                break
            if skill.GetFuncType() == ChConfig.Def_SkillFuncType_NormalAttack:
                useSkill = skill
                break
        atkOK = SkillShell.DoLogic_UseSkill(curNPC, tagObj, useSkill, 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 useSkill.GetFuncType() in [ChConfig.Def_SkillFuncType_NormalAttack]:
                #基础普攻不能主动释放,目前仅用于反击
                continue
            #被动技能无法使用
            if SkillCommon.isPassiveSkill(useSkill):
                continue
            #还在冷却时间内无法释放
            if SkillCommon.RefreshSkillRemainTime(useSkill, tick) != 0:
                continue
            skillTypeID = useSkill.GetSkillTypeID()
            # 常规攻击优先xp
            if SkillCommon.isAngerSkill(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)
        for useInfo in useSkillList:
            useSkill = useInfo[-1]
            #skillID = useSkill.GetSkillID()
            atkOK, tagObj = DoNPCUseSkill(curNPC, useSkill, tick)
            if atkOK:
                break
    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
    if not tagObj:
        return
    if GameObj.GetFaction(curNPC) == GameObj.GetFaction(tagObj):
        # 同阵营直接返回,不处理连击、反击
        return True
    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 CanAtkBack(curNPC, tagObj):
        DoAttack(tagObj, curNPC, tick, ChConfig.TurnBattleType_AtkBack)
        return True
    # 连击
    if CanCombo(curNPC, tagObj):
        DoAttack(curNPC, tagObj, tick, ChConfig.TurnBattleType_Combo, useSkill)
def DoAttack(curBatObj, tagBatObj, tick, turnBattleType=ChConfig.TurnBattleType_Normal, useSkill=None):
#    curID = curBatObj.GetID()
#    npcID = curBatObj.GetNPCID()
#    objName = GetObjName(curBatObj)
#    GameWorld.DebugLog("    ● %s DoAttack: curID=%s,,turnBattleType=%s" % (objName, curID, turnBattleType))
#
#    atkOK = False
#    curBatObj.SetDict(ChConfig.Def_Obj_Dict_TurnBattleType, turnBattleType)
#
#    if turnBattleType == ChConfig.TurnBattleType_AtkBack:
#        if not tagBatObj:
#            return
#        skillManager = curBatObj.GetSkillManager()
#        for index in range(0, skillManager.GetSkillCount()):
#            skill = skillManager.GetSkillByIndex(index)
#            if not skill:
#                continue
#            if skill.GetFuncType() == ChConfig.Def_SkillFuncType_AtkbackSkill:
#                useSkill = skill
#                break
#        atkOK = SkillShell.DoLogic_UseSkill(curBatObj, tagBatObj, useSkill, tick)
#    elif turnBattleType == ChConfig.TurnBattleType_Combo:
#        if not tagBatObj:
#            return
#        atkOK = SkillShell.DoLogic_UseSkill(curBatObj, tagBatObj, useSkill, tick)
#    else:
#        curXP = GameObj.GetXP(curBatObj)
#        xpMax = IpyGameDataPY.GetFuncCfg("AngerXP", 2)
#        skillManager = curBatObj.GetSkillManager()
#        useSkillList = []
#        #GameWorld.DebugLog('skillCount=%s' % skillManager.GetSkillCount(), npcID)
#        for index in range(0, skillManager.GetSkillCount()):
#            useSkill = skillManager.GetSkillByIndex(index)
#            if not useSkill:
#                continue
#            if useSkill.GetFuncType() in [ChConfig.Def_SkillFuncType_AtkbackSkill]:
#                #基础普攻不能主动释放,目前仅用于反击
#                continue
#            #被动技能无法使用
#            if SkillCommon.isPassiveSkill(useSkill):
#                continue
#            #还在冷却时间内无法释放
#            if useSkill.GetRemainTime():
#                continue
#            skillID = useSkill.GetSkillID()
#            # 常规攻击优先xp
#            if SkillCommon.isAngerSkill(useSkill) and turnBattleType == ChConfig.TurnBattleType_Normal:
#                if curXP < xpMax:
#                    continue
#                useCnt = -1 # xp技能优先释放
#            else:
#                useCnt = curBatObj.GetDictByKey(ChConfig.Def_NPC_Dict_SkillUseCnt % skillID) # 该技能已使用次数
#            useSkillList.append([useCnt, index, skillID, useSkill])
#
#        useSkillList.sort() # 按使用次数优先升序排,使用次数低的优先判断使用
#        #GameWorld.DebugLog('    技能使用顺序 = useSkillList%s' % str(useSkillList), npcID)
#
#        for useInfo in useSkillList:
#            useSkill = useInfo[-1]
#            #skillID = useSkill.GetSkillID()
#            atkOK, tagBatObj = OnUseSkill(curBatObj, useSkill, tick)
#            if atkOK:
#                break
#
#    curBatObj.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
#
#    if not tagObj:
#        return
#
#    if GameObj.GetFaction(curNPC) == GameObj.GetFaction(tagObj):
#        # 同阵营直接返回,不处理连击、反击
#        return True
#
#    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 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):
@@ -1512,224 +1403,58 @@
                       % (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 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().getTurnFight(curNPC.GetTFGUID())
#    if not turnFight:
#        return
#
#    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)
#
#    batObjMgr = BattleObj.GetBatObjMgr()
#    for tagNum in tagLineupNumList:
#        tagLineup = tagBatFaction.getBatlineup(tagNum)
#        tagPosNumList = [posNum] # 优先对位位置,再其他位置按顺序遍历
#        pNumList = tagLineup.posObjIDDict.keys()
#        pNumList.sort()
#        for pNum in pNumList:
#            if pNum > 0 and pNum not in tagPosNumList:
#                tagPosNumList.append(pNum)
#        for pNum in tagPosNumList:
#            batObj = batObjMgr.getBatObj(objID)
#            if not batObj:
#                continue
#            if batObj.GetHP( )<= 0:
#                continue
#            return batObj
#    return
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
    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 SetTurnObjKilled(gameObj, killer=None):
    if not gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
        #GameWorld.DebugLog("非回合战斗主体被击杀: curID=%s" % gameObj.GetID())
        return
def SetObjKilled(turnFight, gameObj, killer=None, useSkill=None):
    objID = gameObj.GetID()
    GameWorld.DebugLog("        %s 回合战斗主体被击杀: curID=%s" % (GetObjName(gameObj), objID))
    gameObj.SetCurAction(IPY_GameWorld.laNPCDie)
    if GameObj.GetHP(gameObj) != 0:
        GameObj.SetHP(gameObj, 0) # 回合制死亡仅设置为0,实例暂时不回收
    gameObj.SetVisible(False)
    gameObj.SetHP(0)
    killerObjID = killer.GetID() if killer else 0
    skillID = useSkill.GetSkillID() if useSkill else 0
    
    turnFight = GetTurnFightMgr().getNPCTurnFight(objID)
    if not turnFight:
        return True
    clientPack = ChPyNetSendPack.tagMCTurnFightObjDead()
    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagMCTurnFightObjDead)
    clientPack.ObjID = objID
    turnFight.addBatPack(clientPack)
    # 记录主动发起的玩家阵营击杀
    curPlayer = turnFight.curPlayer
    if killer and curPlayer and killer.GetDictByKey(ChConfig.Def_Obj_Dict_LineupPlayerID) == curPlayer.GetPlayerID():
        if objID not in turnFight.playerKillObjIDList:
            turnFight.playerKillObjIDList.append(objID)
        GameWorld.DebugLog("玩家单次击杀统计: %s" % turnFight.playerKillObjIDList)
    clientPack.KillerObjID = killerObjID
    clientPack.SkillID = skillID
    turnFight.addBatPack(clientPack)
    return True
def OnTurnfightAttackSuccess(curObj, tagObj, curSkill):
    ## 回合战斗攻击成功额外处理,AttackResult之前,一般处理技能相关
    if curObj.GetGameObjType() != IPY_GameWorld.gotNPC:
        return
    if not curObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
        return
    objID = curObj.GetID()
    turnFight = GetTurnFightMgr().getNPCTurnFight(objID)
    if not turnFight:
        return
    skillID = curSkill.GetSkillID() if curSkill else 0
    isXP = SkillCommon.isAngerSkill(curSkill)
    if isXP:
        GameObj.SetXP(curObj, 0)
    elif curSkill:
        if SkillCommon.isTurnNormalSkill(curSkill):
            addXP = IpyGameDataPY.GetFuncCfg("AngerXP", 3)
            AddTurnFightXP(curObj, addXP, "skillID:%s" % skillID)
    curPlayer = turnFight.curPlayer
    # 仅主动发起玩家阵容触发
    if curPlayer and curObj.GetDictByKey(ChConfig.Def_Obj_Dict_LineupPlayerID) == curPlayer.GetPlayerID():
        FBLogic.OnPlayerLineupAttackSuccess(curPlayer, curObj, tagObj, curSkill, turnFight.mapID, turnFight.funcLineID)
    return
def OnTurnfightAttackResult(curObj, tagObj, curSkill):
    ## 回合战斗攻击结果额外处理,AttackResult 之后,一般处理击杀结算相关
    if curObj.GetGameObjType() != IPY_GameWorld.gotNPC:
        return
    if not curObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo):
        return
    objID = curObj.GetID()
    turnFight = GetTurnFightMgr().getNPCTurnFight(objID)
    if not turnFight:
        return
    curPlayer = turnFight.curPlayer
    # 仅主动发起玩家阵容触发
    if curPlayer and curObj.GetDictByKey(ChConfig.Def_Obj_Dict_LineupPlayerID) == curPlayer.GetPlayerID():
        FBLogic.OnPlayerLineupAttackResult(curPlayer, curObj, tagObj, curSkill, turnFight.mapID, turnFight.funcLineID)
    turnFight.playerKillObjIDList = []
    return
def OnTurnAllOver(guid):
    ## 所有回合已经全部执行完毕
@@ -1743,10 +1468,10 @@
    
    if turnFight.playerID:
        # 玩家发起的,未击杀对方,算玩家输
        turnFight.winFaction = Def_FactionB
        turnFight.winFaction = ChConfig.Def_FactionB
    else:
        # 系统场次,按一定规则来,这里先随机
        turnFight.winFaction = random.choice([Def_FactionA, Def_FactionB])
        turnFight.winFaction = random.choice([ChConfig.Def_FactionA, ChConfig.Def_FactionB])
        
    DoTurnFightOver(guid)
    return
@@ -1763,6 +1488,7 @@
    GameWorld.DebugLog("--- 战斗结束处理 --- %s, winFaction=%s, costTime=%ss" % (guid, winFaction, turnFight.costTime))
    
    # 统计明细
    batObjMgr = BattleObj.GetBatObjMgr()
    statInfo = {}
    for faction in turnFight.factionDict.keys():
        if str(faction) not in statInfo:
@@ -1774,26 +1500,26 @@
                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()
                heroID = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_HeroID)
                atkHurt = hurtStatDict.get(objID, 0)
                defHurt = defStatDict.get(objID, 0)
                cureHP = cureStatDict.get(objID, 0)
            for posNum, objID in batLineup.posObjIDDict.items():
                batObj = batObjMgr.getBatObj(objID)
                if not batObj:
                    continue
                objID = batObj.GetID()
                npcID = batObj.GetNPCID()
                heroID = batObj.GetHeroID()
                atkHurt = batObj.hurtStat
                defHurt = batObj.defStat
                cureHP = batObj.cureStat
                GameWorld.DebugLog("    Pos:%s ID=%s-%s-%s,,HP=%s/%s, 输出=%s,承伤=%s,治疗=%s" 
                                   % (posNum, objID, npcID, heroID, GameObj.GetHP(curNPC), GameObj.GetMaxHP(curNPC), atkHurt, defHurt, cureHP))
                                   % (posNum, objID, npcID, heroID, batObj.GetHP(), batObj.GetMaxHP(), atkHurt, defHurt, cureHP))
                lineupStatInfo[str(posNum)] = {"ObjID":objID, "HeroID":heroID, "NPCID":npcID, "AtkHurt":atkHurt, "DefHurt":defHurt, "CureHP":cureHP}
                
    awardItemList = []
    playerID = turnFight.playerID
    if playerID:
        curPlayer = turnFight.curPlayer
        isWin = (winFaction == Def_FactionA)
        isWin = (winFaction == ChConfig.Def_FactionA)
        # 主线小怪
        if turnFight.mapID == ChConfig.Def_FBMapID_Main:
            OnOver_MainLevel(curPlayer, isWin, awardItemList)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -184,8 +184,6 @@
               }
# 属性ID对应0418刷新类型 {属性ID:[刷新同步类型, 是否支持超20亿的大数值], ...}
# 卡牌项目玩家/主公属性仅为中间层属性,并非最终属性,最终属性体现在卡牌上,暂定全部前端自己算
# 所以仅配置战斗场景需要同步的属性即可
CDBRefresh_AttrIDDict = {
                         AttrID_MaxHP:[IPY_PlayerDefine.CDBPlayerRefresh_MaxHP, 1],
                         AttrID_HP:[IPY_PlayerDefine.CDBPlayerRefresh_HP, 1],
@@ -772,6 +770,8 @@
Def_Skill_Effect_BoomSeedHurt = 804 # BUFF种子单层伤害
Def_Skill_Effect_StoreBlood = 809 # 将期间受到的伤害总值,用于最后回血,不影响伤害
Def_Skill_Effect_AttackReplace = 1009  #攻击计算,野外小怪伤害替换1010 (2018-03-07增加精英怪)
Def_Skill_Effect_Cure = 1000  #治疗
Def_Skill_Effect_Anger = 1001  #怒气增减偷
Def_Skill_Effect_Attack = 1010  #攻击计算
Def_Skill_Effect_LayerCnt = 1011 # BUFF层级数量 A值层数;B值:10位-是否非叠加属性,个位-层数处理方式0递增1递减;C值: 是否攻击减层
Def_Skill_Effect_MasterBuff = 1012 # 主从技能(同步buff持续时间)
@@ -951,6 +951,20 @@
#伤害类型
(
HurtType_Fail,              # 失败 - 如概率没有触发 0
HurtType_Normal,            # 普通伤害 1
HurtTYpe_Recovery,          # 回血 2
HurtType_3,
HurtType_4,
HurtType_Parry,             # 格挡 5
HurtType_IgnoreDef,         # 无视防御 6
HurtType_SuperHit,          # 暴击 7
HurtType_8,
HurtType_Miss,              # 闪避 9
) = range(10)
#伤害类型
(
Def_HurtType_Normal,         # 普通伤害 1
Def_HurtTYpe_Recovery,       # 回复 2
Def_HurtType_BounceHurt,     # 反弹伤害 3
@@ -969,7 +983,7 @@
Def_HurtType_ThumpHit,     # 重击 16
Def_HurtType_Yinji,     # 印记 17
Def_HurtType_Burn,     # 灼烧 18
) = range(1, 19)
) = range(1001, 1001 + 18)
#Def_HurtType_SuckBlood,      # 吸血 
(
@@ -1319,6 +1333,24 @@
Def_Skill_HappenState_LuckyHit = 0x0004  # 必会心一击
Def_Skill_HappenState_ThumpHit = 0x0008  # 必重击
# 技能目标 - 瞄准范围
(
SkillTagAim_All, # 全部 0
SkillTagAim_Relative, # 对位位置 1
SkillTagAim_FrontRow, # 前排  2
SkillTagAim_BackRow, # 后排  3
SkillTagAim_Vertical, # 竖排/纵排 4
SkillTagAim_Self, # 自己 5
SkillTagAim_MainSkill, # 继承主技能目标 6
) = range(7)
# 技能目标 - 细分
(
SkillTagAffect_None, # 无  0
SkillTagAffect_HPLowest, # 血量最低 1
SkillTagAffect_HPHighest, # 血量最高 2
SkillTagAffect_Death, # 死亡单位 3
) = range(4)
#技能施法目标
Def_UseSkillAim_Type = 3
@@ -1827,10 +1859,9 @@
Def_NstNull, Def_NstMoving, Def_NstDead, Def_NstAttack = range(4)
#-------------------------------#副本相关#------------------------
# 主线小怪
Def_FBMapID_Main = 1
# 主线Boss
Def_FBMapID_MainBoss = 2
Def_FBMapID_Main = 1 # 主线小怪
Def_FBMapID_MainBoss = 2 # 主线Boss
#创角新手村地图ID列表
Def_CreatRoleMapIDList = [10000]
@@ -2278,6 +2309,10 @@
DropOwnerType_Contend, # 争夺 8 第一个攻击的获得归属,击杀当前归属者的玩家成为新归属者
DropOwnerType_RankHurtPlayer, # 个人伤血排行奖励归属 9 根据玩家个人伤血排行给奖励,伤血不重置
) = range(10)
#阵营
Def_FactionA = 1
Def_FactionB = 2
#------------------------------------------------
#技能类型
@@ -3022,7 +3057,8 @@
TurnBattleType_Normal, # 常规攻击
TurnBattleType_Combo, # 连击
TurnBattleType_AtkBack, # 反击
) = range(3)
TurnBattleType_Pursue , # 追击
) = range(4)
Def_PerTurnTick = 1000 # 每回合等同于常规tick时长
@@ -4295,7 +4331,8 @@
#主线
Def_PDict_UnXiantaoCntExp = "UnXiantaoCntExp" # 累计未结算经验的战锤数
Def_PDict_UnXiantaoCntEquip = "UnXiantaoCntEquip" # 累计未结算掉落的战锤数
Def_PDict_UnXiantaoCntEquip = "UnXiantaoCntEquip" # 累计未结算装备掉落的战锤数
Def_PDict_UnXiantaoCntBooty = "UnXiantaoCntBooty_%s" # 累计未结算战利品掉落的战锤数,参数(itemID)
Def_PDict_BootyDropToday = "BootyDropToday_%s" # 今日已累计掉落战利品数量,参数(itemID)
#-------------------------------------------------------------------------------
@@ -5103,6 +5140,16 @@
) = range(27)
# 回合卡牌
(
Def_SkillFuncType_Common, #0为通用技能
Def_SkillFuncType_TurnNormaSkill,  #1 普攻技能
Def_SkillFuncType_AngerSkill,  #2 怒气技能
Def_SkillFuncType_PotentialSkill,  #3 潜能技能
Def_SkillFuncType_AtkbackSkill,  #4 反击技能
) = range(5)
# MMO项目 - 先保留,重新定义从1000开始,后续可陆续删除
(Def_SkillFuncType_Common, #0为通用技能
Def_SkillFuncType_FbSkill, #1为法宝功能获得的主动技能
Def_SkillFuncType_FbPassiveSkill, #2为法宝功能获得的被动技能
@@ -5126,8 +5173,7 @@
Def_SkillFuncType_ShentongSkill,     #20 神通技能
Def_SkillFuncType_ElfSkill,     #21 精怪技能
Def_SkillFuncType_GatherTheSoul,     #22 聚魂技能
Def_SkillFuncType_TurnNormaSkill,  #23 回合普攻技能
) = range(24)
) = range(1000, 1000 + 23)
# 受技能效果完全影响的怪, 对应 Def_BattleRelationType_CommNoBoss
Def_SkillAttack_NPCIsBoss = [ Def_NPCType_Ogre_Normal     ,  #平凡小怪 0    # c++ 定义为普通NPC视野刷新
@@ -5154,24 +5200,6 @@
ExpRateLimitType_Recover, # 资源找回
ExpRateLimitType_Sweep, # 扫荡
) = range(2)
# 技能功能类对应战斗力模块
Def_SkillFuncType_MFPType={
                           Def_SkillFuncType_FbSkill:ShareDefine.Def_MFPType_MagicWeapon1,
                           Def_SkillFuncType_FbPassiveSkill:ShareDefine.Def_MFPType_MagicWeapon2,
                           Def_SkillFuncType_FbSPSkill:ShareDefine.Def_MFPType_MagicWeapon1,
                           Def_SkillFuncType_GiftSkill:ShareDefine.Def_MFPType_Role,
                           Def_SkillFuncType_HorseSkill:ShareDefine.Def_MFPType_Horse,
                           Def_SkillFuncType_PetSkill:ShareDefine.Def_MFPType_Pet,
                           Def_SkillFuncType_PetOwnerSkill:ShareDefine.Def_MFPType_Pet,
                           Def_SkillFuncType_GWSkill:ShareDefine.Def_MFPType_Prestige,
                           Def_SkillFuncType_SuiteSkill:ShareDefine.Def_MFPType_Equip,
                           Def_SkillFuncType_TitleSkill:ShareDefine.Def_MFPType_Dienstgrad,
                           Def_SkillFuncType_LianTiSkill:ShareDefine.Def_MFPType_LianTi,
                           Def_SkillFuncType_ShentongSkill:ShareDefine.Def_MFPType_Shentong,
                           }
# 投资理财类型,和前端对应,从7开始
InvestTypeList = (
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -49840,6 +49840,142 @@
#------------------------------------------------------
# B4 29 Buff消失 #tagSCBuffDel
class  tagSCBuffDel(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("ObjID", c_int),
                  ("BuffID", c_int),
                  ("RelatedSkillID", c_int),    # 关联的技能ID,一般是主技能ID或由于某个技能释放引起的buff变更
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xB4
        self.SubCmd = 0x29
        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 = 0x29
        self.ObjID = 0
        self.BuffID = 0
        self.RelatedSkillID = 0
        return
    def GetLength(self):
        return sizeof(tagSCBuffDel)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// B4 29 Buff消失 //tagSCBuffDel:
                                Cmd:%s,
                                SubCmd:%s,
                                ObjID:%d,
                                BuffID:%d,
                                RelatedSkillID:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.ObjID,
                                self.BuffID,
                                self.RelatedSkillID
                                )
        return DumpString
m_NAtagSCBuffDel=tagSCBuffDel()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagSCBuffDel.Cmd,m_NAtagSCBuffDel.SubCmd))] = m_NAtagSCBuffDel
#------------------------------------------------------
# B4 28 Buff刷新 #tagSCBuffRefresh
class  tagSCBuffRefresh(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("ObjID", c_int),    # 谁身上的buff
                  ("BuffID", c_int),    # buffID,某个obj上的唯一buffID,不同的buffID可能skillID相同
                  ("SkillID", c_int),    # 该buff对应技能表ID
                  ("RelatedSkillID", c_int),    # 关联的技能ID,一般是主技能ID或由于某个技能释放引起的buff变更
                  ("LastTime", c_int),    # 剩余时长毫秒/回合数
                  ("Layer", c_ushort),    # 层数,不需要默认0
                  ("OwnerID", c_int),    # buff来源者,即施法方
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xB4
        self.SubCmd = 0x28
        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 = 0x28
        self.ObjID = 0
        self.BuffID = 0
        self.SkillID = 0
        self.RelatedSkillID = 0
        self.LastTime = 0
        self.Layer = 0
        self.OwnerID = 0
        return
    def GetLength(self):
        return sizeof(tagSCBuffRefresh)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// B4 28 Buff刷新 //tagSCBuffRefresh:
                                Cmd:%s,
                                SubCmd:%s,
                                ObjID:%d,
                                BuffID:%d,
                                SkillID:%d,
                                RelatedSkillID:%d,
                                LastTime:%d,
                                Layer:%d,
                                OwnerID:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.ObjID,
                                self.BuffID,
                                self.SkillID,
                                self.RelatedSkillID,
                                self.LastTime,
                                self.Layer,
                                self.OwnerID
                                )
        return DumpString
m_NAtagSCBuffRefresh=tagSCBuffRefresh()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagSCBuffRefresh.Cmd,m_NAtagSCBuffRefresh.SubCmd))] = m_NAtagSCBuffRefresh
#------------------------------------------------------
# B4 12 删除恶意攻击玩家 #tagMCDelMaliciousAtkPlayer
class  tagMCDelMaliciousAtkPlayer(Structure):
@@ -50032,6 +50168,90 @@
m_NAtagMCNPCSkillWarn=tagMCNPCSkillWarn()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCNPCSkillWarn.Head.Cmd,m_NAtagMCNPCSkillWarn.Head.SubCmd))] = m_NAtagMCNPCSkillWarn
#------------------------------------------------------
# B4 18 对象属性刷新展示 #tagSCObjPropertyRefreshView
class  tagSCObjPropertyRefreshView(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("ObjID", c_int),
                  ("RefreshType", c_ushort),    # 同0418刷新类型,如血量、怒气
                  ("Value", c_int),    # 更新值
                  ("ValueEx", c_int),    # 更新值,如果是大数值的此值为整除亿部分
                  ("DiffType", c_ubyte),    # 变化类型,0-减少;1-增加
                  ("DiffValue", c_int),    # 变化值
                  ("DiffValueEx", c_int),    # 变化值,如果是大数值的此值为整除亿部分
                  ("SkillID", c_int),    # 使用的技能表ID
                  ("RelatedSkillID", c_int),    # 关联的技能ID,一般是主技能ID,非主技能额外触发的为0
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xB4
        self.SubCmd = 0x18
        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 = 0x18
        self.ObjID = 0
        self.RefreshType = 0
        self.Value = 0
        self.ValueEx = 0
        self.DiffType = 0
        self.DiffValue = 0
        self.DiffValueEx = 0
        self.SkillID = 0
        self.RelatedSkillID = 0
        return
    def GetLength(self):
        return sizeof(tagSCObjPropertyRefreshView)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// B4 18 对象属性刷新展示 //tagSCObjPropertyRefreshView:
                                Cmd:%s,
                                SubCmd:%s,
                                ObjID:%d,
                                RefreshType:%d,
                                Value:%d,
                                ValueEx:%d,
                                DiffType:%d,
                                DiffValue:%d,
                                DiffValueEx:%d,
                                SkillID:%d,
                                RelatedSkillID:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.ObjID,
                                self.RefreshType,
                                self.Value,
                                self.ValueEx,
                                self.DiffType,
                                self.DiffValue,
                                self.DiffValueEx,
                                self.SkillID,
                                self.RelatedSkillID
                                )
        return DumpString
m_NAtagSCObjPropertyRefreshView=tagSCObjPropertyRefreshView()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagSCObjPropertyRefreshView.Cmd,m_NAtagSCObjPropertyRefreshView.SubCmd))] = m_NAtagSCObjPropertyRefreshView
#------------------------------------------------------
@@ -51241,6 +51461,8 @@
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("ObjID", c_int),    
                  ("KillerObjID", c_int),    # 被谁击杀的,可能为0
                  ("SkillID", c_int),    # 被什么技能击杀,可能为0
                  ]
    def __init__(self):
@@ -51258,6 +51480,8 @@
        self.Cmd = 0xB4
        self.SubCmd = 0x22
        self.ObjID = 0
        self.KillerObjID = 0
        self.SkillID = 0
        return
    def GetLength(self):
@@ -51270,12 +51494,16 @@
        DumpString = '''// B4 22 回合战斗对象死亡 //tagMCTurnFightObjDead:
                                Cmd:%s,
                                SubCmd:%s,
                                ObjID:%d
                                ObjID:%d,
                                KillerObjID:%d,
                                SkillID:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.ObjID
                                self.ObjID,
                                self.KillerObjID,
                                self.SkillID
                                )
        return DumpString
@@ -51569,6 +51797,251 @@
#------------------------------------------------------
# B4 26 回合战斗标签 #tagSCTurnFightTag
class  tagSCTurnFightTag(Structure):
    Head = tagHead()
    Len = 0    #(BYTE Len)
    Tag = ""    #(String Tag)// 标签,释放技能的标签格式: Skill_objID_skillID,其他标签格式可再扩展
    Sign = 0    #(BYTE Sign)// 0-标签头;1-标签尾;
    data = None
    def __init__(self):
        self.Clear()
        self.Head.Cmd = 0xB4
        self.Head.SubCmd = 0x26
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        _pos = self.Head.ReadData(_lpData, _pos)
        self.Len,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.Tag,_pos = CommFunc.ReadString(_lpData, _pos,self.Len)
        self.Sign,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        return _pos
    def Clear(self):
        self.Head = tagHead()
        self.Head.Clear()
        self.Head.Cmd = 0xB4
        self.Head.SubCmd = 0x26
        self.Len = 0
        self.Tag = ""
        self.Sign = 0
        return
    def GetLength(self):
        length = 0
        length += self.Head.GetLength()
        length += 1
        length += len(self.Tag)
        length += 1
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
        data = CommFunc.WriteBYTE(data, self.Len)
        data = CommFunc.WriteString(data, self.Len, self.Tag)
        data = CommFunc.WriteBYTE(data, self.Sign)
        return data
    def OutputString(self):
        DumpString = '''
                                Head:%s,
                                Len:%d,
                                Tag:%s,
                                Sign:%d
                                '''\
                                %(
                                self.Head.OutputString(),
                                self.Len,
                                self.Tag,
                                self.Sign
                                )
        return DumpString
m_NAtagSCTurnFightTag=tagSCTurnFightTag()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagSCTurnFightTag.Head.Cmd,m_NAtagSCTurnFightTag.Head.SubCmd))] = m_NAtagSCTurnFightTag
#------------------------------------------------------
# B4 27 使用技能 #tagSCUseSkill
class  tagSCUseSkillHurt(Structure):
    _pack_ = 1
    _fields_ = [
                  ("ObjID", c_int),
                  ("AttackTypes", c_int),    # 飘血类型汇总,支持多种类型并存,如无视防御且暴击同时被格挡,二进制或运算最终值;0-失败;1-普通;2-回血;5-格挡;6-无视防御;7-暴击;9-闪避
                  ("HurtHP", c_int),    # 飘血值,求余亿部分
                  ("HurtHPEx", c_int),    # 飘血值,整除亿部分
                  ("CurHP", c_int),    # 更新剩余血量,求余亿部分
                  ("CurHPEx", c_int),    # 更新剩余血量,整除亿部分
                  ("SuckHP", c_int),    # 本次伤害转化的吸血量
                  ("BounceHP", c_int),    # 本次伤害反弹的伤害量
                  ]
    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.AttackTypes = 0
        self.HurtHP = 0
        self.HurtHPEx = 0
        self.CurHP = 0
        self.CurHPEx = 0
        self.SuckHP = 0
        self.BounceHP = 0
        return
    def GetLength(self):
        return sizeof(tagSCUseSkillHurt)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// B4 27 使用技能 //tagSCUseSkill:
                                ObjID:%d,
                                AttackTypes:%d,
                                HurtHP:%d,
                                HurtHPEx:%d,
                                CurHP:%d,
                                CurHPEx:%d,
                                SuckHP:%d,
                                BounceHP:%d
                                '''\
                                %(
                                self.ObjID,
                                self.AttackTypes,
                                self.HurtHP,
                                self.HurtHPEx,
                                self.CurHP,
                                self.CurHPEx,
                                self.SuckHP,
                                self.BounceHP
                                )
        return DumpString
class  tagSCUseSkill(Structure):
    Head = tagHead()
    ObjID = 0    #(DWORD ObjID)
    PMType = 0    #(BYTE PMType)// 物法类型 0或1-物理;2-法术
    BattleType = 0    #(BYTE BattleType)// 战斗类型 0-常规;1-连击;2-反击;3-追击
    CurHP = 0    #(DWORD CurHP)// 释放技能后剩余血量,吸血、反弹可能引起变化,求余亿部分
    CurHPEx = 0    #(DWORD CurHPEx)// 释放技能后剩余血量,吸血、反弹可能引起变化,整除亿部分
    SkillID = 0    #(DWORD SkillID)
    HurtCount = 0    #(BYTE HurtCount)//伤害数目
    HurtList = list()    #(vector<tagSCUseSkillHurt> HurtList)//size = HurtCount
    data = None
    def __init__(self):
        self.Clear()
        self.Head.Cmd = 0xB4
        self.Head.SubCmd = 0x27
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        _pos = self.Head.ReadData(_lpData, _pos)
        self.ObjID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.PMType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.BattleType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.CurHP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.CurHPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.SkillID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.HurtCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.HurtCount):
            temHurtList = tagSCUseSkillHurt()
            _pos = temHurtList.ReadData(_lpData, _pos)
            self.HurtList.append(temHurtList)
        return _pos
    def Clear(self):
        self.Head = tagHead()
        self.Head.Clear()
        self.Head.Cmd = 0xB4
        self.Head.SubCmd = 0x27
        self.ObjID = 0
        self.PMType = 0
        self.BattleType = 0
        self.CurHP = 0
        self.CurHPEx = 0
        self.SkillID = 0
        self.HurtCount = 0
        self.HurtList = list()
        return
    def GetLength(self):
        length = 0
        length += self.Head.GetLength()
        length += 4
        length += 1
        length += 1
        length += 4
        length += 4
        length += 4
        length += 1
        for i in range(self.HurtCount):
            length += self.HurtList[i].GetLength()
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
        data = CommFunc.WriteDWORD(data, self.ObjID)
        data = CommFunc.WriteBYTE(data, self.PMType)
        data = CommFunc.WriteBYTE(data, self.BattleType)
        data = CommFunc.WriteDWORD(data, self.CurHP)
        data = CommFunc.WriteDWORD(data, self.CurHPEx)
        data = CommFunc.WriteDWORD(data, self.SkillID)
        data = CommFunc.WriteBYTE(data, self.HurtCount)
        for i in range(self.HurtCount):
            data = CommFunc.WriteString(data, self.HurtList[i].GetLength(), self.HurtList[i].GetBuffer())
        return data
    def OutputString(self):
        DumpString = '''
                                Head:%s,
                                ObjID:%d,
                                PMType:%d,
                                BattleType:%d,
                                CurHP:%d,
                                CurHPEx:%d,
                                SkillID:%d,
                                HurtCount:%d,
                                HurtList:%s
                                '''\
                                %(
                                self.Head.OutputString(),
                                self.ObjID,
                                self.PMType,
                                self.BattleType,
                                self.CurHP,
                                self.CurHPEx,
                                self.SkillID,
                                self.HurtCount,
                                "..."
                                )
        return DumpString
m_NAtagSCUseSkill=tagSCUseSkill()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagSCUseSkill.Head.Cmd,m_NAtagSCUseSkill.Head.SubCmd))] = m_NAtagSCUseSkill
#------------------------------------------------------
# B5 04 拍卖行新上架拍品 #tagGCAddAuctionItemInfo
class  tagGCAddAuctionItem(Structure):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Hero.py
@@ -157,8 +157,10 @@
def __oneKeyLineup(curPlayer, msgList):
    ## 阵容上阵: Hero f 阵容ID [武将ID ...]
    lineupID = msgList[1] if len(msgList) > 1 else 0
    heroIDList = list(set(msgList[2:])) # 去重,单阵容武将ID不能重复
    heroIDList = []
    for heroID in msgList[2:]: # 去重,单阵容武将ID不能重复
        if heroID not in heroIDList:
            heroIDList.append(heroID)
    if lineupID not in ShareDefine.LineupList:
        GameWorld.DebugAnswer(curPlayer, "不存在该阵容: %s" % lineupID)
        return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py
@@ -25,7 +25,7 @@
    if not gmList:
        GameWorld.DebugAnswer(curPlayer, "重置主线: MainLevel 0")
        GameWorld.DebugAnswer(curPlayer, "设置主线: MainLevel 章节 关卡 波")
        GameWorld.DebugAnswer(curPlayer, "测试掉落: MainLevel d 击杀数")
        GameWorld.DebugAnswer(curPlayer, "测试掉落: MainLevel d 战锤数")
        GameWorld.DebugAnswer(curPlayer, "重置战利: MainLevel b 0")
        GameWorld.DebugAnswer(curPlayer, "设置战利: MainLevel b 战利品ID 已掉落个数")
        return
@@ -33,8 +33,8 @@
    value = gmList[0]
    
    if value == "d":
        killCnt = gmList[1] if len(gmList) > 1 else 1
        GameLogic_MainLevel.GMTestKillDrop(curPlayer, killCnt)
        unXiantao = gmList[1] if len(gmList) > 1 else 1
        GameLogic_MainLevel.GMTestKillDrop(curPlayer, unXiantao)
        return
    
    if value == "b":
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameObj.py
@@ -533,9 +533,9 @@
    else:
        sendPack.Value = value
        sendPack.ValueEx = 0
    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(gameObj.GetID())
    if turnFight:
        turnFight.addBatPack(sendPack)
        return
    #turnFight = TurnAttack.GetTurnFightMgr().getTurnFight(gameObj.GetTFGUID())
    #if turnFight:
    #    turnFight.addBatPack(sendPack)
    #    return
    gameObj.NotifyAll(sendPack.GetBuffer(), sendPack.GetLength())
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -2409,25 +2409,14 @@
    
    return callFunc(curPlayer, mapID, funcLineID, tagType, tagID, valueList)
def OnPlayerLineupAttackSuccess(curPlayer, atkObj, defObj, curSkill, mapID, funcLineID):
    ## 回合战斗主动发起的玩家阵容释放技能成功
    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnPlayerLineupAttackSuccess"))
    if callFunc:
        callFunc(curPlayer, atkObj, defObj, curSkill, mapID, funcLineID)
    return
def OnPlayerLineupAttackResult(curPlayer, atkObj, defObj, curSkill, mapID, funcLineID):
    ## 回合战斗主动发起的玩家阵容攻击结果额外处理
def OnPlayerLineupAttackResult(curPlayer, atkObj, killObjIDList, useSkill, mapID, funcLineID):
    ## 回合战斗主动发起的玩家阵容攻击结果额外处理 ,一般处理副本相关的掉落、奖励等
    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
    
    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnPlayerLineupAttackResult"))
    
    if callFunc:
        callFunc(curPlayer, atkObj, defObj, curSkill, mapID, funcLineID)
        callFunc(curPlayer, atkObj, killObjIDList, useSkill, mapID, funcLineID)
        
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevel.py
@@ -18,17 +18,16 @@
import ChConfig
import GameWorld
import ShareDefine
import SkillCommon
import IpyGameDataPY
import PlayerControl
import ChPyNetSendPack
import ItemControler
import IPY_GameWorld
import NetPackCommon
import TurnAttack
import ItemCommon
import NPCCommon
import ChEquip
import ObjPool
import random
@@ -43,6 +42,7 @@
def ResetBootyDropToday(curPlayer):
    bootyItemIDList = GetBootyItemIDList()
    for itemID in bootyItemIDList:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntBooty % itemID, 0)
        if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BootyDropToday % itemID):
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_BootyDropToday % itemID, 0)
    SyncDropBootyInfo(curPlayer)
@@ -64,63 +64,21 @@
    ipyData = ipyDataMgr.GetMainChapterByIndex(chapterCount - 1)
    return [booty[0] for booty in ipyData.GetDailyBootyUpperList()]
def OnPlayerLineupAttackSuccess(curPlayer, atkObj, defObj, curSkill, mapID, funcLineID):
    ## 回合战斗主动发起的玩家阵容释放技能成功
def OnPlayerLineupAttackResult(curPlayer, atkObj, killObjIDList, useSkill, mapID, funcLineID):
    ## 回合战斗主动发起的玩家阵容攻击结果额外处理 ,一般处理副本相关的掉落、奖励等
    
    if mapID == ChConfig.Def_FBMapID_Main:
        __doCostZhanchui(curPlayer, atkObj, curSkill)
        __doKillAward(curPlayer, atkObj, killObjIDList)
        
    return
def __doCostZhanchui(curPlayer, atkObj, curSkill):
    ## 扣除战锤消耗
    costZhanchui = 0
    isXP = SkillCommon.isAngerSkill(curSkill)
    turnBattleType = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnBattleType)
    if isXP:
        costZhanchui = IpyGameDataPY.GetFuncCfg("ZhanchuiCost", 2)
    elif turnBattleType == ChConfig.TurnBattleType_Combo:
        costZhanchui = IpyGameDataPY.GetFuncCfg("ZhanchuiCost", 3)
    elif turnBattleType == ChConfig.TurnBattleType_AtkBack:
        costZhanchui = IpyGameDataPY.GetFuncCfg("ZhanchuiCost", 4)
    elif SkillCommon.isTurnNormalSkill(curSkill):
        costZhanchui = IpyGameDataPY.GetFuncCfg("ZhanchuiCost", 1)
    if costZhanchui <= 0:
        return
    fightPoint = max(curPlayer.GetFightPoint(), 1) # 主线战斗消耗倍值,默认1
    costZhanchuiTotal = costZhanchui * fightPoint
    if not PlayerControl.PayMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, costZhanchuiTotal, isNotify=False):
        # 不足时,有多少扣多少
        nowMoney = PlayerControl.GetMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao)
        PlayerControl.PayMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, min(nowMoney, costZhanchuiTotal), isNotify=False)
    return
def OnPlayerLineupAttackResult(curPlayer, atkObj, defObj, curSkill, mapID, funcLineID):
    if mapID == ChConfig.Def_FBMapID_Main:
        __doKillAward(curPlayer, atkObj, mapID, funcLineID)
    return
def __doKillAward(curPlayer, atkObj, mapID, funcLineID):
def __doKillAward(curPlayer, atkObj, killObjIDList):
    ## 计算击杀奖励
    turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(atkObj.GetID())
    if not turnFight:
    if not killObjIDList:
        GameWorld.DebugLog("没有击杀不需要处理!")
        return
    unXiantaoCntExp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntExp)
    if not turnFight.playerKillObjIDList:
        unXiantaoCntEquip = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip)
        GameWorld.DebugLog("没有击杀不需要处理! unXiantaoCntExp=%s,unXiantaoCntEquip=%s" % (unXiantaoCntExp, unXiantaoCntEquip))
        return
    killCnt = len(turnFight.playerKillObjIDList)
    # 直接重置,防止异常时重复结算
    turnFight.playerKillObjIDList = []
    # 结算经验
    unXiantaoCntExp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntExp)
    if unXiantaoCntExp:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntExp, 0)
        perExp = IpyGameDataPY.GetFuncCfg("Mainline", 1) # 每个战锤增加经验
@@ -128,60 +86,68 @@
        GameWorld.DebugLog("增加经验: totalExp=%s,unXiantaoCntExp=%s" % (totalExp, unXiantaoCntExp))
        PlayerControl.PlayerControl(curPlayer).AddExp(totalExp, ShareDefine.Def_ViewExpType_KillNPC)
        
    __doMainDrop(curPlayer, killCnt)
    __doMainDrop(curPlayer)
    return
def __doMainDrop(curPlayer, killCnt):
def __doMainDrop(curPlayer):
    # 装备掉落
    __doDropEquip(curPlayer)
    
    playerID = curPlayer.GetPlayerID()
    DailyBootyUpperList, BootyWeightList = [], []
    DailyBootyUpperList = []
    chapterID = PlayerControl.GetMainLevelNowInfo(curPlayer)[0]
    chapterIpyData = IpyGameDataPY.GetIpyGameData("MainChapter", chapterID)
    if chapterIpyData:
        DailyBootyUpperList = chapterIpyData.GetDailyBootyUpperList()
        BootyWeightList = chapterIpyData.GetBootyWeightList()
    bootyDropUpperDict = {k:v for k, v in DailyBootyUpperList}
    GameWorld.DebugLog("可掉落战利品上限: chapterID=%s,%s,killCnt=%s" % (chapterID, bootyDropUpperDict, killCnt), playerID)
    GameWorld.DebugLog("可掉落战利品上限: chapterID=%s, %s" % (chapterID, DailyBootyUpperList), playerID)
    
    # 其他战利品掉落
    for _ in range(killCnt):
        dropInfo = GameWorld.GetResultByWeightList(BootyWeightList)
        if not dropInfo:
            continue
        itemID = dropInfo[0]
        if not itemID:
            GameWorld.DebugLog("本次不掉落战利品!", playerID)
            continue
        if itemID not in bootyDropUpperDict:
            GameWorld.DebugLog("该战利品未解锁! itemID=%s" % itemID, playerID)
    bootyDropNeedDict = IpyGameDataPY.GetFuncEvalCfg("MainBootyDrop", 1, {})
    bootyDropCntDict = IpyGameDataPY.GetFuncEvalCfg("MainBootyDrop", 2, {})
    for itemID, dropUpper in DailyBootyUpperList:
        if dropUpper <= 0:
            continue
        todyDropCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BootyDropToday % itemID)
        dropUpper = bootyDropUpperDict.get(itemID, 0)
        if todyDropCnt >= dropUpper:
            GameWorld.DebugLog("战利品已达今日掉落上限! itemID=%s,todyDropCnt=%s >= %s" % (itemID, todyDropCnt, dropUpper), playerID)
            continue
        if itemID not in bootyDropNeedDict or itemID not in bootyDropCntDict:
            continue
        dropOneNeed = bootyDropNeedDict[itemID]
        unXiantaoCntBooty = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntBooty % itemID)
        dropBootyCnt = unXiantaoCntBooty / dropOneNeed
        if dropBootyCnt <= 0:
            continue
        
        dropMin = dropInfo[1] if len(dropInfo) > 1 else 1
        dropMax = dropInfo[2] if len(dropInfo) > 2 else 1
        dropCntRange = bootyDropCntDict[itemID]
        if not isinstance(dropCntRange, (list, tuple)) or len(dropCntRange) != 2:
            continue
        dropMin = dropCntRange[0]
        dropMax = dropCntRange[1]
        
        if dropMin == dropMax:
            dropCnt = dropMin
        else:
            dropCnt = random.randint(dropMin, dropMax)
        dropCnt = min(dropCnt, dropUpper - todyDropCnt)
        dropCntTotal = 0
        for _ in range(dropBootyCnt):
            if dropMin == dropMax:
                dropCnt = dropMin
            else:
                dropCnt = random.randint(dropMin, dropMax)
            dropCntTotal += dropCnt
        dropCntTotal = min(dropCntTotal, dropUpper - todyDropCnt)
        if dropCntTotal <= 0:
            continue
        
        GameWorld.DebugLog("掉落战利品! itemID=%s,dropCnt=%s" % (itemID, dropCnt), playerID)
        curItem = ItemControler.GetOutPutItemObj(itemID, dropCnt, False, curPlayer=curPlayer)
        GameWorld.DebugLog("掉落战利品! itemID=%s,unXiantaoCntBooty=%s,次数=%s,dropCntTotal=%s" % (itemID, unXiantaoCntBooty, dropBootyCnt, dropCntTotal), playerID)
        curItem = ItemControler.GetOutPutItemObj(itemID, dropCntTotal, False, curPlayer=curPlayer)
        if curItem == None:
            continue
        curItem.SetIsBind(1) # 为1时代表是掉落
        if not ItemControler.DoLogic_PutItemInPack(curPlayer, curItem, packIndexList=[IPY_GameWorld.rptIdentify]):
            continue
        SetBootyDropToday(curPlayer, itemID, todyDropCnt + dropCnt)
        unXiantaoCntBooty = unXiantaoCntBooty % dropOneNeed
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntBooty % itemID, unXiantaoCntBooty)
        SetBootyDropToday(curPlayer, itemID, todyDropCnt + dropCntTotal)
    return
def __doDropEquip(curPlayer):
@@ -243,14 +209,23 @@
        
    return
def GMTestKillDrop(curPlayer, killCnt):
def GMTestKillDrop(curPlayer, unXiantao):
    ## GM测试掉落
    unXiantaoCntEquip = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip) + killCnt
    unXiantaoCntEquip = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip) + unXiantao
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntEquip, unXiantaoCntEquip)
    __doMainDrop(curPlayer, killCnt)
    unXiantaoCntEquip = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip)
    GameWorld.DebugAnswer(curPlayer, "剩余未结算装备掉落战锤数:%s" % unXiantaoCntEquip)
    GameWorld.DebugAnswer(curPlayer, "未结算装备战锤数: %s" % unXiantaoCntEquip)
    chapterID = PlayerControl.GetMainLevelNowInfo(curPlayer)[0]
    chapterIpyData = IpyGameDataPY.GetIpyGameData("MainChapter", chapterID)
    if chapterIpyData:
        DailyBootyUpperList = chapterIpyData.GetDailyBootyUpperList()
        for itemID, upperCnt in DailyBootyUpperList:
            if upperCnt <= 0:
                continue
            unXiantaoCntBooty = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntBooty % itemID) + unXiantao
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntBooty % itemID, unXiantaoCntBooty)
            GameWorld.DebugAnswer(curPlayer, "未结算战利品(%s)战锤数: %s" % (itemID, unXiantaoCntBooty))
    __doMainDrop(curPlayer)
    return
#// B4 15 主线掉落物品操作 #tagCSMainDropItemOP
@@ -406,10 +381,11 @@
        syncItemIDList = GetBootyItemIDList()
    else:
        syncItemIDList = [itemID]
    clientPack = ChPyNetSendPack.tagSCDropBootyInfo()
    poolMgr = ObjPool.GetPoolMgr()
    clientPack = poolMgr.acquire(ChPyNetSendPack.tagSCDropBootyInfo)
    clientPack.DropBootyList = []
    for itemID in syncItemIDList:
        dropBooty = ChPyNetSendPack.tagSCDropBooty()
        dropBooty = poolMgr.acquire(ChPyNetSendPack.tagSCDropBooty)
        dropBooty.ItemID = itemID
        dropBooty.TodayDropCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BootyDropToday % itemID)
        clientPack.DropBootyList.append(dropBooty)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldEvent.py
@@ -26,6 +26,8 @@
import ShareDefine
import PlayerTeam
import PyGameData
import BattleObj
import ObjPool
import datetime
import time
@@ -265,6 +267,8 @@
    #GameWorldActionControl.Dispose_DailyActionState()
    #GameWorldActionControl.Dispose_FBStateTime()
    PlayerOnline.OnMinute()
    BattleObj.OnMinute()
    ObjPool.OnMinute()
    
    PlayerTeam.OnCheckTeamPlayerDisconnectTimeout(tick)
    __CheckIpyDataRecycle(curTime)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -56,8 +56,64 @@
                        ("BYTE", "OPLimitInAct", 0),
                        ),
                "NPC":(
                        ("DWORD", "NPCID", 1),
                        ("char", "NPCName", 0),
                        ("BYTE", "Country", 0),
                        ("BYTE", "AtkDistType", 0),
                        ("WORD", "LV", 0),
                        ("DWORD", "Atk", 0),
                        ("DWORD", "Def", 0),
                        ("DWORD", "MaxHP", 0),
                        ("list", "SkillIDList", 0),
                        ("DWORD", "FinalDamPer", 0),
                        ("DWORD", "FinalDamPerDef", 0),
                        ("DWORD", "MissRate", 0),
                        ("DWORD", "MissRateDef", 0),
                        ("DWORD", "SuperHitRate", 0),
                        ("DWORD", "SuperHitRateDef", 0),
                        ("DWORD", "StunRate", 0),
                        ("DWORD", "StunRateDef", 0),
                        ("DWORD", "ComboRate", 0),
                        ("DWORD", "ComboRateDef", 0),
                        ("DWORD", "ParryRate", 0),
                        ("DWORD", "ParryRateDef", 0),
                        ("DWORD", "SuckHPPer", 0),
                        ("DWORD", "SuckHPPerDef", 0),
                        ("dict", "SpecAttrInfo", 0),
                        ),
                "Skill":(
                        ("DWORD", "SkillID", 1),
                        ("DWORD", "SkillTypeID", 0),
                        ("WORD", "SkillMaxLV", 0),
                        ("char", "SkillName", 0),
                        ("BYTE", "FuncType", 0),
                        ("BYTE", "SkillType", 0),
                        ("BYTE", "HurtType", 0),
                        ("BYTE", "AtkType", 0),
                        ("BYTE", "TagAim", 0),
                        ("BYTE", "TagFriendly", 0),
                        ("BYTE", "TagAffect", 0),
                        ("BYTE", "TagCount", 0),
                        ("WORD", "HappenRate", 0),
                        ("WORD", "LastTime", 0),
                        ("WORD", "CoolDownTime", 0),
                        ("WORD", "Priority", 0),
                        ("DWORD", "EffectID1", 0),
                        ("list", "EffectValues1", 0),
                        ("DWORD", "EffectID2", 0),
                        ("list", "EffectValues2", 0),
                        ("DWORD", "EffectID3", 0),
                        ("list", "EffectValues3", 0),
                        ("DWORD", "ConnSkill", 0),
                        ("list", "EnhanceSkillList", 0),
                        ("DWORD", "FightPower", 0),
                        ),
                "Hero":(
                        ("DWORD", "HeroID", 1),
                        ("char", "Name", 0),
                        ("BYTE", "Country", 0),
                        ("BYTE", "Quality", 0),
                        ("BYTE", "AtkDistType", 0),
@@ -161,7 +217,6 @@
                "MainChapter":(
                        ("BYTE", "ChapterID", 1),
                        ("list", "DailyBootyUpperList", 0),
                        ("list", "BootyWeightList", 0),
                        ),
                "MainLevel":(
@@ -730,33 +785,6 @@
                        ("float", "AttrPer", 0),
                        ("dict", "AttrSpecDict", 0),
                        ("dict", "AttrExDict", 0),
                        ),
                "NPCEx":(
                        ("DWORD", "NPCID", 1),
                        ("BYTE", "FightPowerLackAtkLimit", 0),
                        ("DWORD", "SuppressFightPower", 0),
                        ("BYTE", "AtkDistType", 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":(
@@ -2840,6 +2868,71 @@
    def GetSortReverse(self): return self.attrTuple[6] # 是否倒序 BYTE
    def GetOPLimitInAct(self): return self.attrTuple[7] # 活动期间限制队伍操作 BYTE
# NPC表
class IPY_NPC():
    def __init__(self):
        self.attrTuple = None
        return
    def GetNPCID(self): return self.attrTuple[0] # NPCID DWORD
    def GetNPCName(self): return self.attrTuple[1] # 名称 char
    def GetCountry(self): return self.attrTuple[2] # 国家 BYTE
    def GetAtkDistType(self): return self.attrTuple[3] # 远近类型;1-近战;2-远程 BYTE
    def GetLV(self): return self.attrTuple[4] # 等级 WORD
    def GetAtk(self): return self.attrTuple[5] # 攻击力 DWORD
    def GetDef(self): return self.attrTuple[6] # 防御值 DWORD
    def GetMaxHP(self): return self.attrTuple[7] # 最大生命值,可超过20E DWORD
    def GetSkillIDList(self): return self.attrTuple[8] # 技能ID列表 list
    def GetFinalDamPer(self): return self.attrTuple[9] # 最终增伤 DWORD
    def GetFinalDamPerDef(self): return self.attrTuple[10] # 最终减伤 DWORD
    def GetMissRate(self): return self.attrTuple[11] # 闪避概率 DWORD
    def GetMissRateDef(self): return self.attrTuple[12] # 抗闪避概率 DWORD
    def GetSuperHitRate(self): return self.attrTuple[13] # 暴击概率 DWORD
    def GetSuperHitRateDef(self): return self.attrTuple[14] # 抗暴击概率 DWORD
    def GetStunRate(self): return self.attrTuple[15] # 击晕概率 DWORD
    def GetStunRateDef(self): return self.attrTuple[16] # 抗击晕概率 DWORD
    def GetComboRate(self): return self.attrTuple[17] # 连击概率 DWORD
    def GetComboRateDef(self): return self.attrTuple[18] # 抗连击概率 DWORD
    def GetParryRate(self): return self.attrTuple[19] # 格挡概率 DWORD
    def GetParryRateDef(self): return self.attrTuple[20] # 抗格挡概率 DWORD
    def GetSuckHPPer(self): return self.attrTuple[21] # 吸血比率 DWORD
    def GetSuckHPPerDef(self): return self.attrTuple[22] # 抗吸血比率 DWORD
    def GetSpecAttrInfo(self): return self.attrTuple[23] # 特殊属性信息 {"属性ID":值, ...} dict
# 技能表
class IPY_Skill():
    def __init__(self):
        self.attrTuple = None
        return
    def GetSkillID(self): return self.attrTuple[0] # 技能ID DWORD
    def GetSkillTypeID(self): return self.attrTuple[1] # 技能TypeID DWORD
    def GetSkillMaxLV(self): return self.attrTuple[2] # 最高等级 WORD
    def GetSkillName(self): return self.attrTuple[3] # 技能名 char
    def GetFuncType(self): return self.attrTuple[4] # 功能分类 BYTE
    def GetSkillType(self): return self.attrTuple[5] # 技能类型 BYTE
    def GetHurtType(self): return self.attrTuple[6] # 伤害类型 BYTE
    def GetAtkType(self): return self.attrTuple[7] # 释放方式 BYTE
    def GetTagAim(self): return self.attrTuple[8] # 瞄准位置 BYTE
    def GetTagFriendly(self): return self.attrTuple[9] # 敌我目标 BYTE
    def GetTagAffect(self): return self.attrTuple[10] # 目标细分 BYTE
    def GetTagCount(self): return self.attrTuple[11] # 目标个数 BYTE
    def GetHappenRate(self): return self.attrTuple[12] # 释放或添加几率 WORD
    def GetLastTime(self): return self.attrTuple[13] # 持续时间 WORD
    def GetCoolDownTime(self): return self.attrTuple[14] # 冷却时间 WORD
    def GetPriority(self): return self.attrTuple[15] # 优先级 WORD
    def GetEffectID1(self): return self.attrTuple[16] # 效果ID1 DWORD
    def GetEffectValues1(self): return self.attrTuple[17] # 效果值列表1 list
    def GetEffectID2(self): return self.attrTuple[18] # 效果ID2 DWORD
    def GetEffectValues2(self): return self.attrTuple[19] # 效果值列表2 list
    def GetEffectID3(self): return self.attrTuple[20] # 效果ID3 DWORD
    def GetEffectValues3(self): return self.attrTuple[21] # 效果值列表3 list
    def GetConnSkill(self): return self.attrTuple[22] # 关联技能 DWORD
    def GetEnhanceSkillList(self): return self.attrTuple[23] # 触发技能ID列表 list
    def GetFightPower(self): return self.attrTuple[24] # 技能战斗力 DWORD
# 武将表
class IPY_Hero():
    
@@ -2848,17 +2941,18 @@
        return
        
    def GetHeroID(self): return self.attrTuple[0] # 英雄ID DWORD
    def GetCountry(self): return self.attrTuple[1] #  国家 BYTE
    def GetQuality(self): return self.attrTuple[2] #  品质 BYTE
    def GetAtkDistType(self): return self.attrTuple[3] # 远近类型;1-近战;2-远程 BYTE
    def GetSkinIDList(self): return self.attrTuple[4] #  皮肤ID列表 list
    def GetNormalSkillID(self): return self.attrTuple[5] # 普攻技能ID DWORD
    def GetAngerSkillID(self): return self.attrTuple[6] # 怒气技能ID DWORD
    def GetAtkInheritPer(self): return self.attrTuple[7] # 攻击继承 WORD
    def GetDefInheritPer(self): return self.attrTuple[8] # 防御继承 WORD
    def GetHPInheritPer(self): return self.attrTuple[9] # 生命继承 WORD
    def GetBatAttrDict(self): return self.attrTuple[10] # 其他战斗属性字典 {"属性ID":值, ...} dict
    def GetFetterIDList(self): return self.attrTuple[11] # 羁绊ID列表 list
    def GetName(self): return self.attrTuple[1] # 名称 char
    def GetCountry(self): return self.attrTuple[2] #  国家 BYTE
    def GetQuality(self): return self.attrTuple[3] #  品质 BYTE
    def GetAtkDistType(self): return self.attrTuple[4] # 远近类型;1-近战;2-远程 BYTE
    def GetSkinIDList(self): return self.attrTuple[5] #  皮肤ID列表 list
    def GetNormalSkillID(self): return self.attrTuple[6] # 普攻技能ID DWORD
    def GetAngerSkillID(self): return self.attrTuple[7] # 怒气技能ID DWORD
    def GetAtkInheritPer(self): return self.attrTuple[8] # 攻击继承 WORD
    def GetDefInheritPer(self): return self.attrTuple[9] # 防御继承 WORD
    def GetHPInheritPer(self): return self.attrTuple[10] # 生命继承 WORD
    def GetBatAttrDict(self): return self.attrTuple[11] # 其他战斗属性字典 {"属性ID":值, ...} dict
    def GetFetterIDList(self): return self.attrTuple[12] # 羁绊ID列表 list
# 武将星级天赋表
class IPY_HeroTalent():
@@ -3010,8 +3104,7 @@
        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
    def GetDailyBootyUpperList(self): return self.attrTuple[1] #  每日战利品掉落上限,[[物品ID,每日上限], ...] list
# 主线关卡表
class IPY_MainLevel():
@@ -3890,38 +3983,6 @@
    def GetAttrPer(self): return self.attrTuple[5] # 对应等级表中的比例 float
    def GetAttrSpecDict(self): return self.attrTuple[6] # 特殊属性值字典 {attrKey:value, ...} dict
    def GetAttrExDict(self): return self.attrTuple[7] # 特殊属性值字典 {attrKey:value, ...} dict
# NPC表扩展
class IPY_NPCEx():
    def __init__(self):
        self.attrTuple = None
        return
    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 GetAtkDistType(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():
@@ -7084,6 +7145,8 @@
        self.__LoadFileData("DirtyList", onlyCheck)
        self.__LoadFileData("DirtyName", onlyCheck)
        self.__LoadFileData("FuncTeamSet", onlyCheck)
        self.__LoadFileData("NPC", onlyCheck)
        self.__LoadFileData("Skill", onlyCheck)
        self.__LoadFileData("Hero", onlyCheck)
        self.__LoadFileData("HeroTalent", onlyCheck)
        self.__LoadFileData("HeroBreak", onlyCheck)
@@ -7159,7 +7222,6 @@
        self.__LoadFileData("PlayerLV", onlyCheck)
        self.__LoadFileData("SpecMapPlayerAttrFormat", onlyCheck)
        self.__LoadFileData("GMAttr", onlyCheck)
        self.__LoadFileData("NPCEx", onlyCheck)
        self.__LoadFileData("NPCRealmStrengthen", onlyCheck)
        self.__LoadFileData("NPCStrengthen", onlyCheck)
        self.__LoadFileData("NPCTimeLostHP", onlyCheck)
@@ -7603,6 +7665,20 @@
    def GetFuncTeamSetByIndex(self, index):
        self.CheckLoadData("FuncTeamSet")
        return self.ipyFuncTeamSetCache[index]
    def GetNPCCount(self):
        self.CheckLoadData("NPC")
        return self.ipyNPCLen
    def GetNPCByIndex(self, index):
        self.CheckLoadData("NPC")
        return self.ipyNPCCache[index]
    def GetSkillCount(self):
        self.CheckLoadData("Skill")
        return self.ipySkillLen
    def GetSkillByIndex(self, index):
        self.CheckLoadData("Skill")
        return self.ipySkillCache[index]
    def GetHeroCount(self):
        self.CheckLoadData("Hero")
@@ -8128,13 +8204,6 @@
    def GetGMAttrByIndex(self, index):
        self.CheckLoadData("GMAttr")
        return self.ipyGMAttrCache[index]
    def GetNPCExCount(self):
        self.CheckLoadData("NPCEx")
        return self.ipyNPCExLen
    def GetNPCExByIndex(self, index):
        self.CheckLoadData("NPCEx")
        return self.ipyNPCExCache[index]
    def GetNPCRealmStrengthenCount(self):
        self.CheckLoadData("NPCRealmStrengthen")
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
@@ -129,12 +129,12 @@
            return realmNPCIpyData.GetLV()
    return curNPC.GetLV()
def GetNPCDataEx(npcID):
    ## 获取NPC扩展数据表,可热更
    npcDataEx = IpyGameDataPY.GetIpyGameDataNotLog("NPCEx", npcID)
def GetNPCDataPy(npcID):
    ## 获取NPC数据表,py自定义的表,可热更
    npcDataEx = IpyGameDataPY.GetIpyGameDataNotLog("NPC", npcID)
    if not npcDataEx:
        if False: # 不可能成立的条件,只为了 . 出代码提示
            npcDataEx = IpyGameDataPY.IPY_NPCEx()
            npcDataEx = IpyGameDataPY.IPY_NPC()
        return npcDataEx
    return npcDataEx
@@ -142,11 +142,9 @@
def SetRealmLV(curNPC, realmLV): return curNPC.SetMAtkMin(realmLV)      # NPC表中此字段含义改成境界等级
def GetIsLVSuppress(curNPC): return curNPC.GetWindDef() # 风防代表是否等级压制
def GetFightPowerLackAtkLimit(curNPC): # 战力不足限制攻击,默认不限制
    npcDataEx = GetNPCDataEx(curNPC.GetNPCID())
    return npcDataEx.GetFightPowerLackAtkLimit() if npcDataEx else 0
def GetSuppressFightPower(curNPC):
    npcDataEx = GetNPCDataEx(curNPC.GetNPCID())
    return npcDataEx.GetSuppressFightPower() if npcDataEx else curNPC.GetThunderDef() # 雷防代表压制战力
    return 0
def GetSuppressFightPower(curNPC): # 压制战力
    return 0
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() # 雷攻代表掉落归属类型
@@ -1124,19 +1122,18 @@
            
    # 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)
    #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), notifyClient)
    tfMgr.delNPCGUID(objID)
    return
def GameServer_KillGameWorldBoss(bossID, killPlayerName, hurtValue, isNotify=True, killerIDList=[]):
@@ -2444,13 +2441,13 @@
    
    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
        #curNPC = self.__Instance
        #objID = curNPC.GetID()
        #turnFight = TurnAttack.GetTurnFightMgr().getNPCTurnFight(objID)
        #if not turnFight:
        #    # 非回合制怪保留原通知
        #    curNPC.NotifyAppear()
        #    return
        
        # 回合制怪不通知,统一由 // B4 24 回合战斗初始化 #tagSCTurnFightInit
        return
@@ -2583,7 +2580,7 @@
                                 ChConfig.AttrID_MaxHP:3000000000,
                                 })
        else:
            npcDataEx = GetNPCDataEx(curNPC.GetNPCID())
            npcDataEx = GetNPCDataPy(curNPC.GetNPCID())
            if not npcDataEx:
                return
            heroAttrDict.update({
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NetPackCommon.py
@@ -31,6 +31,7 @@
import traceback
import ChGameToMapPyPack
from PyMongoDB.DBCommon import CommonDefine
import ObjPool
#-------------------------------------------------------------------------------
#---全局变量---
@@ -227,6 +228,8 @@
        Log("SendFakePack: clientPack Len = %s > %s"%(clientPack.GetLength(), len(innerPackData)))
    #curPlayer.SendFakePack(innerPackData, len(innerPackData))
    curPlayer.SendFakePack(clientPack.GetBuffer(), clientPack.GetLength())
    ObjPool.GetPoolMgr().release(clientPack)
    return
    
def SendFackPackOnline(clientPack, parseFunc=None, *args):
    ## 发送给全服在线玩家
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ObjPool.py
New file
@@ -0,0 +1,498 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package ObjPool
#
# @todo:对象池管理
# @author hxp
# @date 2025-08-06
# @version 1.0
#
# 详细描述: 对象池管理,建议对频繁创建的类使用对象池,如封包相关、战斗相关
#
#-------------------------------------------------------------------------------
#"""Version = 2025-08-06 18:30"""
#-------------------------------------------------------------------------------
import GameWorld
import PyGameData
import collections
import types
#import sys
#import ctypes
class RecursiveObjectPoolManager(object):
    def __init__(self):
        self._pools = {}  # 存储各类的对象池 {class: pool}
        # 使用对象ID作为键的映射:{id(obj): obj_class}
        self._obj_id_to_pool = {}
        self._releasing = set()  # 正在释放的对象ID集合
    def create_pool(self, obj_class, max_size=10):
        """为指定类创建对象池
        :param obj_class: 要管理的对象类
        :param max_size: 对象池最大容量
        """
        if obj_class in self._pools:
            GameWorld.Log("Pool for %s already exists" % obj_class.__name__)
            return
        GameWorld.Log("create_pool %s, max_size=%s" % ( obj_class.__name__, max_size))
        self._pools[obj_class] = {
            'max_size': max_size,
            'free_list': [],      # 空闲对象列表
            'active_list': [],    # 使用中对象列表
            'created_count': 0    # 已创建对象计数
        }
        return self
    def acquire(self, obj_class, *args, **kwargs):
        """获取对象并记录其所属池"""
        if obj_class not in self._pools:
            # 如果池不存在,自动创建大小为0的无限制池
            self.create_pool(obj_class, 0)
        pool = self._pools[obj_class]
        if pool['free_list']:
            # 复用空闲对象
            obj = pool['free_list'].pop()
            # 重新初始化对象
            obj.__init__(*args, **kwargs)
        else:
            # 创建新对象
            if pool['max_size'] <= 0 or pool['created_count'] < pool['max_size']:
                obj = obj_class(*args, **kwargs)
                pool['created_count'] += 1
                # 记录对象到池的映射
                obj_id = id(obj)
                self._obj_id_to_pool[obj_id] = obj_class
            else:
                GameWorld.ErrLog("%s pool exhausted" % obj_class.__name__)
                return None  # 返回None表示获取失败
        pool['active_list'].append(obj)
        return obj
    def release(self, obj):
        """释放对象并递归释放其嵌套对象池对象"""
        obj_id = id(obj)
        # 检查是否正在递归释放中
        if obj_id in self._releasing:
            return
        # 只处理对象池管理的对象
        if obj_id not in self._obj_id_to_pool:
            return
        # 标记为正在释放
        self._releasing.add(obj_id)
        visited = set([obj_id])  # 初始化已访问集合
        try:
            # 1. 递归释放嵌套对象池对象
            self._recursive_release(obj, visited)
            # 2. 释放当前对象
            self._return_to_pool(obj)
        finally:
            # 移除释放标记
            self._releasing.discard(obj_id)
    def _recursive_release(self, obj, visited):
        """递归释放嵌套对象池对象(深度优先)"""
        # 获取对象的所有引用(不包含基础类型)
        for ref in self._get_references(obj):
            try:
                ref_id = id(ref)
            except TypeError:
                # 如果对象不可哈希,跳过
                continue
            if ref_id in visited:
                continue
            visited.add(ref_id)
            # 只处理对象池管理的对象
            if ref_id in self._obj_id_to_pool:
                # 先递归释放嵌套对象
                self._recursive_release(ref, visited)
                # 再释放当前引用对象
                self._return_to_pool(ref)
            else:
                # 对于非对象池管理的对象,继续递归查找其中的对象池对象
                self._recursive_release(ref, visited)
    def _get_references(self, obj):
        """安全获取对象的所有直接引用,支持复杂数据结构"""
        refs = []
        obj_type = type(obj)
        # 处理自定义对象的属性
        try:
            # 首先尝试__dict__属性
            if hasattr(obj, '__dict__'):
                for attr in dir(obj):
                    if attr.startswith('__'):
                        continue
                    try:
                        attr_value = getattr(obj, attr)
                    except Exception:
                        continue
                    # 过滤基础类型
                    if not self._is_basic_type(attr_value):
                        refs.append(attr_value)
        except Exception:
            pass
        # 处理常见容器类型
        if obj_type in (list, tuple, set, collections.deque):
            for item in obj:
                if not self._is_basic_type(item):
                    refs.append(item)
        elif obj_type is dict:
            for key, value in obj.iteritems():
                if not self._is_basic_type(key):
                    refs.append(key)
                if not self._is_basic_type(value):
                    refs.append(value)
        # 处理特殊属性(如__slots__定义的对象)
        try:
            if hasattr(obj, '__slots__'):
                for slot_name in obj.__slots__:
                    if hasattr(obj, slot_name):
                        slot_value = getattr(obj, slot_name)
                        if not self._is_basic_type(slot_value):
                            refs.append(slot_value)
        except Exception:
            pass
        return refs
    def _is_basic_type(self, value):
        """判断值是否为基本类型,不需要递归处理"""
        if value is None:
            return True
        if isinstance(value, (int, long, float, bool, str, unicode)):
            return True
        if isinstance(value, (types.FunctionType, types.MethodType, types.ModuleType)):
            return True
        if isinstance(value, (types.BuiltinFunctionType, type)):
            return True
        return False
    def _return_to_pool(self, obj):
        """将对象归还到其所属池"""
        obj_id = id(obj)
        # 获取对象所属类
        if obj_id not in self._obj_id_to_pool:
            return  # 如果不在池中,忽略
        obj_class = self._obj_id_to_pool[obj_id]
        if obj_class not in self._pools:
            return  # 如果池不存在,忽略
        pool = self._pools[obj_class]
        # 确保对象在活跃列表中
        if obj in pool['active_list']:
            # 从活跃列表移除
            try:
                pool['active_list'].remove(obj)
            except ValueError:
                # 如果对象不在列表中,忽略
                return
            # 执行对象清理方法
            #if hasattr(obj, 'cleanup'):
            #    try:
            #        obj.cleanup()
            #    except Exception:
            #        pass
            # 添加到空闲列表
            pool['free_list'].append(obj)
        else:
            # 如果在空闲列表中,跳过
            pass
    def destroy_pool(self, obj_class):
        """销毁指定类的对象池"""
        if obj_class not in self._pools:
            return
        pool = self._pools[obj_class]
        # 清理所有对象
        for obj in list(pool['free_list'] + pool['active_list']):
            # 清理对象属性
            #if hasattr(obj, 'cleanup'):
            #    try:
            #        obj.cleanup()
            #    except Exception:
            #        pass
            # 从映射中移除
            obj_id = id(obj)
            if obj_id in self._obj_id_to_pool:
                del self._obj_id_to_pool[obj_id]
        del self._pools[obj_class]
    def pool_status(self):
        """输出所有对象池的状态信息"""
        statusList = []
        max_name_len = 0
        max_created_len = 0
        max_free_len = 0
        max_active_len = 0
        # 收集状态并计算最大长度
        for obj_class, pool in self._pools.items():
            # 获取类名
            class_name = getattr(obj_class, '__name__', str(obj_class))
            created = pool['created_count']
            free = len(pool['free_list'])
            active = len(pool['active_list'])
            statusList.append({
                'obj_class': class_name,
                'created': created,
                'free': free,
                'active': active
            })
            # 计算最大长度
            max_name_len = max(max_name_len, len(class_name))
            max_created_len = max(max_created_len, len(str(created)))
            max_free_len = max(max_free_len, len(str(free)))
            max_active_len = max(max_active_len, len(str(active)))
        # 添加表头长度
        max_name_len = max(max_name_len, 8)  # "对象池名称"的长度
        max_created_len = max(max_created_len, 4)  # "总数"的长度
        max_free_len = max(max_free_len, 4)  # "空闲"的长度
        max_active_len = max(max_active_len, 4)  # "活跃"的长度
        # 按活跃对象数量排序
        statusList.sort(key=lambda s: (s['created'], s['active']), reverse=True)
        # 创建格式字符串
        header_format = "| {:<{name_width}} | {:>{created_width}} | {:>{free_width}} | {:>{active_width}} |"
        row_format = "| {:<{name_width}} | {:>{created_width}} | {:>{free_width}} | {:>{active_width}} |"
        # 输出表头
        GameWorld.Log("----- 对象池状态 -----")
        GameWorld.Log(header_format.format(
            "对象池名称", "总数", "空闲", "活跃",
            name_width=max_name_len,
            created_width=max_created_len,
            free_width=max_free_len,
            active_width=max_active_len
        ))
        # 输出分隔线
        separator = "+-{}-+-{}-+-{}-+-{}-+".format(
            "-" * max_name_len,
            "-" * max_created_len,
            "-" * max_free_len,
            "-" * max_active_len
        )
        GameWorld.Log(separator)
        # 输出数据行
        for s in statusList:
            GameWorld.Log(row_format.format(
                s['obj_class'], s['created'], s['free'], s['active'],
                name_width=max_name_len,
                created_width=max_created_len,
                free_width=max_free_len,
                active_width=max_active_len
            ))
        return
def GetPoolMgr():
    """获取指定类创建对象池"""
    poolMgr = PyGameData.g_objPoolMgr
    if not poolMgr:
        poolMgr = RecursiveObjectPoolManager()
        PyGameData.g_objPoolMgr = poolMgr
    return poolMgr
def OnMinute():
    """每分钟执行,输出对象池状态"""
    GetPoolMgr().pool_status()
    return
## 使用示例
#if __name__ == "__main__":
#
#    # 定义一个嵌套的Structure类
#    class SubStructure(ctypes.Structure):
#        _fields_ = [("z", ctypes.c_int)]
#
#        def __init__(self, name):
#            self.name = name
#            print "初始化SubStructure: name=%s" % (self.name)
#            return
#
#        def cleanup(self):
#            """清理方法"""
#            print "清理SubStructure: name=%s" % (self.name)
#            self.name = None
#
#    # 定义一个继承自Structure的类
#    class MyStructure(ctypes.Structure):
#        _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]
#
#        def __init__(self, name):
#            self.name = name
#            # 嵌套的Structure对象
#            self.sub = GetPoolMgr().acquire(SubStructure, "%s_sub" % name)
#            print "初始化MyStructure: name=%s" % (self.name)
#            return
#
#        def cleanup(self):
#            """清理方法"""
#            print "清理MyStructure: name=%s" % (self.name)
#            self.name = None
#            # 不需要手动释放sub,递归释放会处理
#            self.sub = None
#
#    class ClassE():
#
#        def __init__(self, name):
#            self.name = name
#            print "初始化对象 ClassE: name=%s" % (self.name)
#            self.ed1 = GetPoolMgr().acquire(ClassD, "%s_ed_1" % name)
#            return
#
#    class ClassD():
#
#        def __init__(self, name):
#            self.name = name
#            print "初始化对象 ClassD: name=%s" % (self.name)
#            return
#
#        def cleanup(self):
#            """清理方法"""
#            print "清理ClassD: name=%s" % (self.name)
#            self.name = None
#
#    class ClassC():
#
#        def __init__(self, name, bb):
#            self.name = name
#            print "初始化对象 ClassC: name=%s" % (self.name)
#            self.bb = bb
#            self.cd1 = GetPoolMgr().acquire(ClassD, "%s_cd_1" % name)
#            self.cd2 = GetPoolMgr().acquire(ClassD, "%s_cd_2" % name)
#            return
#
#    class ClassB():
#
#        def __init__(self, name):
#            self.name = name
#            print "初始化对象 ClassB: name=%s" % (self.name)
#            bc1 = GetPoolMgr().acquire(ClassC, "%s_c_1" % name, self)
#            bc2 = GetPoolMgr().acquire(ClassC, "%s_c_2" % name, self)
#            self.cList = [bc1, bc2]
#            self.dDict = {1:GetPoolMgr().acquire(ClassD, "%s_d_1" % name), GetPoolMgr().acquire(ClassD, "%s_d_2" % name):2, "bc1":bc1, "bc2":bc2}
#            return
#
#        def do(self, doStr):
#            print "ClassB.do name=%s, %s" % (self.name, doStr)
#            return
#
#    class ClassA():
#
#        def __init__(self, objID, name):
#            self.objID = objID
#            self.name = name
#            self.b = GetPoolMgr().acquire(ClassB, "%s_b" % name)
#            self.e = ClassE("%s_e" % name)
#            self.struct = GetPoolMgr().acquire(MyStructure, "%s_struct" % name)  # Structure对象
#            self.dDict = {1:GetPoolMgr().acquire(ClassD, "%s_d_1" % name), GetPoolMgr().acquire(ClassD, "%s_d_2" % name):2}
#            print "初始化对象 ClassA: objID=%s,name=%s" % (self.objID, self.name)
#
#        def do(self, doStr):
#            print "ClassA.do objID=%s,name=%s, %s" % (self.objID, self.name, doStr)
#            return
#
#        def cleanup(self):
#            """清理方法"""
#            print "清理ClassA: objID=%s,name=%s" % (self.objID, self.name)
#            self.b = None
#            self.e = None
#            self.struct = None
#            self.dDict = None
#            self.name = None
#            self.objID = None
#
#
#    # 创建对象池管理器
#    poolMgr = GetPoolMgr()
#
#    # 为所有类创建对象池,max_size=0 表示无限制
#    poolMgr.create_pool(ClassA, max_size=0)
#    poolMgr.create_pool(ClassB, max_size=0)
#    poolMgr.create_pool(ClassC, max_size=0)
#    poolMgr.create_pool(ClassD, max_size=0)
#    poolMgr.create_pool(MyStructure, max_size=0)
#    poolMgr.create_pool(SubStructure, max_size=0)  # 嵌套的Structure类
#
#    # 创建测试对象
#    a1 = poolMgr.acquire(ClassA, 1000, "hanmeimei")
#    if a1 is None:
#        GameWorld.ErrLog("Failed to acquire ClassA instance for hanmeimei")
#    else:
#        a1.do("hello 1ilei")
#
#    a2 = poolMgr.acquire(ClassA, 1001, "lilei")
#    if a2 is None:
#        GameWorld.ErrLog("Failed to acquire ClassA instance for lilei")
#    else:
#        a2.do("hello hanmeimei")
#
#    # 输出状态
#    print("\n释放前状态")
#    poolMgr.pool_status()
#
#    # 释放对象
#    if a1:
#        poolMgr.release(a1)
#    if a2:
#        poolMgr.release(a2)
#
#    # 输出状态
#    print("\n释放后状态")
#    poolMgr.pool_status()
#
#    # 再次获取应复用对象
#    a3 = poolMgr.acquire(ClassA, 1002, "lihua")
#    if a3 is None:
#        GameWorld.ErrLog("Failed to acquire ClassA instance for lihua")
#    else:
#        a3.do("fuck you")
#
#    # 输出状态
#    print("\n再次获取后状态")
#    poolMgr.pool_status()
#
#    # 清理
#    if a3:
#        poolMgr.release(a3)
#    poolMgr.destroy_pool(ClassA)
#    poolMgr.destroy_pool(ClassB)
#    poolMgr.destroy_pool(ClassC)
#    poolMgr.destroy_pool(ClassD)
#    poolMgr.destroy_pool(MyStructure)
#    poolMgr.destroy_pool(SubStructure)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -578,7 +578,7 @@
#  @return 返回值无意义
def ClearPyPlayerAction(curPlayer):
    #清除py自定义状态
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_PyPlayerAction, 0)
    NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_PyPlayerAction, 0)
    return
#---------------------------------------------------------------------
@@ -2837,10 +2837,25 @@
    #轮回殿
    PlayerActLunhuidian.AddLunhuidianValue(curPlayer, PlayerActLunhuidian.AwardType_PayMoney, type_Price, price)
    if type_Price == ShareDefine.TYPE_Price_Xiantao:
        # 累加未结算战锤 - 经验
        unXiantaoCntExp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntExp)
        NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntExp, unXiantaoCntExp + price)
        # 累加未结算战锤 - 装备
        unXiantaoCntEquip = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntEquip)
        NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntEquip, unXiantaoCntEquip + price)
        # 累加未结算战锤 - 战利品
        chapterID = GetMainLevelNowInfo(curPlayer)[0]
        chapterIpyData = IpyGameDataPY.GetIpyGameData("MainChapter", chapterID)
        if chapterIpyData:
            DailyBootyUpperList = chapterIpyData.GetDailyBootyUpperList()
            for itemID, upperCnt in DailyBootyUpperList:
                if upperCnt <= 0:
                    continue
                if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_BootyDropToday % itemID) >= upperCnt:
                    continue
                unXiantaoCntBooty = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_UnXiantaoCntBooty % itemID)
                NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_UnXiantaoCntBooty % itemID, unXiantaoCntBooty + 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
@@ -3476,7 +3491,8 @@
        
        # 杀怪
        if expViewType == ShareDefine.Def_ViewExpType_KillNPC:
            exp_rate = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_TotalExpRate)
            #exp_rate = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_TotalExpRate)
            exp_rate = 10000
        elif expViewType in [ShareDefine.Def_ViewExpType_GameEvent, ShareDefine.Def_ViewExpType_Sit]:
            exp_rate = curPlayer.GetGameEventExpRate()
            #exp_rate += GetFamilySitExpPer(curPlayer)
@@ -4965,12 +4981,6 @@
    insidePerAttrDict = {}
    customAttrDict = {}
    return attrList, insidePerAttrDict, customAttrDict
def GetLordAttr(curPlayer):
    ## 获取主公属性汇总
    lordAttrDict = {"Atk":curPlayer.GetMaxAtk(), "Def":curPlayer.GetDef(), "MaxHP":GameObj.GetMaxHP(curPlayer),
                    "Hit":curPlayer.GetHit(), "Miss":curPlayer.GetMiss()}
    return lordAttrDict
#-------------------------------------------------------------------------------
## 设置玩家字典值, 存库
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py
@@ -30,7 +30,7 @@
import time
class LineupHero():
    ## 阵容战斗武将,注意:同一个武将在不同阵容中可能属性不一样
    ## 阵容武将,注意:同一个武将在不同阵容中可能属性不一样
    
    def __init__(self):
        self.Clear()
@@ -39,6 +39,7 @@
    def Clear(self):
        self.itemIndex = 0
        self.heroID = 0
        self.skinID = 0
        self.posNum = 0
        self.heroBatAttrDict = {} # 武将的最终战斗属性字典, {attrID:value, ...}
        self.heroSkillIDList = [] # 武将拥有的技能ID列表 [skillID, ...]
@@ -153,7 +154,7 @@
        return
    
    def OnClear(self):
        self.mainFight.clear()
        self.mainFight.turnFight.clearFight()
        return
    
    def SetPlayer(self, curPlayer):
@@ -453,7 +454,15 @@
        star = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
        breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
        awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
        skinIndex = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroSkin)
        
        skinID = 0
        skinIDList = heroIpyData.GetSkinIDList()
        if skinIndex < 0 or skinIndex >= len(skinIDList):
            skinID = skinIDList[skinIndex]
        elif skinIDList:
            skinID = skinIDList[0]
        InitAddPer += qualityIpyData.GetInitAddPer()
        LVAddPer += qualityIpyData.GetLVAddPer() * heroLV
        BreakLVAddPer += qualityIpyData.GetBreakLVAddPer() * breakLV
@@ -465,6 +474,7 @@
        lineupHero.itemIndex = itemIndex
        lineupHero.posNum = posNum
        lineupHero.heroID = heroID
        lineupHero.skinID = skinID
        lineupHero.heroBatAttrDict = {}
        lineupHero.heroSkillIDList = []
        lineupHero.fightPower = 0
@@ -695,7 +705,7 @@
        GameWorld.DebugLog("    fightPower=%s,heroSkillIDList=%s" % (fightPower, lineupHero.heroSkillIDList))
        skillTypeIDDict = {}
        for skillID in lineupHero.heroSkillIDList:
            skillData = GameWorld.GetGameData().GetSkillBySkillID(skillID)
            skillData = IpyGameDataPY.GetIpyGameData("Skill", skillID)
            if not skillData:
                continue
            skillTypeID = skillData.GetSkillTypeID()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -24,6 +24,9 @@
g_serverClosing = 0 # 是否关服中 0-非关服中;1-关服中;2-关服结束
g_closeSaveDataOK = False # 关服数据入库是否成功
g_objPoolMgr = None # 对象池管理器
g_batObjMgr = None # 战斗对象管理
g_pyGameDataManager = None
g_dbPlayerIDMap = {} # 本服DBPlayer玩家表ID映射关系 {playerID:accID, ...}
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py
@@ -1284,13 +1284,13 @@
        elif curObjType == IPY_GameWorld.gotNPC:
            AttackCommon.NPCAddObjInHurtList(attackerOwner, curObj, curObjHP_BeforeAttack, lostValue)
            
    TurnAttack.AddTurnObjHurtValue(buffOwner, curObj, hurtType, lostValue, lostHP, curSkill)
    #TurnAttack.AddTurnObjHurtValue(buffOwner, curObj, lostValue, lostHP, curSkill)
    
    #统一调用攻击结束动作
    if isDoAttackResult:
        BaseAttack.DoLogic_AttackResult(buffOwner, curObj, None, tick)
        
    TurnAttack.OnTurnfightAttackResult(buffOwner, curObj, curSkill)
    #TurnAttack.OnTurnfightAttackResult(buffOwner, curObj, curSkill)
    return lostHP
## 检查增加淬毒buff
@@ -1852,12 +1852,16 @@
def isAngerSkill(curSkill):
    ## 是否怒气技能
    return curSkill and curSkill.GetXP() > 0
    return curSkill and curSkill.GetFuncType() == ChConfig.Def_SkillFuncType_AngerSkill
def isTurnNormalSkill(curSkill):
    ## 是否回合普攻技能,区别与无技能的普通A一下,该普攻同样可以有各种技能效果,只是他属于普攻
    return curSkill and curSkill.GetFuncType() == ChConfig.Def_SkillFuncType_TurnNormaSkill
def isAtkbackSkill(curSkill):
    ## 是否反击技能
    return curSkill and curSkill.GetFuncType() == ChConfig.Def_SkillFuncType_AtkbackSkill
## 检查技能是否为被动技能, 用于控制不可释放技能
def isPassiveSkill(curSkill):
    return curSkill.GetSkillType() in [ChConfig.Def_SkillType_Passive,
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PySkillManager.py
File was deleted
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
@@ -3494,7 +3494,7 @@
        #调用攻击惩罚逻辑
        BaseAttack.DoLogic_AttackResult(attacker, defender, None, tick)
        TurnAttack.OnTurnfightAttackResult(attacker, defender, curSkill)
        #TurnAttack.OnTurnfightAttackResult(attacker, defender, curSkill)
        
    return
#---------------------------------------------------------------------
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnBuff.py
New file
@@ -0,0 +1,74 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Skill.TurnBuff
#
# @todo:回合战斗buff
# @author hxp
# @date 2025-08-06
# @version 1.0
#
# 详细描述: 回合战斗buff
#
#-------------------------------------------------------------------------------
#"""Version = 2025-08-06 18:30"""
#-------------------------------------------------------------------------------
import ChConfig
import GameWorld
import ChPyNetSendPack
import ObjPool
def OnAddBuff(turnFight, batObj, curSkill, buffOwner=None):
    skillID = curSkill.GetSkillID()
    enhanceBySkill = curSkill.GetEnhanceBySkill()
    relatedSkillID = enhanceBySkill.GetSkillID() if enhanceBySkill else 0
    curID = batObj.GetID()
    ownerID = buffOwner.GetID() if buffOwner else curID
    GameWorld.DebugLog("OnAddBuff: curID=%s,skillID=%s,ownerID=%s,relatedSkillID=%s" % (curID, skillID, ownerID, relatedSkillID))
    #检查是否几率触发
    if not enhanceBySkill:
        rate = curSkill.GetHappenRate()
        if rate and rate != ChConfig.Def_MaxRateValue and not GameWorld.CanHappen(rate, ChConfig.Def_MaxRateValue):
            GameWorld.DebugLog("    概率不触发buff!")
            return
    skillTypeID = curSkill.GetSkillTypeID()
    # 先简单做下能加上即可
    buffMgr = batObj.GetBuffManager()
    buffIDList = buffMgr.FindBuffIDBySkillTypeID(skillTypeID)
    if buffIDList:
        GameWorld.DebugLog("    已经存在该buff: skillTypeID=%s,buffIDList=%s" % (skillTypeID, buffIDList))
        return True
    buff = buffMgr.AddBuff(skillID)
    if not buff:
        GameWorld.DebugLog("    添加buff失败! skillID=%s" % skillID)
        return False
    GameWorld.DebugLog("    AddBuffOK. buffID=%s" % buff.GetBuffID())
    buff.SetOwnerID(ownerID)
    buff.SetRemainTime(curSkill.GetLastTime())
    #buff.SetLayer()
    SyncBuffRefresh(turnFight, batObj, buff, relatedSkillID)
    return True
def SyncBuffRefresh(turnFight, curBatObj, curBuff, relatedSkillID=0):
    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagSCBuffRefresh)
    clientPack.ObjID = curBatObj.GetID()
    clientPack.BuffID = curBuff.GetBuffID()
    clientPack.SkillID = curBuff.GetSkillID()
    clientPack.RelatedSkillID = relatedSkillID
    clientPack.LastTime = curBuff.GetRemainTime()
    clientPack.Layer = curBuff.GetLayer()
    clientPack.OwnerID = curBuff.GetOwnerID()
    turnFight.addBatPack(clientPack)
    return
def SyncBuffDel(turnFight, curBatObj, buffID, relatedSkillID=0):
    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagSCBuffDel)
    clientPack.ObjID = curBatObj.GetID()
    clientPack.BuffID = buffID
    clientPack.RelatedSkillID = relatedSkillID
    turnFight.addBatPack(clientPack)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnSkill.py
New file
@@ -0,0 +1,899 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Skill.TurnSkill
#
# @todo:回合战斗技能
# @author hxp
# @date 2025-08-06
# @version 1.0
#
# 详细描述: 回合战斗技能
#
#-------------------------------------------------------------------------------
#"""Version = 2025-08-06 18:30"""
#-------------------------------------------------------------------------------
import ChConfig
import GameWorld
import BattleObj
import SkillCommon
import IpyGameDataPY
import IPY_GameWorld
import ChPyNetSendPack
import PlayerControl
import ShareDefine
import TurnAttack
import TurnBuff
import ObjPool
import FBLogic
import random
def GetPMType(batObj, useSkill):
    ## 获取物法伤害类型
    # GetHurtType 个位数用法改成 pvp pve标识,十位数-物攻法攻 IPY_GameWorld.ghtPhy = 1
    #---技能攻击, 读表获取攻击类型---
    ght = useSkill.GetHurtType() % 10
    if ght == IPY_GameWorld.ghtMag: # 做配置兼容用,优先验证法伤,否则默认物伤
        return IPY_GameWorld.ghtMag
    return IPY_GameWorld.ghtPhy
def IsIgnoreDef(useSkill):
    ## 是否无视防御
    return useSkill.GetHurtType() / 10 == 1 # 2为真伤,待扩展
def OnUseSkill(turnFight, curBatObj, useSkill, tagObjList=None, batType=ChConfig.TurnBattleType_Normal, enhanceBySkill=None):
    '''使用技能通用入口
    @param useSkill: 使用的技能,注意并不一定是身上的技能,可能只是 SkillData 表数据
    @param enhanceBySkill: 由哪个主技能额外触发的
    @return: 是否成功
    '''
    if not useSkill:
        return
    skillID = useSkill.GetSkillID()
    if not skillID:
        return
    #没有指定目标,则按技能自身的目标逻辑
    if not tagObjList:
        tagAim = useSkill.GetTagAim()
        tagFriendly = useSkill.GetTagFriendly()
        tagAffect = useSkill.GetTagAffect()
        tagCount = useSkill.GetTagCount()
        tagObjList = GetSkillTags(turnFight, curBatObj, tagAim, tagFriendly, tagAffect, tagCount)
    enhanceBySkillID = enhanceBySkill.GetSkillID() if enhanceBySkill else 0
    GameWorld.DebugLog("使用技能: curID=%s,skillID=%s,tagCnt=%s,batType=%s,enhanceBySkill=%s"
                       % (curBatObj.GetID(), skillID, len(tagObjList), batType, enhanceBySkillID))
    if not tagObjList:
        # 可扩展其他目标选择,如复活技能没有死亡单位时则使用另外的效果
        return
    # 以下为技能可以使用的处理,之后的逻辑默认技能使用成功
    poolMgr = ObjPool.GetPoolMgr()
    usePoolSkill = False
    if isinstance(useSkill, IpyGameDataPY.IPY_Skill):
        usePoolSkill = True
        # 统一使用 BattleObj.PySkill
        useSkill = poolMgr.acquire(BattleObj.PySkill, useSkill)
    useSkill.SetTagObjList(tagObjList)
    useSkill.SetBatType(batType)
    useSkill.SetEnhanceBySkill(enhanceBySkill)
    useSkill.ClearHurtObj()
    curBatObj.ClearSkillTempAttr()
    for tagObj in tagObjList:
        tagObj.ClearSkillTempAttr()
    objID = curBatObj.GetID()
    useTag = ""
    if not enhanceBySkill:
        # 因为可能触发连击,所以标记需带上累计使用技能次数,确保唯一
        useTag = "Skill_%s_%s_%s" % (objID, skillID, curBatObj.GetSkillUseCnt(skillID) + 1)
        clientPack = poolMgr.acquire(ChPyNetSendPack.tagSCTurnFightTag)
        clientPack.Tag = useTag
        clientPack.Len = len(clientPack.Tag)
        clientPack.Sign = 0
        turnFight.addBatPack(clientPack)
    #这个技能是Buff
    if SkillCommon.IsBuff(useSkill):
        __doAddBuff(turnFight, curBatObj, useSkill)
    else:
        __doUseSkill(turnFight, curBatObj, useSkill)
    if useTag:
        clientPack = poolMgr.acquire(ChPyNetSendPack.tagSCTurnFightTag)
        clientPack.Tag = useTag
        clientPack.Len = len(clientPack.Tag)
        clientPack.Sign = 1
        turnFight.addBatPack(clientPack)
    # 最后重置、清空回收对象池
    useSkill.SetTagObjList([])
    useSkill.SetEnhanceBySkill(None) # 需重置,防止主技能被误回收
    useSkill.ClearHurtObj()
    if usePoolSkill:
        poolMgr.release(useSkill)
    return True
def GetSkillTags(turnFight, curBatObj, tagAim, tagFriendly, tagAffect, tagCount):
    ## 获取技能目标
    curFaction = curBatObj.GetFaction()
    # 自己,直接返回
    if tagAim == ChConfig.SkillTagAim_Self:
        return [curBatObj]
    if tagFriendly:
        tagFaction = curFaction
    else:
        tagFaction = ChConfig.Def_FactionB if curFaction == ChConfig.Def_FactionA else ChConfig.Def_FactionA
    batObjMgr = BattleObj.GetBatObjMgr()
    lineupNum = curBatObj.GetLineupNum()
    posNum = curBatObj.GetPosNum()
    batFaction = turnFight.getBatFaction(tagFaction)
    if tagFaction == curFaction:
        lineupNumList = [lineupNum] # 友方暂时仅限制自己阵容
    else:
        lineupNumList = [lineupNum] # 敌方优先对位阵容,再其他阵容
        for tagNum in batFaction.lineupDict.keys():
            if tagNum not in lineupNumList:
                lineupNumList.append(tagNum)
    aimObjList = [] # 先筛选出范围目标
    for num in lineupNumList:
        batLineup = batFaction.getBatlineup(num)
        # 对位
        if tagAim == ChConfig.SkillTagAim_Relative:
            posNumList = [posNum] # 优先对位位置,再其他位置按顺序遍历
            pNumList = batLineup.posObjIDDict.keys()
            pNumList.sort()
            for pNum in pNumList:
                if pNum > 0 and pNum not in posNumList:
                    posNumList.append(pNum)
            for pNum in posNumList:
                if pNum not in batLineup.posObjIDDict:
                    continue
                tagObjID = batLineup.posObjIDDict[pNum]
                tagBatObj = batObjMgr.getBatObj(tagObjID)
                if not __skillTagFilter(tagBatObj, tagAffect):
                    continue
                aimObjList.append(tagBatObj) # 对位的默认只选1个
                break
        # 前排
        elif tagAim == ChConfig.SkillTagAim_FrontRow:
            # 优先前排,如果没有则后排亦是前排
            for pNumList in [[1, 2, 3], [4, 5, 6]]:
                hasObj = False
                for pNum in pNumList:
                    if pNum not in batLineup.posObjIDDict:
                        continue
                    tagObjID = batLineup.posObjIDDict[pNum]
                    tagBatObj = batObjMgr.getBatObj(tagObjID)
                    if not __skillTagFilter(tagBatObj, tagAffect):
                        continue
                    hasObj = True
                    aimObjList.append(tagBatObj)
                if hasObj:
                    break
        # 后排
        elif tagAim == ChConfig.SkillTagAim_BackRow:
            # 优先后排,如果没有则前排亦是后排
            for pNumList in [[4, 5, 6], [1, 2, 3]]:
                hasObj = False
                for pNum in pNumList:
                    if pNum not in batLineup.posObjIDDict:
                        continue
                    tagObjID = batLineup.posObjIDDict[pNum]
                    tagBatObj = batObjMgr.getBatObj(tagObjID)
                    if not __skillTagFilter(tagBatObj, tagAffect):
                        continue
                    hasObj = True
                    aimObjList.append(tagBatObj)
                if hasObj:
                    break
        # 竖排/纵排
        elif tagAim == ChConfig.SkillTagAim_Vertical:
            verticalNumList = [[1, 4], [2, 5], [3, 6]]
            for pNumList in verticalNumList:
                # 优先对位排
                if posNum in pNumList:
                    verticalNumList.remove(pNumList)
                    verticalNumList.insert(0, pNumList)
                    break
            for pNumList in verticalNumList:
                hasObj = False
                for pNum in pNumList:
                    if pNum not in batLineup.posObjIDDict:
                        continue
                    tagObjID = batLineup.posObjIDDict[pNum]
                    tagBatObj = batObjMgr.getBatObj(tagObjID)
                    if not __skillTagFilter(tagBatObj, tagAffect):
                        continue
                    hasObj = True
                    aimObjList.append(tagBatObj)
                if hasObj:
                    break
        # 其他,默认全部
        else:
            pNumList = batLineup.posObjIDDict.keys()
            for pNum in pNumList:
                if pNum <= 0:
                    continue
                tagObjID = batLineup.posObjIDDict[pNum]
                tagBatObj = batObjMgr.getBatObj(tagObjID)
                if not __skillTagFilter(tagBatObj, tagAffect):
                    continue
                aimObjList.append(tagBatObj)
    # 目标细分
    # 血量最低
    if tagAffect == ChConfig.SkillTagAffect_HPLowest:
        aimObjList.sort(key=lambda o:(o.GetHP()), reverse=False)
        aimObjList = aimObjList[:tagCount]
    # 血量最高
    elif tagAffect == ChConfig.SkillTagAffect_HPHighest:
        aimObjList.sort(key=lambda o:(o.GetHP()), reverse=True)
        aimObjList = aimObjList[:tagCount]
    else:
        # 范围目标超过个数,则随机取
        if tagCount and len(aimObjList) > tagCount:
            random.shuffle(aimObjList)
            aimObjList = aimObjList[:tagCount]
    return aimObjList
def __skillTagFilter(tagBatObj, tagAffect):
    ## 技能目标过滤器
    # @return: 是否允许添加该单位
    if not tagBatObj:
        return False
    if tagAffect != ChConfig.SkillTagAffect_Death and tagBatObj.GetHP() <= 0:
        return False
    if tagAffect == ChConfig.SkillTagAffect_Death and tagBatObj.GetHP() > 0:
        return False
    if not tagBatObj.GetCanAttack():
        return False
    return True
def __doAddBuff(turnFight, curBatObj, useSkill):
    #执行添加buff
    for tagBatObj in useSkill.GetTagObjList():
        TurnBuff.OnAddBuff(turnFight, tagBatObj, useSkill, buffOwner=curBatObj)
    DoAttackResult(turnFight, curBatObj, useSkill)
    return
def __doUseSkill(turnFight, curBatObj, useSkill):
    atkType = useSkill.GetAtkType()
    GameWorld.DebugLog("__doUseSkill: curID=%s,skillID=%s,atkType=%s" % (curBatObj.GetID(), useSkill.GetSkillID(), atkType))
    # 通用攻击
    if atkType == 1:
        SkillModule_1(turnFight, curBatObj, useSkill)
        return
    # 治疗
    if atkType == 2:
        SkillModule_2(turnFight, curBatObj, useSkill)
        return
    # 复活
    if atkType == 3:
        return
    # 多次攻击(锁目标多次伤害,非前端的多段飘血)
    if atkType == 4:
        return
    # 弹射(多次攻击,切换目标)
    if atkType == 5:
        return
    # 6 怒气增减偷
    if atkType == 6:
        SkillModule_6(turnFight, curBatObj, useSkill)
        return
    return
def SkillModule_1(turnFight, curBatObj, useSkill):
    ## 通用攻击,单攻、群攻
    #执行攻击结果
    for tagBatObj in useSkill.GetTagObjList():
        __doSkillHurtHP(curBatObj, tagBatObj, useSkill)
    DoAttackResult(turnFight, curBatObj, useSkill)
    return
def SkillModule_2(turnFight, curBatObj, useSkill):
    ## 治疗
    skillID = useSkill.GetSkillID()
    for tagBatObj in useSkill.GetTagObjList():
        cureHP = CalcCureHP(curBatObj, tagBatObj, useSkill, largeNum=True)
        if cureHP <= 0:
            continue
        dHP = tagBatObj.GetHP()
        dMapHP = tagBatObj.GetMaxHP()
        remainHP = min(dHP + cureHP, dMapHP)
        realCureHP = max(remainHP - dHP, 0)
        tagBatObj.SetHP(remainHP)
        dID = tagBatObj.GetID()
        hurtObj = useSkill.AddHurtObj(dID)
        hurtObj.AddHurtType(ChConfig.HurtTYpe_Recovery)
        hurtObj.SetHurtHP(cureHP)
        hurtObj.SetLostHP(realCureHP)
        hurtObj.SetCurHP(tagBatObj.GetHP())
        GameWorld.DebugLog("    治疗: dID=%s,cureHP=%s,realCureHP=%s,%s/%s" % (dID, cureHP, realCureHP, tagBatObj.GetHP(), dMapHP))
        TurnAttack.AddTurnObjCureHP(tagBatObj, curBatObj, cureHP, realCureHP, skillID)
    DoAttackResult(turnFight, curBatObj, useSkill)
    return
def SkillModule_6(turnFight, curBatObj, useSkill):
    ## 怒气增减偷
    curID = curBatObj.GetID()
    skillID = useSkill.GetSkillID()
    enhanceBySkill = useSkill.GetEnhanceBySkill()
    relatedSkillID = enhanceBySkill.GetSkillID() if enhanceBySkill else 0
    effIDNum = useSkill.FindEffectID(ChConfig.Def_Skill_Effect_Anger)
    angerPer = useSkill.GetEffectValue(effIDNum, 0) # 最大值的百分比,万分率
    angerValue = useSkill.GetEffectValue(effIDNum, 1) # 固定值
    calcType = useSkill.GetEffectValue(effIDNum, 2) # 计算方式(1增 2减 3偷)
    xpMax = IpyGameDataPY.GetFuncCfg("AngerXP", 2)
    calcValue = int(xpMax * angerPer / 10000.0 + angerValue)
    attrID = ChConfig.AttrID_XP
    GameWorld.DebugLog("怒气增减偷: curID=%s,calcValue=%s,skillID=%s,angerPer=%s,angerValue=%s,calcType=%s,relatedSkillID=%s"
                       % (curID, calcValue, skillID, angerPer, angerValue, calcType, relatedSkillID))
    curStealTotal = 0
    for tagBatObj in useSkill.GetTagObjList():
        tagID = tagBatObj.GetID()
        # 减
        if calcType == 2:
            diffType = 0
            tagXP = tagBatObj.GetXP()
            diffValue = min(tagXP, calcValue) # 取较小值,不足时剩多少减多少
            updValue = tagXP - diffValue
            tagBatObj.SetXP(updValue, False)
            GameWorld.DebugLog("    减怒气: tagID=%s,diffValue=%s,tagXP=%s,updXP=%s" % (tagID, diffValue, tagXP, updValue))
            Sync_PropertyRefreshView(turnFight, tagBatObj, attrID, updValue, diffValue, diffType, skillID, relatedSkillID)
        # ͵
        elif calcType == 3:
            diffType = 0
            tagXP = tagBatObj.GetXP()
            diffValue = min(tagXP, calcValue) # 取较小值,不足时剩多少减多少
            updValue = tagXP - diffValue
            tagBatObj.SetXP(updValue, False)
            GameWorld.DebugLog("    偷怒气: tagID=%s,diffValue=%s,tagXP=%s,updXP=%s" % (tagID, diffValue, tagXP, updValue))
            Sync_PropertyRefreshView(turnFight, tagBatObj, attrID, updValue, diffValue, diffType, skillID, relatedSkillID)
            curStealTotal += diffValue
        # 其他默认加
        else:
            diffType = 1
            tagXP = tagBatObj.GetXP()
            diffValue = calcValue
            updValue = tagXP + diffValue
            tagBatObj.SetXP(updValue, False)
            GameWorld.DebugLog("    加怒气: tagID=%s,diffValue=%s,tagXP=%s,updXP=%s" % (tagID, diffValue, tagXP, updValue))
            Sync_PropertyRefreshView(turnFight, tagBatObj, attrID, updValue, diffValue, diffType, skillID, relatedSkillID)
    if curStealTotal > 0:
        diffType = 1
        curXP = curBatObj.GetXP()
        diffValue = curStealTotal
        updValue = curXP + diffValue
        curBatObj.SetXP(updValue, False)
        GameWorld.DebugLog("    偷总怒气: curID=%s,curStealTotal=%s,curXP=%s,updXP=%s" % (curID, curStealTotal, curXP, updValue))
        Sync_PropertyRefreshView(turnFight, curBatObj, attrID, updValue, diffValue, diffType, skillID, relatedSkillID)
    DoAttackResult(turnFight, curBatObj, useSkill)
    return
def DoAttackResult(turnFight, curBatObj, useSkill):
    '''执行攻击结果,技能、buff通用
    @param curBatObj: 施法方或buff归属方
    '''
    if curBatObj:
        # 反弹、吸血 的原因,需要统一处理后结算,可能导致HP为负值,修正为0
        if curBatObj.GetHP() < 0:
            curBatObj.SetHP(0)
    skillID = useSkill.GetSkillID()
    curBatObj.AddSkillUseCnt(skillID)
    # 需先通知伤血 - 前端按顺序优先表现主技能内容,
    enhanceBySkill = useSkill.GetEnhanceBySkill()
    if not enhanceBySkill:
        Sync_UseSkill(turnFight, curBatObj, useSkill)
        __doCostZhanchui(turnFight, curBatObj, useSkill)
        __doSkillUserAnger(turnFight, curBatObj, useSkill)
    # 统计死亡
    killObjIDList = [] # 击杀的目标ID列表
    for tagObj in useSkill.GetTagObjList():
        tagID = tagObj.GetID()
        hurtObj = useSkill.GetHurtObj(tagID)
        if hurtObj and not hurtObj.HaveHurtType(ChConfig.HurtTYpe_Recovery):
            __doSkillHurtAnger(tagObj, hurtObj.GetLostHP(), useSkill)
        if tagObj.GetHP() <= 0:
            killObjIDList.append(tagID)
            TurnAttack.SetObjKilled(turnFight, tagObj, curBatObj, useSkill)
    if curBatObj.GetHP() <= 0:
        TurnAttack.SetObjKilled(turnFight, curBatObj)
    # 结算副本相关的攻击结果,仅主动发起玩家阵容武将触发
    curPlayer = turnFight.curPlayer
    if curPlayer and curBatObj.GetOwnerID() == curPlayer.GetPlayerID():
        FBLogic.OnPlayerLineupAttackResult(curPlayer, curBatObj, killObjIDList, useSkill, turnFight.mapID, turnFight.funcLineID)
    # 额外触发技能
    __doUseEnhanceSkill(turnFight, curBatObj, useSkill)
    # 循环触发被动,待扩展
    # 最后处理反击 或 连击
    return
def __doCostZhanchui(turnFight, curBatObj, useSkill):
    turnFight.mapID
    if turnFight.mapID != ChConfig.Def_FBMapID_Main:
        ## 仅主线战斗需要
        return
    curPlayer = turnFight.curPlayer
    if not curPlayer:
        return
    # 仅主动发起玩家阵容武将释放技能需要消耗
    if curBatObj.GetOwnerID() != curPlayer.GetPlayerID():
        return
    costZhanchui = 0
    batType = useSkill.GetBatType()
    if SkillCommon.isAngerSkill(useSkill):
        costZhanchui = IpyGameDataPY.GetFuncCfg("ZhanchuiCost", 2)
    elif batType == ChConfig.TurnBattleType_Combo:
        costZhanchui = IpyGameDataPY.GetFuncCfg("ZhanchuiCost", 3)
    elif batType == ChConfig.TurnBattleType_AtkBack:
        costZhanchui = IpyGameDataPY.GetFuncCfg("ZhanchuiCost", 4)
    elif SkillCommon.isTurnNormalSkill(useSkill):
        costZhanchui = IpyGameDataPY.GetFuncCfg("ZhanchuiCost", 1)
    if costZhanchui <= 0:
        return
    fightPoint = max(curPlayer.GetFightPoint(), 1) # 主线战斗消耗倍值,默认1
    costZhanchuiTotal = costZhanchui * fightPoint
    if not PlayerControl.PayMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, costZhanchuiTotal, isNotify=False):
        # 不足时,有多少扣多少
        nowMoney = PlayerControl.GetMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao)
        PlayerControl.PayMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, min(nowMoney, costZhanchuiTotal), isNotify=False)
    return
def __doSkillUserAnger(turnFight, curBatObj, useSkill):
    ## 技能释放者怒气相关
    if SkillCommon.isAngerSkill(useSkill):
        curBatObj.SetXP(0)
    elif SkillCommon.isTurnNormalSkill(useSkill):
        addXP = IpyGameDataPY.GetFuncCfg("AngerXP", 3)
        AddTurnFightXP(curBatObj, addXP, "skillID:%s" % useSkill.GetSkillID())
    return
def __doSkillHurtAnger(batObj, lostHP, useSkill):
    ## 结算受伤者怒气
    if lostHP <= 0:
        return
    addXP = IpyGameDataPY.GetFuncCfg("AngerXP", 4)
    if addXP <= 0:
        return
    AddTurnFightXP(batObj, addXP, "skillID:%s" % useSkill.GetSkillID())
    return
def AddTurnFightXP(gameObj, addXP, reason=""):
    ## 回合战斗增加XP
    if addXP <= 0 or not addXP:
        #GameWorld.DebugLog("        没有增加XP! curID=%s" % (gameObj.GetID()))
        return
    posNum = gameObj.GetPosNum()
    if posNum <= 0:
        #非主战单位不加
        return
    curXP = gameObj.GetXP()
    updXP = curXP + addXP
    gameObj.SetXP(updXP)
    GameWorld.DebugLog("        更新XP: curID=%s,curXP=%s,addXP=%s,updXP=%s,reason=%s" % (gameObj.GetID(), curXP, addXP, updXP, reason))
    return
def __doUseEnhanceSkill(turnFight, curBatObj, useSkill):
    if useSkill.GetEnhanceBySkill():
        GameWorld.DebugLog("自身为额外触发的技能不再触发额外技能! skillID=%s" % useSkill.GetSkillID())
        return
    enhanceSkillIDList = useSkill.GetEnhanceSkillList()
    if not enhanceSkillIDList:
        return
    GameWorld.DebugLog("额外触发的技能! skillID=%s,enhanceSkillIDList=%s" % (useSkill.GetSkillID(), enhanceSkillIDList))
    # 根据触发技能的特点决定是触发一次还是 触发多次
    # 群体BUFF的请参考 IsPlayerUseSkill 客户端决定对象,一样可以实现同样效果
    tagObjList = useSkill.GetTagObjList()
    for enhanceSkillID in enhanceSkillIDList:
        enhanceSkillData = IpyGameDataPY.GetIpyGameData("Skill", enhanceSkillID)
        if not enhanceSkillData:
            continue
        # 继承主技能目标
        if enhanceSkillData.GetTagAim() == ChConfig.SkillTagAim_MainSkill:
            GameWorld.DebugLog("    额外触发技能,继承主技能目标! enhanceSkillID=%s" % enhanceSkillID)
            # 额外触发的技能直接在外层检查概率,如果都没有触发则不需要再处理
            enhanceRate = enhanceSkillData.GetHappenRate()
            enchanceTagObjList = []
            for tagObj in tagObjList:
                tagID = tagObj.GetID()
                if tagObj.GetHP() <= 0:
                    GameWorld.DebugLog("    已被击杀不触发: tagID=%s" % (tagID))
                    continue
                hurtObj = useSkill.GetHurtObj(tagID)
                if not hurtObj:
                    GameWorld.DebugLog("    没有伤血不触发: tagID=%s" % (tagID))
                    continue
                if hurtObj.HaveHurtType(ChConfig.HurtType_Miss):
                    GameWorld.DebugLog("    闪避的对象不再触发技能: tagID=%s,enhanceSkillID=%s" % (tagID, enhanceSkillID))
                    continue
                if enhanceRate and enhanceRate != ChConfig.Def_MaxRateValue and not GameWorld.CanHappen(enhanceRate, ChConfig.Def_MaxRateValue):
                    GameWorld.DebugLog("    概率不触发: tagID=%s,enhanceRate=%s" % (tagID, enhanceRate))
                    continue
                enchanceTagObjList.append(tagObj)
            if enchanceTagObjList:
                OnUseSkill(turnFight, curBatObj, enhanceSkillData, enchanceTagObjList, enhanceBySkill=useSkill)
            continue
        GameWorld.DebugLog("    额外触发技能,重新锁定目标! enhanceSkillID=%s" % enhanceSkillID)
        OnUseSkill(turnFight, curBatObj, enhanceSkillData, enhanceBySkill=useSkill)
    return
def __doSkillHurtHP(attacker, defObj, curSkill):
    ## 执行技能伤血,只计算伤血,其他逻辑等技能同步后再处理
    # @return: None - 没有执行成功,即忽略该目标
    atkObj = attacker
    atkID = atkObj.GetID()
    defID = defObj.GetID()
    hurtObj = curSkill.AddHurtObj(defID)
    #检查是否几率触发
    if not curSkill.GetEnhanceBySkill(): # 额外触发的技能概率在触发时判断
        rate = curSkill.GetHappenRate()
        if rate and rate != ChConfig.Def_MaxRateValue and not GameWorld.CanHappen(rate, ChConfig.Def_MaxRateValue):
            #GameWorld.Log('检查是否几率触发 = %s失败 = %s'%(rate, useSkill.GetSkillName()))
            #放这里,兼容群攻时如果该技能是有概率的,则每个目标单独判断
            hurtObj.AddHurtType(ChConfig.HurtType_Fail)
            hurtObj.SetCurHP(defObj.GetHP())
            return hurtObj
    atkEffIDNum = curSkill.FindEffectID(ChConfig.Def_Skill_Effect_Attack)
    atkSkillValue = 0
    atkSkillPer = curSkill.GetEffectValue(atkEffIDNum, 0)
    dHP = defObj.GetHP()                # 防守方当前血量
    dMaxHP = defObj.GetMaxHP()          # 防守方最大血量
    zhansha = False # 斩杀特殊处理
    # նɱ
    if zhansha:
        pass
    else:
        hurtValue, hurtTypes = CalcHurtHP(atkObj, defObj, curSkill, atkSkillValue, atkSkillPer)
        # 各种减伤 吸收盾处理
        #hurtValue = CalcHurtHPWithBuff(atkObj, defObj, hurtValue, curSkill, tick)
        #伤害结构体
        hurtObj.SetHurtTypes(hurtTypes)
        hurtObj.SetHurtHP(hurtValue)
        remainHP = min(dMaxHP, max(0, dHP - hurtValue)) # 剩余血量
    remainHP = int(remainHP)    #防范
    lostHP = dHP - remainHP # 实际掉血量
    hurtObj.SetLostHP(lostHP)
    hurtObj.SetCurHP(remainHP)
    defObj.SetHP(remainHP)
    GameWorld.DebugLog("    伤血: atkID=%s,defID=%s,hurtValue=%s,lostHP=%s,%s/%s" % (atkID, defID, hurtValue, lostHP, defObj.GetHP(), defObj.GetMaxHP()))
    #反弹伤害
    CalcBounceHP(atkObj, defObj, hurtObj, curSkill)
    #吸血
    CalcSuckBlood(atkObj, defObj, hurtObj, curSkill)
    TurnAttack.AddTurnObjHurtValue(atkObj, defObj, hurtValue, lostHP, curSkill)
    return hurtObj
def CalcHurtHP(atkObj, defObj, curSkill, atkSkillValue, atkSkillPer, **atkwargs):
    '''计算伤害
    '''
    pmType = GetPMType(atkObj, curSkill)
    ignoreDef = IsIgnoreDef(curSkill)
    atkID = atkObj.GetID()
    defID = defObj.GetID()
    skillID = curSkill.GetSkillID()
    isTurnNormalSkill = SkillCommon.isTurnNormalSkill(curSkill)
    isAngerSkill = SkillCommon.isAngerSkill(curSkill)
    isAtkbackSkill = SkillCommon.isAtkbackSkill(curSkill)
    angerOverflow = 0 # 怒气溢出值
    mustHit = False
    if isAngerSkill:
        mustHit = True
        GameWorld.DebugLog("        XP必命中")
        angerOverflow = max(atkObj.GetXP() - IpyGameDataPY.GetFuncCfg("AngerXP", 2), 0)
    #命中公式 攻击方类型不同,公式不同
    if not mustHit:
        aMissRateDef = atkObj.GetAttrValue(ChConfig.AttrID_MissRateDef) #atkObj.GetHit() # 抗闪避率 - 命中
        dMissRate = defObj.GetAttrValue(ChConfig.AttrID_MissRate) # 闪避率
        missNum = defObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnMissNum)
        missRate = eval(IpyGameDataPY.GetFuncCompileCfg("MissCfg", 1))
        if GameWorld.CanHappen(missRate):
            return 0, pow(2, ChConfig.HurtType_Miss)
    hurtTypes = pow(2, ChConfig.HurtType_Normal)
    isSuperHit = False # 是否暴击
    if isSuperHit:
        hurtTypes |= pow(2, ChConfig.HurtType_SuperHit)
    isParry = False # 是否格挡
    if isParry:
        hurtTypes |= pow(2, ChConfig.HurtType_Parry)
    if ignoreDef:
        hurtTypes |= pow(2, ChConfig.HurtType_IgnoreDef)
    #参与运算的数值
    rand = random.random()                #种子数 0~1
    aAtk = atkObj.GetAttrValue(ChConfig.AttrID_Atk) # 攻击方最大攻击
    dHP = defObj.GetHP()
    dDef = 0 if ignoreDef else defObj.GetAttrValue(ChConfig.AttrID_Def) # 防守方防御力
    aFinalDamPer = atkObj.GetAttrValue(ChConfig.AttrID_FinalDamPer) # 最终加成
    dFinalDamPerDef = defObj.GetAttrValue(ChConfig.AttrID_FinalDamPerDef) # 最终减伤
    aNormalSkillPer, dNormalSkillPerDef = 0, 0
    if isTurnNormalSkill:
        aNormalSkillPer = atkObj.GetAttrValue(ChConfig.AttrID_NormalSkillPer) # 普技增伤
        dNormalSkillPerDef = defObj.GetAttrValue(ChConfig.AttrID_NormalSkillPerDef) # 普技减伤
    aAngerSkillPer, dAngerSkillPerDef = 0, 0
    if isAngerSkill:
        aAngerSkillPer = atkObj.GetAttrValue(ChConfig.AttrID_AngerSkillPer) # 普技增伤
        dAngerSkillPerDef = defObj.GetAttrValue(ChConfig.AttrID_AngerSkillPerDef) # 普技减伤
    # 物法增减伤
    if pmType == IPY_GameWorld.ghtMag: # 法伤
        aPMDamPer = atkObj.GetAttrValue(ChConfig.AttrID_MagDamPer)
        dPMDamPerDef = defObj.GetAttrValue(ChConfig.AttrID_MagDamPerDef)
    else: # 物伤
        aPMDamPer = atkObj.GetAttrValue(ChConfig.AttrID_PhyDamPer)
        dPMDamPerDef = defObj.GetAttrValue(ChConfig.AttrID_PhyDamPerDef)
    # 所有万分率参数统一除10000.0
    atkSkillPer /= 10000.0
    aNormalSkillPer /= 10000.0
    dNormalSkillPerDef /= 10000.0
    aAngerSkillPer /= 10000.0
    dAngerSkillPerDef /= 10000.0
    aPMDamPer /= 10000.0
    dPMDamPerDef /= 10000.0
    aFinalDamPer /= 10000.0
    dFinalDamPerDef /= 10000.0
    GameWorld.DebugLog("伤血计算: atkID=%s,defID=%s,skillID=%s,atkSkillPer=%s,aAtk=%s,dDef=%s,dHP=%s,hurtTypes=%s"
                       % (atkID, defID, skillID, atkSkillPer, aAtk, dDef, dHP, hurtTypes))
    if "hurtFormulaKey" in atkwargs:
        aBurnValue = atkwargs.get('burnValue', 0)
        aBurnPer = atkwargs.get('burnPer', 0)
        hurtFormulaKey = atkwargs.get('hurtFormulaKey', None)
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("DOTFormula", 1))
    elif isTurnNormalSkill:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 1))
        GameWorld.DebugLog("    普攻技能伤害=%s" % (hurtValue))
    elif isAngerSkill:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 2))
        GameWorld.DebugLog("    怒气技能伤害=%s" % (hurtValue))
    elif isAtkbackSkill:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 3))
        GameWorld.DebugLog("    反击伤害=%s" % (hurtValue))
    else:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 4))
        GameWorld.DebugLog("    其他伤害=%s" % (hurtValue))
    hurtValue = max(1, int(hurtValue)) # 负值、保底防范
    return hurtValue, hurtTypes
def CalcBounceHP(atkObj, defObj, hurtObj, curSkill):
    '''计算反弹反弹伤害
    '''
    if not atkObj.GetCanAttack():
        return
    bounceHP = int(hurtObj.GetHurtHP() * random.uniform(0.1, 0.2))
    if bounceHP <= 0:
        return
    remainHP = atkObj.GetHP() - bounceHP # 反弹允许弹到负值,如果最终吸血没有吸到正值则算死亡
    hurtObj.SetBounceHP(bounceHP)
    atkObj.SetHP(remainHP)
    GameWorld.DebugLog("    反弹伤害=%s,remainHP=%s" % (bounceHP, remainHP))
    TurnAttack.AddTurnObjHurtValue(defObj, atkObj, bounceHP, bounceHP, isBounce=True)
    return
def CalcSuckBlood(atkObj, defObj, hurtObj, curSkill):
    '''计算吸血
    '''
    suckHP = int(hurtObj.GetHurtHP() * random.uniform(0.1, 0.2))
    if suckHP <= 0:
        return
    curHP = atkObj.GetHP()
    maxHP = atkObj.GetMaxHP()
    remainHP = min(curHP + suckHP, maxHP)
    cureHP = remainHP - curHP # 实际治疗量
    hurtObj.SetSuckHP(suckHP)
    atkObj.SetHP(remainHP)
    GameWorld.DebugLog("    吸血=%s,remainHP=%s/%s" % (suckHP, remainHP, maxHP))
    # 吸血暂定算治疗
    TurnAttack.AddTurnObjCureHP(atkObj, atkObj, suckHP, cureHP)
    return
def CalcCureHP(userObj, tagObj, curSkill, largeNum=False):
    ''' 计算治疗值
    '''
    cureBaseValue = 0     #治疗基础值
    effIDNum = curSkill.FindEffectID(ChConfig.Def_Skill_Effect_Cure)
    skillPer = curSkill.GetEffectValue(effIDNum, 0)
    cureType = curSkill.GetEffectValue(effIDNum, 1)
    #获得基础治疗值
    if cureType == ChConfig.Def_Cure_Attack:
        cureBaseValue = userObj.GetAtk()
    elif cureType == ChConfig.Def_Cure_MaxHP:
        cureBaseValue = userObj.GetMaxHP()
    #elif cureType == ChConfig.Def_Cure_HurtValue:
    #    cureBaseValue = GameObj.GetLastHurtValue(userObj)
    elif cureType == ChConfig.Def_Cure_TagMaxHP:
        cureBaseValue = 0 if not tagObj else tagObj.GetMaxHP()
    #skillPer += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(userObj, None, curSkill, ChConfig.TriggerType_AddHP)
    # 回合制
    curePer = 0 # 治疗加成
    cureDefPer = 0 # 敌方的弱化治疗
    angerOverflow = 0 # 怒气溢出值
    if SkillCommon.isAngerSkill(curSkill):
        angerOverflow = max(userObj.GetXP() - IpyGameDataPY.GetFuncCfg("AngerXP", 2), 0)
        #enemyObj = TurnAttack.GetEnemyObj(userObj)
        #curePer += GameObj.GetCurePer(userObj)
        #if enemyObj:
        #    cureDefPer += GameObj.GetCureDefPer(enemyObj)
    skillPer /= float(ChConfig.Def_MaxRateValue)
    curePer /= float(ChConfig.Def_MaxRateValue)
    cureDefPer /= float(ChConfig.Def_MaxRateValue)
    baseValue = max(0, cureBaseValue) # 防止基值被弱化为负值,在恢复比例也是负值的情况下负负得正导致可以恢复血量
    #公式计算治疗值
    cureHP = eval(IpyGameDataPY.GetFuncCompileCfg("CureFormula", 1))
    if not largeNum:
        cureHP = min(cureHP, ChConfig.Def_UpperLimit_DWord)
    cureHP = max(1, int(cureHP)) # 保底1点
    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
def Sync_UseSkill(turnFight, curBatObj, useSkill):
    ## 通知释放技能
    poolMgr = ObjPool.GetPoolMgr()
    clientPack = poolMgr.acquire(ChPyNetSendPack.tagSCUseSkill)
    clientPack.ObjID = curBatObj.GetID()
    clientPack.PMType = GetPMType(curBatObj, useSkill)
    clientPack.BattleType = useSkill.GetBatType()
    clientPack.CurHP = curBatObj.GetHP() % ChConfig.Def_PerPointValue
    clientPack.CurHPEx = curBatObj.GetHP() / ChConfig.Def_PerPointValue
    clientPack.SkillID = useSkill.GetSkillID()
    clientPack.HurtList = []
    for hurtObj in useSkill.GetHurtObjList():
        hurt = poolMgr.acquire(ChPyNetSendPack.tagSCUseSkillHurt)
        hurt.ObjID = hurtObj.GetObjID()
        hurt.AttackTypes = hurtObj.GetHurtTypes()
        hurt.HurtHP = hurtObj.GetHurtHP() % ChConfig.Def_PerPointValue
        hurt.HurtHPEx = hurtObj.GetHurtHP() / ChConfig.Def_PerPointValue
        hurt.CurHP = hurtObj.GetCurHP() % ChConfig.Def_PerPointValue
        hurt.CurHPEx = hurtObj.GetCurHP() / ChConfig.Def_PerPointValue
        hurt.SuckHP = min(hurtObj.GetSuckHP(), ChConfig.Def_UpperLimit_DWord)
        hurt.BounceHP = min(hurtObj.GetBounceHP(), ChConfig.Def_UpperLimit_DWord)
        clientPack.HurtList.append(hurt)
    clientPack.HurtCount = len(clientPack.HurtList)
    turnFight.addBatPack(clientPack)
    return
def Sync_PropertyRefreshView(turnFight, curBatObj, attrID, value, diffValue, diffType=0, skillID=0, relatedSkillID=0):
    '''通知对象属性刷新展示B418
    @param attrID: 通知变化的属性ID
    @param diffValue: 变化值
    @param diffType: 变化类型,0-减少;1-增加
    @param skillID: 使用的技能表ID,即哪个技能的效果
    @param relatedSkillID: 关联的技能ID,一般是主技能ID,非主技能额外触发的为0
    '''
    if attrID not in ChConfig.CDBRefresh_AttrIDDict:
        return
    refreshType, isBig = ChConfig.CDBRefresh_AttrIDDict[attrID]
    poolMgr = ObjPool.GetPoolMgr()
    clientPack = poolMgr.acquire(ChPyNetSendPack.tagSCObjPropertyRefreshView)
    clientPack.ObjID = curBatObj.GetID()
    clientPack.RefreshType = refreshType
    if isBig:
        clientPack.Value = value % ShareDefine.Def_PerPointValue
        clientPack.ValueEx = value / ShareDefine.Def_PerPointValue
        clientPack.DiffValue = diffValue % ShareDefine.Def_PerPointValue
        clientPack.DiffValueEx = diffValue / ShareDefine.Def_PerPointValue
    else:
        clientPack.Value = value
        clientPack.ValueEx = 0
        clientPack.DiffValue = diffValue
        clientPack.DiffValueEx = 0
    clientPack.DiffType = diffType
    clientPack.SkillID = skillID
    clientPack.RelatedSkillID = relatedSkillID
    turnFight.addBatPack(clientPack)
    return