ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_21.py
@@ -1,63 +1,60 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#---------------------------------------------------------------------
##@package AIType_21
#巡逻守卫AI,攻击非本国家玩家,和红名玩家
#-------------------------------------------------------------------------------
#
# @author PanWei
# @date 2010-4-28下午06:01:33
# @version 1.4
##@package NPCAI.AIType_21
#
#模块详细说明.
# @change: "2010-05-12 18:30" zhengyang 添加注释
# @change: "2010-06-09 09:50" zhengyang 守卫npc加入异常状态眩晕判断
# @change: 2010-06-14 16:00 zhengyang 修正普攻参数传错
# @change: "2010-11-22 12:15" Alee 删除异常/沉默判定
#---------------------------------------------------------------------
"""Version = 2010-11-22 12:15"""
#---------------------------------------------------------------------
import IPY_GameWorld
import GameWorld
import NPCCommon
import BaseAttack
# @todo:副本活动机器人
# @author hxp
# @date 2018-12-06
# @version 1.0
#
# 详细描述: 副本活动机器人
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-06 20:00"""
#-------------------------------------------------------------------------------
import ChConfig
import AICommon
import NPCCommon
import BaseAttack
import IpyGameDataPY
import IPY_GameWorld
import GameWorld
import FBCommon
import GameObj
import random
#---------------------------------------------------------------------
#---------------------------------------------------------------------
## 初始化
#  @param curNPC NPC实例
#  @param curNPC 当前npc
#  @return None
#  @remarks 函数详细说明.
def DoInit(curNPC):
    curNPC.GetNPCAngry().Init(ChConfig.Def_BossAngryCount)
    curNPC.GetNPCAngry().Init(ChConfig.Def_SuperFBBossAngryCount)
    return
#---------------------------------------------------------------------
##正常AI逻辑处理
#@param curNPC NPC实例
#@param tick 时间戳
#@return 返回值无意义
#@remarks 正常AI逻辑处理
def OnNPCReborn(curNPC):
    curNPC.SetIsNeedProcess(True)
    return
## 执行AI
#  @param curNPC 当前npc
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def ProcessAI(curNPC, tick):
    npcControl = NPCCommon.NPCControl(curNPC)
    if curNPC.GetCurAction() == IPY_GameWorld.laNPCDie or not curNPC.IsAlive():
        #NPC死亡, 进入死亡倒计时
        if npcControl.DieTick(tick) == 0:
            return
    #刷新自己的buff
    npcControl.RefreshBuffState(tick)
    if GameObj.GetHP(curNPC) == 0 :
        # BUFF刷新中可能会导致NPC死亡
        return
    
#    #判断异常状态
#    if curNPC.GetAbnormalState() == IPY_GameWorld.sctFaint:
#        return
    curNPCAction = curNPC.GetCurAction()
    #NPC快速奔跑中, 处理
    if curNPCAction == IPY_GameWorld.laNPCMove and curNPC.GetCurMoveType() == IPY_GameWorld.mtRun :
        AICommon.NormalNPCFast_Move(curNPC , tick)
    #刷新自己的buff
    npcControl.RefreshBuffState(tick)
    if GameObj.GetHP(curNPC) == 0:
        # BUFF刷新中可能会导致NPC死亡
        return
    
    #刷新自己仇恨度列表
@@ -65,35 +62,170 @@
    curNPCAngry = npcControl.GetMaxAngryTag()
    
    #仇恨度列表中的人为空
    if curNPCAngry == None :
        AICommon.NormalNPCFree_Move(curNPC , tick)
    if curNPCAngry == None:
        if curNPC.GetSpeed() != 0:
            __RobotMove(curNPC)
        return
    
    #仇恨对象类型,仇恨对象ID
    curNPCAngryType = curNPCAngry.GetObjType()
    curNPCAngryID = curNPCAngry.GetObjID()
    #战斗中回血
    npcControl.ProcessBattleHPRestore(tick)
    #执行攻击逻辑
    __NPCFight(curNPC, curNPCAngryID, curNPCAngryType, tick)
    return
def __RobotMove(curNPC):
    if curNPC.GetCurAction() == IPY_GameWorld.laNPCMove:
        #GameWorld.DebugLog("移动中不处理!(%s,%s)" % (curNPC.GetPosX(), curNPC.GetPosY()))
        return
    mapID = GameWorld.GetMap().GetMapID()
    lineID = FBCommon.GetFBPropertyMark()
    dataMapID = FBCommon.GetRecordMapID(mapID)
    fbRandMovePosDict = IpyGameDataPY.GetFuncEvalCfg("AI198Point", 2, {})
    if str(dataMapID) in fbRandMovePosDict:
        __RobotMove2(curNPC, fbRandMovePosDict[str(dataMapID)])
        return
    posKey = "%d%02d" % (mapID, lineID)
    fbMovePosDict = IpyGameDataPY.GetFuncCfg("AI198Point", 1)
    if posKey not in fbMovePosDict:
        posKey = "%d%02d" % (mapID, 0)
        if posKey not in fbMovePosDict:
            return
    posList = fbMovePosDict[posKey]
    Key_PosIndex = "RobotMovePosIndex" # NPC上次移动的坐标索引,存值+1
    posIndex =  curNPC.GetDictByKey(Key_PosIndex) - 1
    if posIndex < 0:
        # 还没有走过点的,寻找所有点中最近的点
        posIndex = random.randint(0, len(posList) - 1)
    else:
        tagPosX, tagPosY = posList[posIndex]
        tagDist = GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(), tagPosX, tagPosY)
        if tagDist < 2:
            posIndex += 1
    if posIndex < 0 or posIndex >= len(posList):
        posIndex = random.randint(0, len(posList) - 1)
    curNPC.SetDict(Key_PosIndex, posIndex + 1)
    tagPosX, tagPosY = posList[posIndex]
    curNPC.Move(tagPosX, tagPosY)
    return
def __RobotMove2(curNPC, randPosList):
    ''' 根据多条路径随机移动,不同路径间如果存在交叉点,那么可能随机改变路径
    @param randPosList: [[[路径A点1x,y],[路径A点2x,y],...], [[路径B点1x,y],[路径B点2x,y],...], ...]
    '''
    if not randPosList:
        return
    #objID = curNPC.GetID()
    #npcID = curNPC.GetNPCID()
    RobotMoveIndexInfo = "RobotMoveIndexInfo" # 上次移动目标坐标点索引信息  i * 100 + j
    RobotMovePosInfo = "RobotMovePosInfo" # 上次移动目标坐标点 posX * 10000 + posY
    indexInfo =  curNPC.GetDictByKey(RobotMoveIndexInfo)
    posInfo =  curNPC.GetDictByKey(RobotMovePosInfo)
    tagI, tagJ = indexInfo / 100, indexInfo % 100
    tagPosX, tagPosY = posInfo / 10000, posInfo % 10000
    curPosX, curPosY = curNPC.GetPosX(), curNPC.GetPosY()
    resetPosPath = True # 重置坐标路径,为True时重新搜索路径
    # 已经有目标点
    if tagPosX and tagPosY:
        # 理论上配置没重读
        if tagI < len(randPosList) and type(randPosList[tagI]) in [list, tuple] and tagJ < len(randPosList[tagI]) \
            and randPosList[tagI][tagJ] == [tagPosX, tagPosY]:
            resetPosPath = False
    #GameWorld.DebugLog("__RobotMove2: objID=%s,npcID=%s,curPos(%s,%s),tagPos(%s,%s),tagIndex(%s,%s)"
    #                   % (objID, npcID, curPosX, curPosY, tagPosX, tagPosY, tagI, tagJ))
    #GameWorld.DebugLog("    resetPosPath=%s,randPosList=%s" % (resetPosPath, randPosList))
    # 重置路径,先走向最近的点
    if resetPosPath:
        nearestDist = 99999999
        for i, pathList in enumerate(randPosList):
            for j, pos in enumerate(pathList):
                posX, posY = pos
                tagDist = GameWorld.GetDist(curPosX, curPosY, posX, posY)
                if tagDist < nearestDist:
                    nearestDist = tagDist
                    tagI, tagJ = i, j
                    tagPosX, tagPosY = posX, posY
        #GameWorld.DebugLog("    nearestDist=%s" % nearestDist)
    else:
        tagDist = GameWorld.GetDist(curPosX, curPosY, tagPosX, tagPosY)
        #GameWorld.DebugLog("    tagDist=%s" % (tagDist))
        # 快到达目标点了,预先寻找下个点
        if tagDist < 2:
            nearPosIndexList = __getNearPosIndexList(randPosList, tagI, tagJ)
            #GameWorld.DebugLog("    nearPosIndexList=%s" % nearPosIndexList)
            if nearPosIndexList:
                tagI, tagJ = random.choice(nearPosIndexList)
                tagPosX, tagPosY = randPosList[tagI][tagJ]
    #GameWorld.DebugLog("    tagPos(%s,%s),tagIndex(%s,%s)" % (tagPosX, tagPosY, tagI, tagJ))
    curNPC.SetDict(RobotMoveIndexInfo, tagI * 100 + tagJ)
    curNPC.SetDict(RobotMovePosInfo, tagPosX * 10000 + tagPosY)
    curNPC.Move(tagPosX, tagPosY)
    return
def __getNearPosIndexList(randPosList, tagI, tagJ):
    ## 获取多条路径上相邻的点
    pathPosList = randPosList[tagI]
    if not pathPosList or len(pathPosList) <= 1:
        return
    tagPosX, tagPosY = randPosList[tagI][tagJ]
    nearPosIndexList = __getNearIndexByPath(pathPosList, tagI, tagJ)
    # 检查其他路径交叉点
    for i, pathList in enumerate(randPosList):
        if i == tagI:
            # 本路径不处理
            continue
        for j, pos in enumerate(pathList):
            posX, posY = pos
            if posX == tagPosX and posY == tagPosY:
                for ni, nj in __getNearIndexByPath(pathList, i, j):
                    if [ni, nj] not in nearPosIndexList:
                        nearPosIndexList.append([ni, nj])
    return nearPosIndexList
def __getNearIndexByPath(pathPosList, tagI, tagJ):
    ## 获取某条路径上的相邻点索引信息
    nearPosIndexList = []
    # 起始点 或 最终点
    if tagJ == 0 or tagJ == len(pathPosList) - 1:
        # 起点=终点
        if pathPosList[0] == pathPosList[-1]:
            nearPosIndexList.append([tagI, 1])
            nearPosIndexList.append([tagI, -2])
        # 起点
        elif tagJ == 0:
            nearPosIndexList.append([tagI, 1])
        else:
            nearPosIndexList.append([tagI, -2])
    # 中间点,取前后两点
    else:
        nearPosIndexList.append([tagI, tagJ - 1])
        nearPosIndexList.append([tagI, tagJ + 1])
    return nearPosIndexList
#---------------------------------------------------------------------
## npc攻击战斗
## npc攻击逻辑
#  @param curNPC 当前npc
#  @param tagID curNPCAngryID
#  @param tagType curNPCAngryType 
#  @param tick 当前时间
#  @return None
#  @remarks 战斗逻辑实现
#  @remarks 函数详细说明.
def __NPCFight(curNPC, tagID, tagType, tick):
    #设置进入战斗状态
    NPCCommon.SetNPCInBattleState(curNPC)
    npcControl = NPCCommon.NPCControl(curNPC)
    #NPC的打怪AI
    if not npcControl.IsInRefreshArea():
        #追击返回
        npcControl.MoveBack()
        return
    
    #开始攻击
    curTag = GameWorld.GetObj(tagID, tagType)
@@ -103,10 +235,37 @@
    
    tagDist = GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(), curTag.GetPosX(), curTag.GetPosY())
    
    if tagDist > curNPC.GetAtkDist() :
        npcControl.MoveToObj_Detel(curTag)
    #---优先释放技能---
    if AICommon.DoAutoUseSkill(curNPC, curTag, tagDist, tick):
        return
    
    #---释放普通攻击---
    if curNPC.GetSpeed() == 0:
        # 不可移动NPC
        if tagDist > curNPC.GetAtkDist():
            return
        if tick - curNPC.GetAttackTick() < curNPC.GetAtkInterval():
            #攻击间隔没有到, 返回
            return
        #普通攻击
        BaseAttack.Attack(curNPC, curTag, None, tick)
        return
    #超过攻击距离,移动过去
    if tagDist > curNPC.GetAtkDist():
        destDist = GameWorld.GetDist(curNPC.GetDestPosX() , curNPC.GetDestPosY(), curTag.GetPosX(), curTag.GetPosY())
        if destDist <= curNPC.GetAtkDist() and curNPC.GetCurAction() == IPY_GameWorld.laNPCMove:
            # 目标在移动的攻击范围内,不改变目标点
            return
        npcControl.MoveToObj_Detel(curTag)
        return
    else:
        curNPC.StopMove()
    if tick - curNPC.GetAttackTick() < curNPC.GetAtkInterval():
        #攻击间隔没有到, 返回
        return
@@ -118,7 +277,13 @@
    #普通攻击
    BaseAttack.Attack(curNPC, curTag, None, tick)
    return
## NPC死亡
#  @param curNPC 当前npc
#  @param hurtType 伤害者的obj类型
#  @param hurtID 伤害者的objID
#  @return None
def OnDie(curNPC, hurtType, hurtID):
    AICommon.DoNPCUseSkillOnDie(curNPC)
    return