10019 【砍树】回合战斗(战斗实例全部统一使用NPC,包含玩家自己及其他玩家镜像数据;)
7个文件已修改
216 ■■■■■ 已修改文件
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PetControl.py 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
@@ -2364,6 +2364,10 @@
    if helpBattleFormatKey:
        hurtFormulaKey = helpBattleFormatKey
        
    # 回合战斗
    if atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightTimeline):
        hurtFormulaKey = "TurnFight"
    if hurtFormulaKey not in hurtDist:
        GameWorld.ErrLog("CalcAttackValue.txt 伤害公式未配置, key=%s" % (hurtFormulaKey))
        return 0, ChConfig.Def_HurtType_Miss
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/SummonNPC_Attack_NormalNPC.py
@@ -59,7 +59,7 @@
def GetTagRelation(curSummonNPC, curTagNormalNPC, skill, tick):
    
    #防守方阵营
    defenderCampType = NPCCommon.GetFaction(curTagNormalNPC)
    defenderCampType = GameObj.GetFaction(curTagNormalNPC)
    
    #玩家的召唤兽才能攻击普通NPC
    npcOwner_Player = NPCCommon.GetSummonNPCOwner(IPY_GameWorld.gotPlayer, curSummonNPC)
@@ -73,7 +73,7 @@
        return ChConfig.Type_Relation_Enemy, ChConfig.Def_PASysMessage_None
    
    #攻击方阵营
    attackerCampType = curSummonNPC.GetDictByKey(ChConfig.Def_NpcDictKey_CampType)
    attackerCampType = GameObj.GetFaction(curSummonNPC)
    if attackerCampType != defenderCampType:
        return ChConfig.Type_Relation_Enemy, ChConfig.Def_PASysMessage_None
    return ChConfig.Type_Relation_Friend, ChConfig.Def_PASysMessage_None
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -34,28 +34,31 @@
import BuffSkill
import FBCommon
import ItemControler
import PlayerPet
# 回合战斗流程状态
(
FightState_Start,
FightState_Fighting,
FightState_Win,
FightState_Fail,
FightState_Over,
) = range(5)
FightState_Start, # 0 起始状态,无特殊意义,仅代表开始了,与Over对应
FightState_PrepareOK, # 1 准备完毕,包含对战NPC召唤OK、其他等
FightState_Fighting, # 2 战斗中
FightState_FightEnd, # 3 战斗结束
FightState_Award, # 4 结算奖励
FightState_Over, # 5 结束状态,无特殊意义,仅代表所有处理结束了,与Start对应
) = range(6)
# 回合战斗复活类型
(
RebornType_PetSkill,
) = range(1, 1 + 1)
def GetObjName(gameObj):
    objName = gameObj.GetName()
    mirrorPlayerID = gameObj.GetDictByKey(ChConfig.Def_NPC_Dict_MirrorPlayerID)
    if mirrorPlayerID:
        objName = "%s[%s]" % (objName, mirrorPlayerID)
    faction = GameObj.GetFaction(gameObj)
    fightPlaceNum = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_FightPetPlaceNum)
    curID = gameObj.GetNPCID() if gameObj.GetGameObjType() == IPY_GameWorld.gotNPC else ""
    return "%s%s %s%s" % ("A" if faction == 1 else "B", fightPlaceNum, objName, curID)
    curID = gameObj.GetDictByKey(ChConfig.Def_NPC_Dict_MirrorPlayerID)
    if not curID and gameObj.GetGameObjType() == IPY_GameWorld.gotNPC:
        curID = gameObj.GetNPCID()
    return "%s%s %s[%s-%s]" % ("A" if faction == 1 else "B", fightPlaceNum, objName, gameObj.GetID(), curID)
#// B4 10 回合制战斗 #tagCMTurnFight
#
@@ -96,18 +99,14 @@
def DoTrunFight(curPlayer, mapID, funcLineID, tagPlayerID, tick):
    playerID = curPlayer.GetPlayerID()
    posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
    GameWorld.DebugLog("===== 执行回合制战斗: mapID=%s,funcLineID=%s,playerID=%s,tagPlayerID=%s" % (mapID, funcLineID, playerID, tagPlayerID))
    PlayerViewCacheTube.UpdPlayerPropPlusCache(curPlayer) # 强制刷一下自己的镜像缓存
    factionInfoA = {"playerID":playerID, "pet":PlayerPet.GetPetCacheInfo(curPlayer)}
    factionInfoB = {}
    ipyData = None
    tagObj = None
    if tagPlayerID:
        npcID = ChConfig.Def_NPCID_PVP
        tagObj = NPCCommon.SummonMapNpc(npcID, posX, posY, sightLevel=playerID, mirrorPlayerID=tagPlayerID)
        if not tagObj:
            return
        PlusData = PlayerViewCacheTube.GetPlayerPropPlusDictByID(playerID)[1] # 从缓存中获取
        petCacheInfo = PlusData.get("Pet")
        fightPetObjListB = PetControl.CalloutFightPet(tagObj, petCacheInfo)
        PlusData = PlayerViewCacheTube.GetPlayerPropPlusDictByID(tagPlayerID)[1] # 从缓存中获取
        factionInfoB = {"playerID":tagPlayerID, "pet":PlusData.get("Pet")}
    else:
        ipyData = IpyGameDataPY.GetIpyGameData("FBTurn", mapID, funcLineID)
        if not ipyData:
@@ -115,25 +114,74 @@
        npcID = ipyData.GetNPCID()
        if not npcID:
            return
        tagObj = NPCCommon.SummonMapNpc(npcID, posX, posY, sightLevel=playerID)
        if not tagObj:
            return
        petNPCIDList = ipyData.GetPetNPCIDList()
        petCacheInfo = [] # 从配表中读取组合,技能默认取NPC表配置的
        for state, petNPCID in enumerate(petNPCIDList, 1):
            petCacheInfo.append({"npcID":petNPCID, "state":state, "quality":0})
        fightPetObjListB = PetControl.CalloutFightPet(tagObj, petCacheInfo)
        factionInfoB = {"npcID":npcID, "pet":petCacheInfo}
        
    ret = ProcessAutoTurnFight(mapID, funcLineID, factionInfoA, factionInfoB, tick, curPlayer)
    if not ret:
        return
    isWin, turnNum, turnMax, factionTotalHurtDict, playbackID = ret
    # 结算奖励...待扩展
    awardItemList = []
    if isWin and ipyData:
        # 山寨测试先默认都是首次奖励,正式后需删除
        awardItemList = ipyData.GetAwardItemListFirst()
    GameWorld.DebugLog("奖励物品: %s" % awardItemList)
    ItemControler.GivePlayerItemOrMail(curPlayer, awardItemList, event=["TurnFight", False, {"mapID":mapID, "funcLineID":funcLineID}])
    overMsg = {"isWin":isWin, "itemInfo":FBCommon.GetJsonItemList(awardItemList), "totalHurt":factionTotalHurtDict.get(1, 0)}
    playbackID and overMsg.update({"playbackID":playbackID})
    SyncTurnFightState(curPlayer, mapID, funcLineID, tagPlayerID, FightState_Award, turnNum, turnMax, overMsg)
    return
def ProcessAutoTurnFight(mapID, funcLineID, factionInfoA, factionInfoB, tick, syncPlayer=None, isSavePlayback=False):
    ''' 处理自动回合战斗过程,仅做战斗流程处理,不做及其他功能逻辑
    @param mapID: 可视为功能ID
    @param funcLineID: 对功能ID的扩展
    @param factionInfoA: A方阵营信息,playerID 和 npcID 二选一 {"playerID":主体玩家镜像ID, "npcID":主体NPCID, "pet":[{出战灵宠信息兼容玩家缓存的灵宠信息k:v, ...}, ...]}
    @param factionInfoB: B方阵营信息
    @param syncPlayer: 需要通知的玩家,可能为None,注意仅做通知用,该玩家可能和AB阵营无任何关系
    @param isSavePlayback: 是否保存战斗回放
    @return: None-无结果;或 (isWin, turnNum, turnMax, factionTotalHurtDict)
                        isWin: 是否获胜
                        turnNum: 打了几回合
                        factionTotalHurtDict: 双方伤血统计
    '''
    if syncPlayer:
        syncPlayer.SetCanAttack(False) # 该玩家设置为不可被攻击
        playerID = syncPlayer.GetPlayerID()
        sightLevel, posX, posY = playerID, syncPlayer.GetPosX(), syncPlayer.GetPosY()
    else:
        playerID = 0
        sightLevel, posX, posY = 99, 192, 109 # 系统默认处理的层级及坐标
    objRetA = __SummonFactionObjs(factionInfoA, sightLevel, posX, posY)
    objRetB = __SummonFactionObjs(factionInfoB, sightLevel, posX, posY)
    objA, petObjListA, factionSyncInfoA = objRetA if objRetA else (None, [])
    objB, petObjListB, factionSyncInfoB = objRetB if objRetB else (None, [])
    if not objA or not objB:
        return
    playerIDA = objA.GetDictByKey(ChConfig.Def_NPC_Dict_MirrorPlayerID) # 可能为0,非玩家镜像时为0
    playerIDB = objB.GetDictByKey(ChConfig.Def_NPC_Dict_MirrorPlayerID) # 可能为0,非玩家镜像时为0
    objNameA = GetObjName(objA)
    objNameB = GetObjName(objB)
    GameWorld.DebugLog("===== 执行回合制战斗: %s VS %s =====" % (objNameA, objNameB))
    turnMax = IpyGameDataPY.GetFuncCfg("TurnFight", 1)
    # 宠物先攻击
    fightPetObjListA = PetControl.CalloutFightPet(curPlayer)
    factionListA = fightPetObjListA + [curPlayer]
    factionListB = fightPetObjListB + [tagObj]
    factionListA = petObjListA + [objA]
    factionListB = petObjListB + [objB]
    atkFactionList = [factionListA, factionListB]
    
    # 设置战斗主体
    curPlayer.SetDict(ChConfig.Def_Obj_Dict_TurnFightMainRole, 1)
    tagObj.SetDict(ChConfig.Def_Obj_Dict_TurnFightMainRole, 1)
    objA.SetDict(ChConfig.Def_Obj_Dict_TurnFightMainRole, 1)
    objB.SetDict(ChConfig.Def_Obj_Dict_TurnFightMainRole, 1)
    
    # 战斗前初始化,可能会改变攻速,所以先初始化
    for faction, factionObjList in enumerate(atkFactionList, 1):
@@ -149,22 +197,24 @@
        fightObjList.sort(key=lambda o: (GameObj.GetAtkSpeed(o), (10 - GameObj.GetFaction(o)), o.GetID()), reverse=True)
    #方式2:根据阵营主角攻速决定先手,然后每个阵营轮流固定位置攻击
    elif sortType == 2:
        if GameObj.GetAtkSpeed(curPlayer) < GameObj.GetAtkSpeed(tagObj):
        if GameObj.GetAtkSpeed(objA) < GameObj.GetAtkSpeed(objB):
            atkFactionList = [factionListB, factionListA]
        for i in range(len(factionListA)):
            for factionObjList in atkFactionList:
                fightObjList.append(factionObjList[i])
                
    SyncTurnFightState(syncPlayer, mapID, funcLineID, playerIDB, FightState_PrepareOK, msg=[factionSyncInfoA, factionSyncInfoB])
    isWin = None
    for turnNum in range(1, turnMax + 1):
        GameWorld.DebugLog("【----- 回合制战斗轮次: %s -----】" % turnNum)
        SyncTurnFightState(curPlayer, mapID, funcLineID, tagPlayerID, FightState_Fighting, turnNum, turnMax)
        SyncTurnFightState(syncPlayer, mapID, funcLineID, playerIDB, FightState_Fighting, turnNum, turnMax)
        
        # 回合开始: 做一些每回合重置逻辑或者某些根据回合触发的效果等
        for gameObj in fightObjList:
            TurnFightObjPerTurnStart(gameObj, turnNum, tick)
            
        isWin = CheckIswin(curPlayer, tagObj)
        isWin = CheckIswin(objA, objB)
        if isWin != None:
            break
        
@@ -179,7 +229,7 @@
                
            objType = gameObj.GetGameObjType()
            objID = gameObj.GetID()
            SyncTurnFightObjAction(curPlayer, turnNum, objType, objID)
            SyncTurnFightObjAction(syncPlayer, turnNum, objType, objID)
            
            if GameObj.GetHP(gameObj) <= 0:
                # 复活时机在自己行动节点
@@ -190,7 +240,7 @@
            objName = GetObjName(gameObj)
            curHP = GameObj.GetHP(gameObj)
            
            tagGameObj = tagObj if faction == 1 else curPlayer
            tagGameObj = objB if faction == 1 else objA
            tagObjType = tagGameObj.GetGameObjType()
            tagObjID = tagGameObj.GetID()
            tagHP = GameObj.GetHP(tagGameObj)
@@ -201,14 +251,16 @@
            if not DoAttack(gameObj, tagGameObj, tick):
                continue
            
            isWin = CheckIswin(curPlayer, tagObj)
            isWin = CheckIswin(objA, objB)
            if isWin != None:
                break
            
        if isWin != None:
            break
    GameWorld.DebugLog("--- 战斗结束处理 ---")
    GameWorld.DebugLog("--- 战斗结束处理 --- isWin=%s" % isWin)
    isWin = 1 if isWin else 0
    # 统计总伤害
    factionTotalHurtDict = {}
    for gameObj in fightObjList:
@@ -224,28 +276,46 @@
    for faction, totalHurt in factionTotalHurtDict.items():
        GameWorld.DebugLog("faction=%s, 阵营总输出: %s" % (faction, totalHurt))
        
    GameWorld.DebugLog("玩家剩余血量: %s / %s" % (GameObj.GetHP(curPlayer), GameObj.GetMaxHP(curPlayer)))
    GameWorld.DebugLog("对方剩余血量: %s / %s" % (GameObj.GetHP(tagObj), GameObj.GetMaxHP(tagObj)))
    GameWorld.DebugLog("A剩余血量: %s / %s" % (GameObj.GetHP(objA), GameObj.GetMaxHP(objA)))
    GameWorld.DebugLog("B剩余血量: %s / %s" % (GameObj.GetHP(objB), GameObj.GetMaxHP(objB)))
    
    # 结算奖励...待扩展
    awardItemList = []
    if isWin and ipyData:
        # 山寨测试先默认都是首次奖励,正式后需删除
        awardItemList = ipyData.GetAwardItemListFirst()
    GameWorld.DebugLog("奖励物品: %s" % awardItemList)
    ItemControler.GivePlayerItemOrMail(curPlayer, awardItemList, event=["TurnFight", False, {"mapID":mapID, "funcLineID":funcLineID}])
    overMsg = {"itemInfo":FBCommon.GetJsonItemList(awardItemList), "totalHurt":factionTotalHurtDict.get(1, 0)}
    overState = FightState_Win if isWin else FightState_Fail
    SyncTurnFightState(curPlayer, mapID, funcLineID, tagPlayerID, overState, turnNum, turnMax, str(overMsg))
    SyncTurnFightState(syncPlayer, mapID, funcLineID, playerIDB, FightState_FightEnd, turnNum, turnMax)
    
    for gameObj in fightObjList:
        TurnFightObjOverReset(gameObj, tick)
        
    GameWorld.DebugLog("===== 回合制战斗结束: mapID=%s,funcLineID=%s,playerID=%s,tagPlayerID=%s,isWin=%s,overState=%s"
                       % (mapID, funcLineID, playerID, tagPlayerID, isWin, overState))
    return
    playbackID = 0 # 战斗回放ID,可根据该ID查看回放
    #playbackInfo = "" # 战斗回放信息
    if isSavePlayback:
        pass
    GameWorld.DebugLog("===== 回合制战斗结束: mapID=%s,funcLineID=%s,playerIDA=%s,playerIDB=%s,isWin=%s"
                       % (mapID, funcLineID, playerIDA, playerIDB, isWin))
    return isWin, turnNum, turnMax, factionTotalHurtDict, playbackID
def __SummonFactionObjs(factionInfo, sightLevel, posX, posY):
    ## 召唤阵营战斗实例
    factionSyncInfo = {} # 同步前端的阵营信息,包含主ID、灵宠、其他灵通等
    playerID = factionInfo.get("playerID")
    npcID = factionInfo.get("npcID")
    if playerID:
        npcID = ChConfig.Def_NPCID_PVP
        mainObj = NPCCommon.SummonMapNpc(npcID, posX, posY, sightLevel=sightLevel, mirrorPlayerID=playerID)
        factionSyncInfo["playerID"] = playerID
    elif npcID:
        mainObj = NPCCommon.SummonMapNpc(npcID, posX, posY, sightLevel=sightLevel)
        factionSyncInfo["npcID"] = npcID
    else:
        return
    if not mainObj:
        return
    petObjList = PetControl.CalloutFightPet(mainObj, factionInfo.get("pet"))
    petObjIDList = []
    for petObj in petObjList:
        if petObj:
            petObjIDList.append(petObj.GetID())
    factionSyncInfo.update({"objID":mainObj.GetID(), "petObjIDList":petObjIDList})
    return mainObj, petObjList, factionSyncInfo
def CheckIswin(curPlayer, tagObj):
    ## 检查是否结束
@@ -824,6 +894,8 @@
    return useSkillResult
def SyncTurnFightState(curPlayer, mapID, funcLineID, tagPlayerID, state, turnNum=0, turnMax=0, msg=""):
    if not curPlayer:
        return
    clientPack = ChPyNetSendPack.tagMCTurnFightState()
    clientPack.Clear()
    clientPack.MapID = mapID
@@ -832,12 +904,14 @@
    clientPack.State = state
    clientPack.TurnNum = turnNum
    clientPack.TurnMax = turnMax
    clientPack.Msg = msg
    clientPack.Msg = str(msg)
    clientPack.Len = len(clientPack.Msg)    
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
def SyncTurnFightObjAction(curPlayer, turnNum, objType, objID):
    if not curPlayer:
        return
    clientPack = ChPyNetSendPack.tagMCTurnFightObjAction()
    clientPack.Clear()
    clientPack.TurnNum = turnNum
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1180,9 +1180,6 @@
#朋友
Type_Relation_Friend = 2
Def_NpcDictKey_CampType = 'CampType'
#阵营 三种 中立 正义 邪恶
CampType_Neutral = ShareDefine.CampType_Neutral  # 中立
CampType_Justice = ShareDefine.CampType_Justice  # 正义
CampType_Evil = ShareDefine.CampType_Evil  # 邪恶
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PetControl.py
@@ -124,16 +124,15 @@
    return
#---------------------------------------------------------------------
def CalloutFightPet(curObj, petCacheInfo=None):
    ## 出战上阵的灵宠
    # @param petCacheInfo: 玩家灵宠功能缓存信息
def CalloutFightPet(curObj, petCacheInfo):
    ## 召唤出战上阵的灵宠
    # @param curObj: 灵宠所属战斗实例
    # @param petCacheInfo: 出战灵宠信息列表,兼容灵宠功能缓存信息
    # @return: [第1位置实例, 第2, ...]  按位置顺序,实例可能为None
    
    fightPlaceCount = len(IpyGameDataPY.GetFuncEvalCfg("PetGoOutFight", 1)) # 战斗位置数
    fightPetObjList = [None] * fightPlaceCount
    if petCacheInfo == None and curObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
        petCacheInfo = PlayerPet.GetPetCacheInfo(curObj)
    if not petCacheInfo: # 没有灵宠
    if not petCacheInfo:
        return fightPetObjList
    
    fightPetDict = {} # 上阵的灵宠
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
@@ -423,7 +423,7 @@
            Sync_PlayerCache(curPlayer, findPlayerID)
        callFunc(curPlayer, findPlayerID, callData, dataDict)
        return dataDict
    PyGameData.g_viewCacheCallback[playerID] = [callFunc, callData]
    PyGameData.g_viewCacheCallback[playerID] = [callFunc, callData, syncClient]
    
    #GameWorld.DebugLog("发送到GameServer查询玩家缓存! playerID=%s,findPlayerID=%s" % (playerID, findPlayerID), playerID)
    sendPack = ChMapToGamePyPack.tagMGQueryPlayerCache()
@@ -470,10 +470,12 @@
    callback = PyGameData.g_viewCacheCallback.pop(playerID, None)
    if not callback:
        return
    callFunc, callData = callback
    callFunc, callData, syncClient = callback
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    if syncClient:
        Sync_PlayerCache(curPlayer, findPlayerID)
    callFunc(curPlayer, findPlayerID, callData, curPlayerPropDict)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/GameSkills/SkillCommon.py
@@ -574,7 +574,7 @@
    lvSummonNPC = curPlayer.GetLV()
    summonNPC.SetLV(lvSummonNPC)
    summonNPC.SetCountry(curPlayer.GetCountry())
    summonNPC.SetDict(ChConfig.Def_NpcDictKey_CampType, curPlayer.GetFaction())
    GameObj.SetFaction(summonNPC, curPlayer.GetFaction())
    summonNPC.GetNPCAngry().Init(ChConfig.Def_SummonNPC_Angry_Count)
    #设置召唤兽属性
    SetSummonNPCProperty(curPlayer, summonNPC, curSkill)