Package l2p.gameserver.ai

Source Code of l2p.gameserver.ai.L2PlayableAI

package l2p.gameserver.ai;

import static l2p.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static l2p.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static l2p.gameserver.ai.CtrlIntention.AI_INTENTION_CAST;
import static l2p.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static l2p.gameserver.ai.CtrlIntention.AI_INTENTION_INTERACT;
import static l2p.gameserver.ai.CtrlIntention.AI_INTENTION_PICK_UP;

import java.util.concurrent.ScheduledFuture;

import l2p.common.ThreadPoolManager;
import l2p.gameserver.cache.Msg;
import l2p.gameserver.geodata.GeoEngine;
import l2p.gameserver.model.L2Character;
import l2p.gameserver.model.L2Object;
import l2p.gameserver.model.L2Playable;
import l2p.gameserver.model.L2Player;
import l2p.gameserver.model.L2Skill;
import l2p.gameserver.model.L2Skill.NextAction;
import l2p.gameserver.model.L2Skill.SkillType;
import l2p.gameserver.model.L2Summon;
import l2p.gameserver.serverpackets.MyTargetSelected;
import l2p.util.Location;

public class L2PlayableAI extends L2CharacterAI
{
  private boolean thinking = false; // to prevent recursive thinking
  private Object _intention_arg0 = null, _intention_arg1 = null;
  private L2Skill _skill;
  private nextAction _nextAction;
  private Object _nextAction_arg0;
  private Object _nextAction_arg1;
  private boolean _nextAction_arg2;
  private boolean _nextAction_arg3;
  private boolean _forceUse;
  private boolean _dontMove;
  private ScheduledFuture<?> _followTask;

  public L2PlayableAI(L2Playable actor)
  {
    super(actor);
  }

  public enum nextAction
  {
    ATTACK,
    CAST,
    MOVE,
    REST,
    PICKUP,
    INTERACT
  }

  @Override
  public void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
  {
    super.changeIntention(intention, arg0, arg1);
    _intention_arg0 = arg0;
    _intention_arg1 = arg1;
  }

  @Override
  public void setIntention(CtrlIntention intention, Object arg0, Object arg1)
  {
    _intention_arg0 = null;
    _intention_arg1 = null;
    super.setIntention(intention, arg0, arg1);
  }

  @Override
  protected void onIntentionCast(L2Skill skill, L2Character target)
  {
    _skill = skill;
    super.onIntentionCast(skill, target);
  }

  @Override
  public void setNextAction(nextAction action, Object arg0, Object arg1, boolean arg2, boolean arg3)
  {
    _nextAction = action;
    _nextAction_arg0 = arg0;
    _nextAction_arg1 = arg1;
    _nextAction_arg2 = arg2;
    _nextAction_arg3 = arg3;
  }

  public boolean setNextIntention()
  {
    nextAction nextAction = _nextAction;
    Object nextAction_arg0 = _nextAction_arg0;
    Object nextAction_arg1 = _nextAction_arg1;
    boolean nextAction_arg2 = _nextAction_arg2;
    boolean nextAction_arg3 = _nextAction_arg3;
    L2Playable actor = getActor();
    if(nextAction == null || actor == null)
    {
      return false;
    }
    L2Skill skill;
    L2Character target;
    L2Object object;
    switch(nextAction)
    {
      case ATTACK:
        if(nextAction_arg0 == null)
        {
          return false;
        }
        target = (L2Character) nextAction_arg0;
        _forceUse = nextAction_arg2;
        _dontMove = nextAction_arg3;
        clearNextAction();
        setIntention(AI_INTENTION_ATTACK, target);
        break;
      case CAST:
        if(nextAction_arg0 == null || nextAction_arg1 == null)
        {
          return false;
        }
        skill = (L2Skill) nextAction_arg0;
        target = (L2Character) nextAction_arg1;
        _forceUse = nextAction_arg2;
        _dontMove = nextAction_arg3;
        clearNextAction();
        if(!skill.checkCondition(actor, target, _forceUse, _dontMove, true))
        {
          if(skill.getNextAction() == NextAction.ATTACK && !actor.equals(target))
          {
            setNextAction(l2p.gameserver.ai.L2PlayableAI.nextAction.ATTACK, target, null, _forceUse, false);
            return setNextIntention();
          }
          return false;
        }
        setIntention(AI_INTENTION_CAST, skill, target);
        break;
      case MOVE:
        if(nextAction_arg0 == null || nextAction_arg1 == null)
        {
          return false;
        }
        Location loc = (Location) nextAction_arg0;
        Integer offset = (Integer) nextAction_arg1;
        clearNextAction();
        actor.moveToLocation(loc, offset, nextAction_arg2);
        break;
      case REST:
        actor.sitDown();
        break;
      case INTERACT:
        if(nextAction_arg0 == null)
        {
          return false;
        }
        object = (L2Object) nextAction_arg0;
        clearNextAction();
        onIntentionInteract(object);
        break;
      case PICKUP:
        if(nextAction_arg0 == null)
        {
          return false;
        }
        object = (L2Object) nextAction_arg0;
        clearNextAction();
        onIntentionPickUp(object);
        break;
      default:
        return false;
    }
    return true;
  }

  @Override
  public void clearNextAction()
  {
    _nextAction = null;
    _nextAction_arg0 = null;
    _nextAction_arg1 = null;
    _nextAction_arg2 = false;
    _nextAction_arg3 = false;
  }

  @Override
  protected void onEvtFinishCasting()
  {
    if(!setNextIntention())
    {
      setIntention(AI_INTENTION_ACTIVE);
    }
  }

  @Override
  protected void onEvtReadyToAct()
  {
    if(!setNextIntention())
    {
      onEvtThink();
    }
  }

  @Override
  protected void onEvtArrived()
  {
    if(!setNextIntention())
    {
      if(getIntention() == AI_INTENTION_INTERACT || getIntention() == AI_INTENTION_PICK_UP)
      {
        onEvtThink();
      }
      else
      {
        changeIntention(AI_INTENTION_ACTIVE, null, null);
      }
    }
  }

  @Override
  protected void onEvtArrivedTarget()
  {
    switch(getIntention())
    {
      case AI_INTENTION_ATTACK:
        thinkAttack(false);
        break;
      case AI_INTENTION_CAST:
        thinkCast(false);
        break;
      case AI_INTENTION_FOLLOW:
        if(_followTask != null)
        {
          _followTask.cancel(false);
        }
        _followTask = ThreadPoolManager.getInstance().scheduleMove(new ThinkFollow(), 1000);
        break;
    }
  }

  @Override
  protected void onEvtThink()
  {
    L2Playable actor = getActor();
    if(actor == null || thinking || actor.isActionsDisabled())
    {
      return;
    }
    thinking = true;
    try
    {
      switch(getIntention())
      {
        case AI_INTENTION_ATTACK:
          thinkAttack(true);
          break;
        case AI_INTENTION_CAST:
          thinkCast(true);
          break;
        case AI_INTENTION_PICK_UP:
          thinkPickUp();
          break;
        case AI_INTENTION_INTERACT:
          thinkInteract();
          break;
      }
    }
    catch(Exception e)
    {
      _log.warning("Exception onEvtThink(): " + e);
      e.printStackTrace();
    }
    finally
    {
      thinking = false;
    }
  }

  public class ThinkFollow implements Runnable
  {
    public L2Playable getActor()
    {
      return L2PlayableAI.this.getActor();
    }

    public void run()
    {
      _followTask = null;
      L2Playable actor = getActor();
      if(actor == null)
      {
        return;
      }
      if(getIntention() != AI_INTENTION_FOLLOW)
      {
        // Если пет прекратил преследование, меняем статус, чтобы не пришлось щелкать на кнопку следования 2 раза.
        if((actor.isPet() || actor.isSummon()) && getIntention() == AI_INTENTION_ACTIVE)
        {
          actor.setFollowStatus(false, false);
        }
        return;
      }
      L2Character target = (L2Character) _intention_arg0;
      Integer offset = (Integer) _intention_arg1;
      if(target == null || target.isAlikeDead() || actor.getDistance(target) > 4000)
      {
        setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
        return;
      }
      L2Player actor_player = actor.getPlayer();
      if(actor_player == null || !actor_player.isConnected() || (actor.isPet() || actor.isSummon()) && actor_player.getPet() != actor)
      {
        setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
        return;
      }
      if(!actor.isInRange(target, offset + 20) && (!actor.isFollow || actor.getFollowTarget() != target))
      {
        actor.followToCharacter(target, offset, false);
      }
      _followTask = ThreadPoolManager.getInstance().scheduleMove(this, 1000);
    }
  }

  @Override
  protected void onIntentionInteract(L2Object object)
  {
    L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    if(actor.isActionsDisabled())
    {
      setNextAction(nextAction.INTERACT, object, null, false, false);
      clientActionFailed();
      return;
    }
    clearNextAction();
    changeIntention(AI_INTENTION_INTERACT, object, null);
    onEvtThink();
  }

  protected void thinkInteract()
  {
    L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    L2Object target = (L2Object) _intention_arg0;
    if(target == null)
    {
      setIntention(AI_INTENTION_ACTIVE);
      return;
    }
    int range = (int) (Math.max(30, actor.getMinDistance(target)) + 20);
    if(actor.isInRangeZ(target, range))
    {
      if(actor.isPlayer())
      {
        ((L2Player) actor).doInteract(target);
      }
      setIntention(AI_INTENTION_ACTIVE);
    }
    else
    {
      actor.moveToLocation(target.getLoc(), 40, true);
      setNextAction(nextAction.INTERACT, target, null, false, false);
    }
  }

  @Override
  protected void onIntentionPickUp(L2Object object)
  {
    L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    if(actor.isActionsDisabled())
    {
      setNextAction(nextAction.PICKUP, object, null, false, false);
      clientActionFailed();
      return;
    }
    clearNextAction();
    changeIntention(AI_INTENTION_PICK_UP, object, null);
    onEvtThink();
  }

  protected void thinkPickUp()
  {
    final L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    final L2Object target = (L2Object) _intention_arg0;
    if(target == null)
    {
      setIntention(AI_INTENTION_ACTIVE);
      return;
    }
    if(actor.isInRange(target, 30) && Math.abs(actor.getZ() - target.getZ()) < 50)
    {
      if(actor.isPlayer() || actor.isPet())
      {
        actor.doPickupItem(target);
      }
      setIntention(AI_INTENTION_ACTIVE);
    }
    else
    {
      ThreadPoolManager.getInstance().executePathfind(new Runnable()
      {
        public void run()
        {
          actor.moveToLocation(target.getLoc(), 10, true);
          setNextAction(nextAction.PICKUP, target, null, false, false);
        }
      });
    }
  }

  protected void thinkAttack(boolean checkRange)
  {
    L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    L2Player player = actor.getPlayer();
    if(player == null)
    {
      onAttackFail();
      return;
    }
    if(actor.isActionsDisabled() || actor.isAttackingDisabled())
    {
      actor.sendActionFailed();
      return;
    }
    boolean isPosessed = actor instanceof L2Summon && ((L2Summon) actor).isPosessed();
    L2Character attack_target = getAttackTarget();
    if(attack_target == null || attack_target.isDead() || !isPosessed && !(_forceUse ? attack_target.isAttackable(player) : attack_target.isAutoAttackable(player)))
    {
      onAttackFail();
      actor.sendActionFailed();
      return;
    }
    if(!checkRange)
    {
      clientStopMoving();
      actor.doAttack(attack_target);
      return;
    }
    int range = actor.getPhysicalAttackRange();
    if(range < 10)
    {
      range = 10;
    }
    boolean canSee = GeoEngine.canSeeTarget(actor, attack_target, false);
    if(!canSee && (range > 200 || Math.abs(actor.getZ() - attack_target.getZ()) > 200))
    {
      actor.sendPacket(Msg.CANNOT_SEE_TARGET);
      onAttackFail();
      actor.sendActionFailed();
      return;
    }
    range += actor.getMinDistance(attack_target);
    if(actor.isFakeDeath())
    {
      actor.breakFakeDeath();
    }
    if(actor.isInRangeZ(attack_target, range))
    {
      if(!canSee)
      {
        actor.sendPacket(Msg.CANNOT_SEE_TARGET);
        onAttackFail();
        actor.sendActionFailed();
        return;
      }
      clientStopMoving(false);
      actor.doAttack(attack_target);
    }
    else if(!_dontMove)
    {
      ThreadPoolManager.getInstance().executePathfind(new ExecuteFollow(actor, attack_target, range - 20));
    }
    else
    {
      actor.sendActionFailed();
    }
  }

  /**
   * Нужен для оверрайда саммоном, чтобы он не прекращал фоллов.
   */
  protected void onAttackFail()
  {
    setIntention(AI_INTENTION_ACTIVE);
  }

  protected void thinkCast(boolean checkRange)
  {
    L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    L2Character attack_target = getAttackTarget();
    if(_skill.getSkillType() == SkillType.CRAFT || _skill.isToggle())
    {
      if(_skill.checkCondition(actor, attack_target, _forceUse, _dontMove, true))
      {
        actor.doCast(_skill, attack_target, _forceUse);
      }
      return;
    }
    if(attack_target == null || attack_target.isDead() != _skill.getCorpse() && !_skill.isNotTargetAoE())
    {
      setIntention(AI_INTENTION_ACTIVE);
      actor.sendActionFailed();
      return;
    }
    if(!checkRange)
    {
      // Если скилл имеет следующее действие, назначим это действие после окончания действия скилла
      if(_skill.getNextAction() == NextAction.ATTACK && !actor.equals(attack_target))
      {
        setNextAction(nextAction.ATTACK, attack_target, null, _forceUse, false);
      }
      else
      {
        clearNextAction();
      }
      clientStopMoving();
      if(_skill.checkCondition(actor, attack_target, _forceUse, _dontMove, true))
      {
        actor.doCast(_skill, attack_target, _forceUse);
      }
      else
      {
        setNextIntention();
        if(getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
        {
          thinkAttack(true);
        }
      }
      return;
    }
    int range = actor.getMagicalAttackRange(_skill);
    if(range < 10)
    {
      range = 10;
    }
    boolean canSee = _skill.getSkillType() == SkillType.TAKECASTLE || _skill.getSkillType() == SkillType.TAKEFORTRESS || GeoEngine.canSeeTarget(actor, attack_target, actor.isFlying());
    boolean noRangeSkill = _skill.getCastRange() == 32767;
    if(!noRangeSkill && !canSee && (range > 200 || Math.abs(actor.getZ() - attack_target.getZ()) > 200))
    {
      actor.sendPacket(Msg.CANNOT_SEE_TARGET);
      setIntention(AI_INTENTION_ACTIVE);
      actor.sendActionFailed();
      return;
    }
    range += actor.getMinDistance(attack_target);
    if(actor.isFakeDeath())
    {
      actor.breakFakeDeath();
    }
    if(actor.isInRangeZ(attack_target, range) || noRangeSkill)
    {
      if(!noRangeSkill && !canSee)
      {
        actor.sendPacket(Msg.CANNOT_SEE_TARGET);
        setIntention(AI_INTENTION_ACTIVE);
        actor.sendActionFailed();
        return;
      }
      // Если скилл имеет следующее действие, назначим это действие после окончания действия скилла
      if(_skill.getNextAction() == NextAction.ATTACK && !actor.equals(attack_target))
      {
        setNextAction(nextAction.ATTACK, attack_target, null, _forceUse, false);
      }
      else
      {
        clearNextAction();
      }
      if(_skill.checkCondition(actor, attack_target, _forceUse, _dontMove, true))
      {
        clientStopMoving(false);
        actor.doCast(_skill, attack_target, _forceUse);
      }
      else
      {
        setNextIntention();
        if(getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
        {
          thinkAttack(true);
        }
      }
    }
    else if(!_dontMove)
    {
      ThreadPoolManager.getInstance().executePathfind(new ExecuteFollow(actor, attack_target, range - 20));
    }
    else
    {
      actor.sendPacket(Msg.YOUR_TARGET_IS_OUT_OF_RANGE);
      setIntention(AI_INTENTION_ACTIVE);
      actor.sendActionFailed();
    }
  }

  @Override
  protected void onEvtDead(L2Character killer)
  {
    clearNextAction();
    L2Playable actor = getActor();
    if(actor != null)
    {
      actor.clearHateList(true);
    }
    super.onEvtDead(killer);
  }

  @Override
  protected void onEvtFakeDeath()
  {
    clearNextAction();
    super.onEvtFakeDeath();
  }

  public void lockTarget(L2Character target)
  {
    L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    if(target == null)
    {
      actor.setAggressionTarget(null);
    }
    else if(actor.getAggressionTarget() == null)
    {
      L2Object actorStoredTarget = actor.getTarget();
      actor.setAggressionTarget(target);
      actor.setTarget(target);
      clearNextAction();
      switch(getIntention())
      {
        case AI_INTENTION_ATTACK:
          setAttackTarget(target);
          break;
        case AI_INTENTION_CAST:
          L2Skill skill = actor.getCastingSkill();
          if(skill == null)
          {
            skill = _skill;
          }
          if(skill != null && !skill.isUsingWhileCasting())
          {
            switch(skill.getTargetType())
            {
              case TARGET_ONE:
              case TARGET_AREA:
              case TARGET_MULTIFACE:
              case TARGET_TUNNEL:
                setAttackTarget(target);
                actor.setCastingTarget(target);
                break;
            }
          }
          break;
      }
      if(actorStoredTarget != target)
      {
        actor.sendPacket(new MyTargetSelected(target.getObjectId(), 0));
      }
    }
  }

  @Override
  public void Attack(L2Object target, boolean forceUse, boolean dontMove)
  {
    L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    if(target.isCharacter() && (actor.isActionsDisabled() || actor.isAttackingDisabled()))
    {
      // Если не можем атаковать, то атаковать позже
      setNextAction(nextAction.ATTACK, target, null, forceUse, false);
      actor.sendActionFailed();
      return;
    }
    _dontMove = dontMove;
    _forceUse = forceUse;
    clearNextAction();
    setIntention(AI_INTENTION_ATTACK, target);
  }

  @Override
  public void Cast(L2Skill skill, L2Character target, boolean forceUse, boolean dontMove)
  {
    L2Playable actor = getActor();
    if(actor == null)
    {
      return;
    }
    // Если скилл альтернативного типа (например, бутылка на хп),
    // то он может использоваться во время каста других скиллов, или во время атаки, или на бегу.
    // Поэтому пропускаем дополнительные проверки.
    if(skill.altUse() || skill.isToggle())
    {
      if(skill.isToggle() && actor.isToggleDisabled() || skill.isHandler() && actor.isPotionsDisabled())
      {
        clientActionFailed();
      }
      else
      {
        actor.altUseSkill(skill, target);
      }
      return;
    }
    // Если не можем кастовать, то использовать скилл позже
    if(actor.isActionsDisabled())
    {
      //if(!actor.isSkillDisabled(skill.getId()))
      setNextAction(nextAction.CAST, skill, target, forceUse, dontMove);
      clientActionFailed();
      return;
    }
    //_actor.stopMove(null);
    _forceUse = forceUse;
    _dontMove = dontMove;
    clearNextAction();
    setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target);
  }

  public static class ExecuteFollow implements Runnable
  {
    private L2Playable _player;
    private L2Character _target;
    private int _range;

    public ExecuteFollow(L2Playable player, L2Character target, int range)
    {
      _player = player;
      _target = target;
      _range = range;
    }

    public void run()
    {
      _player.followToCharacter(_target, _range, true);
    }
  }

  @Override
  public L2Playable getActor()
  {
    return (L2Playable) super.getActor();
  }
}
TOP

Related Classes of l2p.gameserver.ai.L2PlayableAI

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.