Package l2p.gameserver.model.instances

Source Code of l2p.gameserver.model.instances.L2TamedBeastInstance

package l2p.gameserver.model.instances;

import java.util.HashMap;
import java.util.concurrent.Future;

import l2p.Config;
import l2p.common.ThreadPoolManager;
import l2p.gameserver.ai.CtrlIntention;
import l2p.gameserver.model.L2Character;
import l2p.gameserver.model.L2Object;
import l2p.gameserver.model.L2ObjectsStorage;
import l2p.gameserver.model.L2Player;
import l2p.gameserver.model.L2Skill;
import l2p.gameserver.model.L2World;
import l2p.gameserver.model.items.L2ItemInstance;
import l2p.gameserver.serverpackets.NpcInfo;
import l2p.gameserver.tables.SkillTable;
import l2p.gameserver.templates.L2NpcTemplate;
import l2p.util.GArray;
import l2p.util.Location;
import l2p.util.Rnd;

// In addition, it can be fed in order to increase its duration.
// This class handles the running tasks, AI, and feed of the mob.
public final class L2TamedBeastInstance extends L2FeedableBeastInstance
{
  private static final int MAX_DISTANCE_FROM_OWNER = 2000;
  private static final int MAX_DURATION = 1200000; // 20 minutes
  private static final int DURATION_CHECK_INTERVAL = 60000; // 1 minute
  private static final int DURATION_INCREASE_INTERVAL = 20000; // 20 secs
  private static final int BUFF_INTERVAL = 5000; // 5 seconds
  private long ownerStoreId = 0;
  private int _foodSkillId, _remainingTime = MAX_DURATION;
  private Location _homeLoc;
  private Future<?> _buffTask = null, _durationCheckTask = null;

  public L2TamedBeastInstance(int objectId, L2NpcTemplate template)
  {
    super(objectId, template);
    setHome(this);
  }

  public L2TamedBeastInstance(int objectId, L2NpcTemplate template, L2Player owner, int foodSkillId, Location loc)
  {
    super(objectId, template);
    onSpawn();
    setFoodType(foodSkillId);
    setHome(loc);
    setRunning();
    spawnMe(loc);
    setOwner(owner);
  }

  public void onReceiveFood()
  {
    // Eating food extends the duration by 20secs, to a max of 20minutes
    _remainingTime = _remainingTime + DURATION_INCREASE_INTERVAL;
    if(_remainingTime > MAX_DURATION)
    {
      _remainingTime = MAX_DURATION;
    }
  }

  public Location getHome()
  {
    return _homeLoc;
  }

  public void setHome(Location loc)
  {
    _homeLoc = loc;
  }

  public void setHome(L2Character c)
  {
    setHome(c.getLoc());
  }

  public int getRemainingTime()
  {
    return _remainingTime;
  }

  public void setRemainingTime(int duration)
  {
    _remainingTime = duration;
  }

  public int getFoodType()
  {
    return _foodSkillId;
  }

  public void setFoodType(int foodItemId)
  {
    if(foodItemId > 0)
    {
      _foodSkillId = foodItemId;
      // start the duration checks start the buff tasks
      if(_durationCheckTask != null)
      {
        _durationCheckTask.cancel(true);
      }
      _durationCheckTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckDuration(this), DURATION_CHECK_INTERVAL, DURATION_CHECK_INTERVAL);
    }
  }

  @Override
  public void doDie(L2Character killer)
  {
    super.doDie(killer);
    stopMove();
    if(_buffTask != null)
    {
      _buffTask.cancel(true);
      _buffTask = null;
    }
    if(_durationCheckTask != null)
    {
      _durationCheckTask.cancel(true);
      _durationCheckTask = null;
    }
    L2Player owner = getPlayer();
    if(owner != null)
    {
      owner.setTrainedBeast(null);
    }
    _foodSkillId = 0;
    _remainingTime = 0;
  }

  @Override
  public L2Player getPlayer()
  {
    return L2ObjectsStorage.getAsPlayer(ownerStoreId);
  }

  public void setOwner(L2Player owner)
  {
    if(owner != null)
    {
      ownerStoreId = owner.getStoredId();
      setTitle(owner.getName());
      owner.setTrainedBeast(this);
      for(L2Player player : L2World.getAroundPlayers(this))
      {
        if(player != null && _objectId != player.getObjectId())
        {
          player.sendPacket(new NpcInfo(this, player));
        }
      }
      // always and automatically follow the owner.
      getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, owner, Config.FOLLOW_RANGE);
      // instead of calculating this value each time, let's get this now and pass it on
      int totalBuffsAvailable = 0;
      for(L2Skill skill : getTemplate().getSkills().values())
      // if the skill is a buff, check if the owner has it already
      {
        if(skill.getSkillType() == L2Skill.SkillType.BUFF)
        {
          totalBuffsAvailable++;
        }
      }
      // start the buff tasks
      if(_buffTask != null)
      {
        _buffTask.cancel(true);
      }
      _buffTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckOwnerBuffs(this, totalBuffsAvailable), BUFF_INTERVAL, BUFF_INTERVAL);
    }
    else
    {
      doDespawn();
    } // despawn if no owner
  }

  public void doDespawn()
  {
    // stop running tasks
    stopMove();
    _buffTask.cancel(true);
    _durationCheckTask.cancel(true);
    // clean up variables
    L2Player owner = getPlayer();
    if(owner != null)
    {
      owner.setTrainedBeast(null);
    }
    setTarget(null);
    _buffTask = null;
    _durationCheckTask = null;
    _foodSkillId = 0;
    _remainingTime = 0;
    // remove the spawn
    onDecay();
  }

  // notification triggered by the owner when the owner is attacked.
  // tamed mobs will heal/recharge or debuff the enemy according to their skills
  public void onOwnerGotAttacked(L2Character attacker)
  {
    L2Player owner = getPlayer();
    // check if the owner is no longer around...if so, despawn
    if(owner == null || !owner.isOnline())
    {
      doDespawn();
      return;
    }
    // if the owner is too far away, stop anything else and immediately run towards the owner.
    if(!isInRange(owner, MAX_DISTANCE_FROM_OWNER))
    {
      getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, owner, Config.FOLLOW_RANGE);
      return;
    }
    // if the owner is dead, do nothing...
    if(owner.isDead())
    {
      return;
    }
    double HPRatio = owner.getCurrentHpRatio();
    // if the owner has a lot of HP, then debuff the enemy with a random debuff among the available skills
    // use of more than one debuff at this moment is acceptable
    if(HPRatio >= 0.8 && attacker != null)
    {
      HashMap<Integer, L2Skill> skills = getTemplate().getSkills();
      for(L2Skill skill : skills.values())
      // if the skill is a debuff, check if the attacker has it already [ attacker.getEffect(L2Skill skill) ]
      {
        if(skill.isOffensive() && attacker.getEffectList().getEffectsBySkill(skill) == null && Rnd.nextBoolean())
        {
          setTarget(attacker);
          doCast(skill, attacker, true);
        }
      }
    }
    // for HP levels between 80% and 50%, do not react to attack events (so that MP can regenerate a bit)
    // for lower HP ranges, heal or recharge the owner with 1 skill use per attack.
    else if(HPRatio < 0.5)
    {
      int chance = 1;
      if(HPRatio < 0.25)
      {
        chance = 2;
      }
      // if the owner has a lot of HP, then debuff the enemy with a random debuff among the available skills
      HashMap<Integer, L2Skill> skills = getTemplate().getSkills();
      for(L2Skill skill : skills.values())
      {
        if(!skill.isOffensive() && owner.getEffectList().getEffectsBySkill(skill) == null && Rnd.chance(chance * 20))
        {
          setTarget(owner);
          doCast(skill, owner, true);
          return;
        }
      }
    }
    getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, owner, Config.FOLLOW_RANGE);
  }

  private class CheckDuration implements Runnable
  {
    private L2TamedBeastInstance _tamedBeast;

    CheckDuration(L2TamedBeastInstance tamedBeast)
    {
      _tamedBeast = tamedBeast;
    }

    public void run()
    {
      int foodTypeSkillId = _tamedBeast.getFoodType();
      L2Player owner = _tamedBeast.getPlayer();
      _tamedBeast.setRemainingTime(_tamedBeast.getRemainingTime() - DURATION_CHECK_INTERVAL);
      // I tried to avoid this as much as possible...but it seems I can't avoid hardcoding
      // ids further, except by carrying an additional variable just for these two lines...
      // Find which food item needs to be consumed.
      L2ItemInstance item = null;
      if(foodTypeSkillId == 2188)
      {
        item = owner.getInventory().getItemByItemId(6643);
      }
      else if(foodTypeSkillId == 2189)
      {
        item = owner.getInventory().getItemByItemId(6644);
      }
      // if the owner has enough food, call the item handler (use the food and triffer all necessary actions)
      if(item != null && item.getCount() >= 1)
      {
        L2Object oldTarget = owner.getTarget();
        owner.setTarget(_tamedBeast);
        GArray<L2Character> targets = new GArray<L2Character>();
        targets.add(_tamedBeast);
        // emulate a call to the owner using food, but bypass all checks for range, etc
        // this also causes a call to the AI tasks handling feeding, which may call onReceiveFood as required.
        owner.callSkill(SkillTable.getInstance().getInfo(foodTypeSkillId, 1), targets, true);
        owner.setTarget(oldTarget);
      }
      else // if the owner has no food, the beast immediately despawns, except when it was only
        // newly spawned. Newly spawned beasts can last up to 5 minutes
        if(_tamedBeast.getRemainingTime() < MAX_DURATION - 300000)
        {
          _tamedBeast.setRemainingTime(-1);
        }
      if(_tamedBeast.getRemainingTime() <= 0)
      {
        _tamedBeast.doDespawn();
      }
    }
  }

  private class CheckOwnerBuffs implements Runnable
  {
    private L2TamedBeastInstance _tamedBeast;
    private int _numBuffs;

    CheckOwnerBuffs(L2TamedBeastInstance tamedBeast, int numBuffs)
    {
      _tamedBeast = tamedBeast;
      _numBuffs = numBuffs;
    }

    public void run()
    {
      L2Player owner = _tamedBeast.getPlayer();
      // check if the owner is no longer around...if so, despawn
      if(owner == null || !owner.isOnline())
      {
        doDespawn();
        return;
      }
      setRunning();
      // if the owner is too far away, stop anything else and immediately run towards the owner.
      if(!isInRange(owner, MAX_DISTANCE_FROM_OWNER))
      {
        getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _tamedBeast.getPlayer(), Config.FOLLOW_RANGE);
        return;
      }
      // if the owner is dead, do nothing...
      if(owner.isDead())
      {
        return;
      }
      int totalBuffsOnOwner = 0;
      int i = 0;
      int rand = Rnd.get(_numBuffs);
      L2Skill buffToGive = null;
      HashMap<Integer, L2Skill> skills = _tamedBeast.getTemplate().getSkills();
      for(L2Skill skill : skills.values())
      // if the skill is a buff, check if the owner has it already
      {
        if(skill.getSkillType() == L2Skill.SkillType.BUFF)
        {
          if(i == rand)
          {
            buffToGive = skill;
          }
          i++;
          if(owner.getEffectList().getEffectsBySkill(skill) != null)
          {
            totalBuffsOnOwner++;
          }
        }
      }
      // if the owner has less than 60% of this beast's available buff, cast a random buff
      if(_numBuffs * 2 / 3 > totalBuffsOnOwner)
      {
        _tamedBeast.setTarget(owner);
        _tamedBeast.doCast(buffToGive, owner, true);
      }
      getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _tamedBeast.getPlayer(), Config.FOLLOW_RANGE);
    }
  }
}
TOP

Related Classes of l2p.gameserver.model.instances.L2TamedBeastInstance

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.