Рейтинг темы:
  • 0 Голос(ов) - 0 в среднем
  • 1
  • 2
  • 3
  • 4
  • 5
Не могу подцепить AI гварду
#1
Создал гварда по аналогии с L2GuardInstance. Имеется три класа (GuardLevelInstance, GuardLevelKnownlist, GuardLevelAI) - все сделано как в классах L2GuardInstance, GuardKnownList, L2AttackableAI - соответственно, за исключением удаления/редактирования некоторых полей и изменения подключаеммых пакетов. Также создал в базе тип "Type" "GuardLevel".
Когда включаю гварда без GuardLevelAI сервер грузится, гвард переходит из режима IDLE в ACTIVE, тип гварда определяется как GuardLevel. Но стоит только подключить AI в GuardLevelInstance
Код:
    @Override
    public L2CharacterAI getAI() {
        L2CharacterAI ai = _ai; // copy handle
        if (ai == null) {
            synchronized (this) {
                if (_ai == null)
                    _ai = new GuardLevelAI(new AIAccessor());
                return _ai;
            }
        }
        return ai;
    }
как появляется такая ошибка
[SPOILER="
Открыть спойлер

Подскажите в чем может быть проблемма? Где этот Spawn прописать?

L2Jserver CT2.3
Ответ
#2
у вас в ошибке пишется что не может преобразовать в L2AttackableAI, у вас самого АИ родитель не тот, нужно унаследовать от L2AttackableAI.
Ответ
#3
Mifesto Написал:у вас в ошибке пишется что не может преобразовать в L2AttackableAI, у вас самого АИ родитель не тот, нужно унаследовать от L2AttackableAI.

Хорошо, спасибо, ппопробую, но почему же обычные гварды нормально работают. У них этот же суперкласс.

Добавлено через 1 час 2 минуты
Если не сложно, то посмотрите эти коды. Я пробовал изменять родительский клас, но результат тот же.

GuardLevelInstance

Код:
/*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.l2j.gameserver.model.actor.instance;

import java.util.logging.Logger;

import net.sf.l2j.Config;
import net.sf.l2j.gameserver.ThreadPoolManager;
import net.sf.l2j.gameserver.ai.CtrlIntention;
import net.sf.l2j.gameserver.ai.GuardLevelAI;
import net.sf.l2j.gameserver.model.L2CharPosition;
import net.sf.l2j.gameserver.model.L2World;
import net.sf.l2j.gameserver.model.L2WorldRegion;
import net.sf.l2j.gameserver.model.actor.L2Attackable;
import net.sf.l2j.gameserver.model.actor.L2Character;
import net.sf.l2j.gameserver.model.actor.L2Summon;
import net.sf.l2j.gameserver.model.actor.knownlist.GuardLevelKnownList;
import net.sf.l2j.gameserver.model.quest.Quest;
import net.sf.l2j.gameserver.network.serverpackets.ActionFailed;
import net.sf.l2j.gameserver.network.serverpackets.MyTargetSelected;
import net.sf.l2j.gameserver.network.serverpackets.SocialAction;
import net.sf.l2j.gameserver.network.serverpackets.ValidateLocation;
import net.sf.l2j.gameserver.templates.chars.L2NpcTemplate;
import net.sf.l2j.util.Rnd;

/**
* This class manages all Guards in the world. It inherits all methods from
* L2Attackable and adds some more such as tracking PK and aggressive
* L2MonsterInstance.<BR>
* <BR>
*
* @version $Revision: 1.11.2.1.2.7 $ $Date: 2005/04/06 16:13:40 $
*/
public final class GuardLevelInstance extends L2Attackable {
    private static Logger _log = Logger.getLogger(GuardLevelInstance.class
            .getName());

    private static final int RETURN_INTERVAL = 60000;

    public class ReturnTask implements Runnable {
        public void run() {
            if (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
                returnHome();
        }
    }

    /**
     * Constructor of GuardLevelInstance (use L2Character and L2NpcInstance
     * constructor).<BR>
     * <BR>
     *
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Call the L2Character constructor to set the _template of the
     * GuardLevelInstance (copy skills from template to object and link
     * _calculators to NPC_STD_CALCULATOR)</li> <li>Set the name of the
     * GuardLevelInstance</li> <li>Create a RandomAnimation Task that will be
     * launched after the calculated delay if the server allow it</li><BR>
     * <BR>
     *
     * @param objectId
     *            Identifier of the object to initialized
     * @param L2NpcTemplate
     *            Template to apply to the NPC
     */
    public GuardLevelInstance(int objectId, L2NpcTemplate template) {
        super(objectId, template);

        ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new ReturnTask(),
                RETURN_INTERVAL, RETURN_INTERVAL + Rnd.nextInt(60000));
    }

    @Override
    public final GuardLevelKnownList getKnownList() {
        return (GuardLevelKnownList) super.getKnownList();
    }

    @Override
    public void initKnownList() {
        setKnownList(new GuardLevelKnownList(this));
    }

    /**
     * Return True if hte attacker is a L2MonsterInstance.<BR>
     * <BR>
     */
    @Override
    public boolean isAutoAttackable(L2Character attacker)
    {
        if (((L2PcInstance) attacker).getLevel()>40)
            return true;
        if (attacker instanceof L2Summon
                && ((L2Summon) attacker).getOwner().getLevel()>40)
            return true;

        else
            return attacker instanceof L2MonsterInstance;
    }

    /**
     * Notify the GuardLevelInstance to return to its home location
     * (AI_INTENTION_MOVE_TO) and clear its _aggroList.<BR>
     * <BR>
     */
    public void returnHome() {
        if (!isInsideRadius(getSpawn().getLocx(), getSpawn().getLocy(), 150,
                false)) {
            clearAggroList();

            getAI().setIntention(
                    CtrlIntention.AI_INTENTION_MOVE_TO,
                    new L2CharPosition(getSpawn().getLocx(), getSpawn()
                            .getLocy(), getSpawn().getLocz(), 0));
        }
    }

    /**
     * Set the home location of its GuardLevelInstance.<BR>
     * <BR>
     */
    @Override
    public void onSpawn() {
        setIsNoRndWalk(true);
        super.onSpawn();

        // check the region where this mob is, do not activate the AI if region
        // is inactive.
        L2WorldRegion region = L2World.getInstance().getRegion(getX(), getY());
        if ((region != null) && (!region.isActive()))
            ((GuardLevelAI) getAI()).stopAITask();
    }

    /**
     * Return the pathfile of the selected HTML file in function of the
     * GuardLevelInstance Identifier and of the page number.<BR>
     * <BR>
     *
     * <B><U> Format of the pathfile </U> :</B><BR>
     * <BR>
     * <li>if page number = 0 : <B>data/html/guard/12006.htm</B> (npcId-page
     * number)</li> <li>if page number > 0 : <B>data/html/guard/12006-1.htm</B>
     * (npcId-page number)</li><BR>
     * <BR>
     *
     * @param npcId
     *            The Identifier of the L2NpcInstance whose text must be display
     * @param val
     *            The number of the page to display
     *
     */
    @Override
    public String getHtmlPath(int npcId, int val) {
        String pom = "";
        if (val == 0) {
            pom = "" + npcId;
        } else {
            pom = npcId + "-" + val;
        }
        return "data/html/guard/" + pom + ".htm";
    }

    /**
     * Manage actions when a player click on the GuardLevelInstance.<BR>
     * <BR>
     *
     * <B><U> Actions on first click on the GuardLevelInstance (Select it)</U>
     * :</B><BR>
     * <BR>
     * <li>Set the GuardLevelInstance as target of the L2PcInstance player (if
     * necessary)</li> <li>Send a Server->Client packet MyTargetSelected to the
     * L2PcInstance player (display the select window)</li> <li>Set the
     * L2PcInstance Intention to AI_INTENTION_IDLE</li> <li>Send a
     * Server->Client packet ValidateLocation to correct the GuardLevelInstance
     * position and heading on the client</li><BR>
     * <BR>
     *
     * <B><U> Actions on second click on the GuardLevelInstance (Attack it/Interact
     * with it)</U> :</B><BR>
     * <BR>
     * <li>If L2PcInstance is in the _aggroList of the GuardLevelInstance, set the
     * L2PcInstance Intention to AI_INTENTION_ATTACK</li> <li>If L2PcInstance is
     * NOT in the _aggroList of the GuardLevelInstance, set the L2PcInstance
     * Intention to AI_INTENTION_INTERACT (after a distance verification) and
     * show message</li><BR>
     * <BR>
     *
     * <B><U> Example of use </U> :</B><BR>
     * <BR>
     * <li>Client packet : Action, AttackRequest</li><BR>
     * <BR>
     *
     * @param player
     *            The L2PcInstance that start an action on the GuardLevelInstance
     *
     */
    @Override
    public void onAction(L2PcInstance player) {
        if (!canTarget(player))
            return;

        // Check if the L2PcInstance already target the GuardLevelInstance
        if (getObjectId() != player.getTargetId()) {
            if (Config.DEBUG)
                _log.fine(player.getObjectId() + ": Targetted guard "
                        + getObjectId());

            // Set the target of the L2PcInstance player
            player.setTarget(this);

            // Send a Server->Client packet MyTargetSelected to the L2PcInstance
            // player
            // The color to display in the select window is White
            MyTargetSelected my = new MyTargetSelected(getObjectId(), 0);
            player.sendPacket(my);

            // Send a Server->Client packet ValidateLocation to correct the
            // L2NpcInstance position and heading on the client
            player.sendPacket(new ValidateLocation(this));
        } else {
            // Check if the L2PcInstance is in the _aggroList of the
            // GuardLevelInstance
            if (containsTarget(player)) {
                if (Config.DEBUG)
                    _log.fine(player.getObjectId() + ": Attacked guard "
                            + getObjectId());

                // Set the L2PcInstance Intention to AI_INTENTION_ATTACK
                player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK,
                        this);
            } else {
                // Calculate the distance between the L2PcInstance and the
                // L2NpcInstance
                if (!canInteract(player)) {
                    // Set the L2PcInstance Intention to AI_INTENTION_INTERACT
                    player.getAI().setIntention(
                            CtrlIntention.AI_INTENTION_INTERACT, this);
                } else {
                    // Send a Server->Client packet SocialAction to the all
                    // L2PcInstance on the _knownPlayer of the L2NpcInstance
                    // to display a social action of the GuardLevelInstance on
                    // their client
                    SocialAction sa = new SocialAction(getObjectId(),
                            Rnd.nextInt(8));
                    broadcastPacket(sa);

                    // Open a chat window on client with the text of the
                    // GuardLevelInstance
                    Quest[] qlsa = getTemplate().getEventQuests(
                            Quest.QuestEventType.QUEST_START);
                    if ((qlsa != null) && qlsa.length > 0)
                        player.setLastQuestNpcObject(getObjectId());
                    Quest[] qlst = getTemplate().getEventQuests(
                            Quest.QuestEventType.ON_FIRST_TALK);
                    if ((qlst != null) && qlst.length == 1)
                        qlst[0].notifyFirstTalk(this, player);
                    else
                        showChatWindow(player, 0);
                }
            }
        }
        // Send a Server->Client ActionFailed to the L2PcInstance in order to
        // avoid that the client wait another packet
        player.sendPacket(ActionFailed.STATIC_PACKET);
    }
}

GuardLevelKnownList
Код:
/*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.l2j.gameserver.model.actor.knownlist;

import java.util.logging.Logger;

import net.sf.l2j.Config;
import net.sf.l2j.gameserver.ai.CtrlIntention;
import net.sf.l2j.gameserver.ai.L2CharacterAI;
import net.sf.l2j.gameserver.model.L2Object;
import net.sf.l2j.gameserver.model.actor.instance.GuardLevelInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2MonsterInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;


public class GuardLevelKnownList extends AttackableKnownList
{
    private static Logger _log = Logger.getLogger(GuardLevelKnownList.class
            .getName());

    // =========================================================
    // Data Field

    // =========================================================
    // Constructor
    public GuardLevelKnownList(GuardLevelInstance activeChar) {
        super(activeChar);
    }

    // =========================================================
    // Method - Public
    @Override
    public boolean addKnownObject(L2Object object) {
        if (!super.addKnownObject(object))
            return false;

        if (object instanceof L2PcInstance) {
            // Check if the object added is a L2PcInstance that owns Karma
            L2PcInstance player = (L2PcInstance) object;

            if ((player.getLevel()>40))
            {
                if (Config.DEBUG)
                    _log.fine(getActiveChar().getObjectId() + ": Enemy "
                            + player.getObjectId() + " entered scan range");

                // Set the GuardLevelInstance Intention to AI_INTENTION_ACTIVE
                if (getActiveChar().getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
                    getActiveChar().getAI().setIntention(
                            CtrlIntention.AI_INTENTION_ACTIVE, null);
            }
        } else if ((Config.GUARD_ATTACK_AGGRO_MOB && getActiveChar()
                .isInActiveRegion()) && object instanceof L2MonsterInstance) {
            // Check if the object added is an aggressive L2MonsterInstance
            L2MonsterInstance mob = (L2MonsterInstance) object;

            if (mob.isAggressive()) {
                if (Config.DEBUG)
                    _log.fine(getActiveChar().getObjectId()
                            + ": Aggressive mob " + mob.getObjectId()
                            + " entered scan range");

                // Set the GuardLevelInstance Intention to AI_INTENTION_ACTIVE
                if (getActiveChar().getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
                    getActiveChar().getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null);
            }
        }

        return true;
    }

    @Override
    public boolean removeKnownObject(L2Object object) {
        if (!super.removeKnownObject(object))
            return false;

        // Check if the _aggroList of the GuardLevelInstance is Empty
        if (getActiveChar().noTarget()) {
            // removeAllKnownObjects();

            // Set the GuardLevelInstance to AI_INTENTION_IDLE
            L2CharacterAI ai = getActiveChar().getAI();
            if (ai != null)
                ai.setIntention(CtrlIntention.AI_INTENTION_IDLE, null);
        }

        return true;
    }

    // =========================================================
    // Method - Private

    // =========================================================
    // Property - Public
    @Override
    public final GuardLevelInstance getActiveChar() {
        return (GuardLevelInstance) super.getActiveChar();
    }
}

GuardLevelAI
[CODE]
/*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.l2j.gameserver.ai;

import static net.sf.l2j.gameserver.ai.CtrlIntention.*;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.Future;

import javolution.util.FastList;

import net.sf.l2j.Config;
import net.sf.l2j.gameserver.GameTimeController;
import net.sf.l2j.gameserver.GeoData;
import net.sf.l2j.gameserver.Territory;
import net.sf.l2j.gameserver.ThreadPoolManager;
import net.sf.l2j.gameserver.model.L2CharPosition;
import net.sf.l2j.gameserver.model.L2Object;
import net.sf.l2j.gameserver.model.L2Skill;
import net.sf.l2j.gameserver.model.actor.L2Attackable;
import net.sf.l2j.gameserver.model.actor.L2Character;
import net.sf.l2j.gameserver.model.actor.L2Npc;
import net.sf.l2j.gameserver.model.actor.L2Playable;
import net.sf.l2j.gameserver.model.actor.L2Summon;
import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2FestivalMonsterInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2FriendlyMobInstance;
import net.sf.l2j.gameserver.model.actor.instance.GuardLevelInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2MinionInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2MonsterInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
import net.sf.l2j.gameserver.util.Util;
import net.sf.l2j.util.Rnd;

/**
* This class manages AI of L2Attackable.<BR>
* <BR>
*
*/
public class GuardLevelAI extends L2CharacterAI implements Runnable
{

// protected static final Logger _log =
// Logger.getLogger(L2AttackableAI.class.getName());

private static final int RANDOM_WALK_RATE = 30; // confirmed
// private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30
// seconds

/** The L2Attackable AI task executed every 1s (call onEvtThink method) */
private Future<?> _aiTask;

/** The delay after which the attacked is stopped */
private int _attackTimeout;

/** The L2Attackable aggro counter */
private int _globalAggro;

/** The flag used to indicate that a thinking action is in progress */
private boolean _thinking; // to prevent recursive thinking

/** For attack AI, analysis of mob and its targets */
private SelfAnalysis _selfAnalysis = new SelfAnalysis();
private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
private TargetAnalysis _secondMostHatedAnalysis = new TargetAnalysis();

/**
* Constructor of L2AttackableAI.<BR>
* <BR>
*
* @param accessor
* The AI accessor of the L2Character
*
*/
public GuardLevelAI(L2Character.AIAccessor accessor) {
super(accessor);
_selfAnalysis.init();
_attackTimeout = Integer.MAX_VALUE;
_globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
}

public void run() {
// Launch actions corresponding to the Event Think
onEvtThink();

}

/**
* Return True if the target is autoattackable (depends on the actor type).<BR>
* <BR>
*
* <B><U> Actor is a GuardLevelInstance</U> :</B><BR>
* <BR>
* <li>The target isn't a Folk or a Door</li> <li>The target isn't dead,
* isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The L2PcInstance target has karma (=PK)</li> <li>The
* L2MonsterInstance target is aggressive</li><BR>
* <BR>
*
* <B><U> Actor is a L2SiegeGuardInstance</U> :</B><BR>
* <BR>
* <li>The target isn't a Folk or a Door</li> <li>The target isn't dead,
* isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>A siege is in progress</li> <li>The L2PcInstance target isn't a
* Defender</li><BR>
* <BR>
*
* <B><U> Actor is a L2FriendlyMobInstance</U> :</B><BR>
* <BR>
* <li>The target isn't a Folk, a Door or another L2NpcInstance</li> <li>The
* target isn't dead, isn't invulnerable, isn't in silent moving mode AND
* too far (>100)</li> <li>The target is in the actor Aggro range and is at
* the same height</li> <li>The L2PcInstance target has karma (=PK)</li><BR>
* <BR>
*
* <B><U> Actor is a L2MonsterInstance</U> :</B><BR>
* <BR>
* <li>The target isn't a Folk, a Door or another L2NpcInstance</li> <li>The
* target isn't dead, isn't invulnerable, isn't in silent moving mode AND
* too far (>100)</li> <li>The target is in the actor Aggro range and is at
* the same height</li> <li>The actor is Aggressive</li><BR>
* <BR>
*
* @param target
* The targeted L2Object
*
*/
private boolean autoAttackCondition(L2Character target) {
if (target == null || !(_actor instanceof L2Attackable))
return false;
L2Attackable me = (L2Attackable) _actor;

// Check if the target isn't invulnerable
if (target.isInvul()) {
// However EffectInvincible requires to check GMs specially
if (target instanceof L2PcInstance
&& ((L2PcInstance) target).isGM())
return false;
if (target instanceof L2Summon
&& ((L2Summon) target).getOwner().isGM())
return false;
}

// Check if the target isn't a Folk or a Door
if (target instanceof L2NpcInstance || target instanceof L2DoorInstance)
return false;

// Check if the target isn't dead, is in the Aggro range and is at the
// same height
if (target.isAlikeDead()
|| !me.isInsideRadius(target, me.getAggroRange(), false, false)
|| Math.abs(_actor.getZ() - target.getZ()) > 300)
return false;

if (_selfAnalysis.cannotMoveOnLand
&& !target.isInsideZone(L2Character.ZONE_WATER))
return false;

// Check if the target is a L2PlayableInstance
if (target instanceof L2Playable) {
// Check if the AI isn't a Raid Boss and the target isn't in silent
// move mode
if (!(me.isRaid()) && ((L2Playable) target).isSilentMoving())
return false;
}

// Check if the target is a L2PcInstance
if (target instanceof L2PcInstance) {
// Don't take the aggro if the GM has the access level below or
// equal to GM_DONT_TAKE_AGGRO
if (((L2PcInstance) target).isGM()
&& !((L2PcInstance) target).getAccessLevel().canTakeAggro())
return false;

// TODO: Ideally, autoattack condition should be called from the AI
// script. In that case,
// it should only implement the basic behaviors while the script
// will add more specific
// behaviors (like varka/ketra alliance, etc). Once implemented,
// remove specialized stuff
// from this location. (Fulminus)


// check if the target is within the grace period for JUST getting
// up from fake death
if (((L2PcInstance) target).isRecentFakeDeath())
return false;


}
// Check if the target is a L2Summon
if (target instanceof L2Summon)
{
L2PcInstance owner = ((L2Summon) target).getOwner();
if (owner != null)
{
// Don't take the aggro if the GM has the access level below or
// equal to GM_DONT_TAKE_AGGRO
if (owner.isGM()
&& (owner.isInvul() || !owner.getAccessLevel()
.canTakeAggro()))
return false;

}
}
// Check if the actor is a GuardLevelInstance
if (_actor instanceof GuardLevelInstance) {

// Check if the L2PcInstance target has karma (=PK)
if (target instanceof L2PcInstance
&& ((L2PcInstance) target).getLevel()>40)
// Los Check
return GeoData.getInstance().canSeeTarget(me, target);

// if (target instanceof L2Summon)
// return ((L2Summon)target).getKarma() > 0;

// Check if the L2MonsterInstance target is aggressive
if (target instanceof L2MonsterInstance)
return (((L2MonsterInstance) target).isAggressive() && GeoData
.getInstance().canSeeTarget(me, target));

return false;
} else if (_actor instanceof L2FriendlyMobInstance) { // the actor is a
// L2FriendlyMobInstance

// Check if the target isn't another L2NpcInstance
if (target instanceof L2Npc)
return false;

// Check if the L2PcInstance target has karma (=PK)
if (target instanceof L2PcInstance
&& ((L2PcInstance) target).getLevel()>40)
// Los Check
return GeoData.getInstance().canSeeTarget(me, target);
else
return false;
}
else
{ // The actor is a L2MonsterInstance

// Check if the target isn't another L2NpcInstance
if (target instanceof L2Npc)
return false;



// Check if the actor is Aggressive
return (me.isAggressive() && GeoData.getInstance().canSeeTarget(me,
target));
}
}

public void startAITask() {
// If not idle - create an AI task (schedule onEvtThink repeatedly)
if (_aiTask == null) {
_aiTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(
this, 1000, 1000);
}
}

public void stopAITask() {
if (_aiTask != null) {
_aiTask.cancel(false);
_aiTask = null;
}
}

@Override
protected void onEvtDead() {
stopAITask();
super.onEvtDead();
}

/**
* Set the Intention of this L2CharacterAI and create an AI Task executed
* every 1s (call onEvtThink method) for this L2Attackable.<BR>
* <BR>
*
* <FONT COLOR=#FF0000><B> <U>Caution</U> : If actor _knowPlayer isn't
* EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE</B></FONT><BR>
* <BR>
*
* @param intention
* The new Intention to set to the AI
* @param arg0
* The first parameter of the Intention
* @param arg1
* The second parameter of the Intention
*
*/
@Override
synchronized void changeIntention(CtrlIntention intention, Object arg0,
Object arg1) {
if (intention == AI_INTENTION_IDLE || intention == AI_INTENTION_ACTIVE) {
// Check if actor is not dead
if (!_actor.isAlikeDead()) {
L2Attackable npc = (L2Attackable) _actor;

// If its _knownPlayer isn't empty set the Intention to
// AI_INTENTION_ACTIVE
if (!npc.getKnownList().getKnownPlayers().isEmpty())
intention = AI_INTENTION_ACTIVE;
else {
if (npc.getSpawn() != null) {
final int range = Config.MAX_DRIFT_RANGE;
if (!npc.isInsideRadius(npc.getSpawn().getLocx(), npc
.getSpawn().getLocy(),
npc.getSpawn().getLocz(), range + range, true,
false))
intention = AI_INTENTION_ACTIVE;
}
}
}

if (intention == AI_INTENTION_IDLE) {
// Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
super.changeIntention(AI_INTENTION_IDLE, null, null);

// Stop AI task and detach AI from NPC
if (_aiTask != null) {
_aiTask.cancel(true);
_aiTask = null;
}

// Cancel the AI
_accessor.detachAI();

return;
}
}

// Set the Intention of this L2AttackableAI to intention
super.changeIntention(intention, arg0, arg1);

// If not idle - create an AI task (schedule onEvtThink repeatedly)
startAITask();
}

/**
* Manage the Attack Intention : Stop current Attack (if necessary),
* Calculate attack timeout, Start a new Attack and Launch Think Event.<BR>
* <BR>
*
* @param target
* The L2Character to attack
*
*/
@Override
protected void onIntentionAttack(L2Character target) {
// Calculate the attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();

// self and buffs
if (_selfAnalysis.lastBuffTick + 100 < GameTimeController
.getGameTicks()) {
for (L2Skill sk : _selfAnalysis.buffSkills) {
if (_actor.getFirstEffect(sk.getId()) == null) {
if (_actor.getCurrentMp() < sk.getMpConsume())
continue;
if (_actor.isSkillDisabled(sk.getId()))
continue;
// no clan buffs here?
if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_CLAN)
continue;
L2Object OldTarget = _actor.getTarget();
_actor.setTarget(_actor);
clientStopMoving(null);
_accessor.doCast(sk);
// forcing long reuse delay so if cast get interrupted or
// there would be several buffs, doesn't cast again
_selfAnalysis.lastBuffTick = GameTimeController
.getGameTicks();
_actor.setTarget(OldTarget);
}
}
}
// Manage the Attack Intention : Stop current Attack (if necessary),
// Start a new Attack and Launch Think Event
super.onIntentionAttack(target);
}

/**
* Manage AI standard thinks of a L2Attackable (called by onEvtThink).<BR>
* <BR>
*
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Update every 1s the _globalAggro counter to come close to 0</li> <li>
* If the actor is Aggressive and can attack, add all autoAttackable
* L2Character in its Aggro Range to its _aggroList, chose a target and
* order to attack it</li> <li>If the actor is a GuardLevelInstance that can't
* attack, order to it to return to its home location</li> <li>If the actor
* is a L2MonsterInstance that can't attack, order to it to random walk
* (1/100)</li><BR>
* <BR>
*
*/
private void thinkActive() {
L2Attackable npc = (L2Attackable) _actor;

// Update every 1s the _globalAggro counter to come close to 0
if (_globalAggro != 0) {
if (_globalAggro < 0)
_globalAggro++;
else
_globalAggro--;
}

// Add all autoAttackable L2Character in L2Attackable Aggro Range to its
// _aggroList with 0 damage and 1 hate
// A L2Attackable isn't aggressive during 10s after its spawn because
// _globalAggro is set to -10
if (_globalAggro >= 0) {
// Get all visible objects inside its Aggro Range
// L2Object[] objects =
// L2World.getInstance().getVisibleObjects(_actor,
// ((L2NpcInstance)_actor).getAggroRange());
// Go through visible objects
Collection<L2Object> objs = npc.getKnownList().getKnownObjects()
.values();
// synchronized (npc.getKnownList().getKnownObjects())
{
for (L2Object obj : objs) {
if (!(obj instanceof L2Character))
continue;
L2Character target = (L2Character) obj;



/*
* Temporarily adding this commented code as a concept to be
* used eventually. However, the way it is written below
* will NOT work correctly. The NPC should only notify Aggro
* Range Enter when someone enters the range from outside.
* Instead, the below code will keep notifying even while
* someone remains within the range. Perhaps we need a short
* knownlist of range = aggroRange for just people who are
* actively within the npc's aggro range?...(Fulminus) //
* notify AI that a playable instance came within aggro
* range if ((obj instanceof L2PcInstance) || (obj
* instanceof L2Summon)) { if (
* !((L2Character)obj).isAlikeDead() &&
* !npc.isInsideRadius(obj, npc.getAggroRange(), true,
* false) ) { L2PcInstance targetPlayer = (obj instanceof
* L2PcInstance)? (L2PcInstance) obj: ((L2Summon)
* obj).getOwner(); if
* (npc.getTemplate().getEventQuests(Quest
* .QuestEventType.ON_AGGRO_RANGE_ENTER) !=null) for (Quest
* quest:
* npc.getTemplate().getEventQuests(Quest.QuestEventType
* .ON_AGGRO_RANGE_ENTER)) quest.notifyAggroRangeEnter(npc,
* targetPlayer, (obj instanceof L2Summon)); } }
*/
// TODO: The AI Script ought to handle aggro behaviors in
// onSee. Once implemented, aggro behaviors ought
// to be removed from here. (Fulminus)
// For each L2Character check if the target is
// autoattackable
if (autoAttackCondition(target)) // check aggression
{
// Get the hate level of the L2Attackable against this
// L2Character target contained in _aggroList
int hating = npc.getHating(target);

// Add the attacker to the L2Attackable _aggroList with
// 0 damage and 1 hate
if (hating == 0)
npc.addDamageHate(target, 0, 0);
}
}
}

// Chose a target from its aggroList
L2Character hated;
if (_actor.isConfused())
hated = getAttackTarget(); // effect handles selection
else
hated = npc.getMostHated();

// Order to the L2Attackable to attack the target
if (hated != null) {
// Get the hate level of the L2Attackable against this
// L2Character target contained in _aggroList
int aggro = npc.getHating(hated);

if (aggro + _globalAggro > 0) {
// Set the L2Character movement type to run and send
// Server->Client packet ChangeMoveType to all others
// L2PcInstance
if (!_actor.isRunning())
_actor.setRunning();

// Set the AI Intention to AI_INTENTION_ATTACK
setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated);
}

return;
}

}

// Check if the actor is a GuardLevelInstance
if (_actor instanceof GuardLevelInstance) {
// Order to the GuardLevelInstance to return to its home location
// because there's no target to attack
((GuardLevelInstance) _actor).returnHome();
}

// If this is a festival monster, then it remains in the same location.
if (_actor instanceof L2FestivalMonsterInstance)
return;

// Check if the mob should not return to spawn point
if (!npc.canReturnToSpawnPoint())
return;

// Minions following leader
if (_actor instanceof L2MinionInstance
&& ((L2MinionInstance) _actor).getLeader() != null) {
int offset;

if (_actor.isRaidMinion())
offset = 500; // for Raids - need correction
else
offset = 200; // for normal minions - need correction Smile

if (((L2MinionInstance) _actor).getLeader().isRunning())
_actor.setRunning();
else
_actor.setWalking();

if (_actor.getPlanDistanceSq(((L2MinionInstance) _actor)
.getLeader()) > offset * offset) {
int x1, y1, z1;
x1 = ((L2MinionInstance) _actor).getLeader().getX()
+ Rnd.nextInt((offset - 30) * 2) - (offset - 30);
y1 = ((L2MinionInstance) _actor).getLeader().getY()
+ Rnd.nextInt((offset - 30) * 2) - (offset - 30);
z1 = ((L2MinionInstance) _actor).getLeader().getZ();
// Move the actor to Location (x,y,z) server side AND client
// side by sending Server->Client packet CharMoveToLocation
// (broadcast)
moveTo(x1, y1, z1);
return;
} else if (Rnd.nextInt(RANDOM_WALK_RATE) == 0) {
// self and clan buffs
for (L2Skill sk : _selfAnalysis.buffSkills) {
if (_actor.getFirstEffect(sk.getId()) == null) {
// if clan buffs, don't buff every time
if (sk.getTargetType() != L2Skill.SkillTargetType.TARGET_SELF
&& Rnd.nextInt(2) != 0)
continue;
if (_actor.getCurrentMp() < sk.getMpConsume())
continue;
if (_actor.isSkillDisabled(sk.getId()))
continue;
L2Object OldTarget = _actor.getTarget();
_actor.setTarget(_actor);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
}
// Order to the L2MonsterInstance to random walk (1/100)
else if (npc.getSpawn() != null && Rnd.nextInt(RANDOM_WALK_RATE) == 0
&& !_actor.isNoRndWalk()) {
int x1, y1, z1;
final int range = Config.MAX_DRIFT_RANGE;

// self and clan buffs
for (L2Skill sk : _selfAnalysis.buffSkills) {
if (_actor.getFirstEffect(sk.getId()) == null) {
// if clan buffs, don't buff every time
if (sk.getTargetType() != L2Skill.SkillTargetType.TARGET_SELF
&& Rnd.nextInt(2) != 0)
continue;
if (_actor.getCurrentMp() < sk.getMpConsume())
continue;
if (_actor.isSkillDisabled(sk.getId()))
continue;
L2Object OldTarget = _actor.getTarget();
_actor.setTarget(_actor);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}

// If NPC with random coord in territory
if (npc.getSpawn().getLocx() == 0 && npc.getSpawn().getLocy() == 0) {
// Calculate a destination point in the spawn area
int p[] = Territory.getInstance().getRandomPoint(
npc.getSpawn().getLocation());
x1 = p[0];
y1 = p[1];
z1 = p[2];

// Calculate the distance between the current position of the
// L2Character and the target (x,y)
double distance2 = _actor.getPlanDistanceSq(x1, y1);

if (distance2 > (range + range) * (range + range)) {
npc.setisReturningToSpawnPoint(true);
float delay = (float) Math.sqrt(distance2) / range;
x1 = _actor.getX() + (int) ((x1 - _actor.getX()) / delay);
y1 = _actor.getY() + (int) ((y1 - _actor.getY()) / delay);
}

// If NPC with random fixed coord, don't move (unless needs to
// return to spawnpoint)
if (Territory.getInstance().getProcMax(
npc.getSpawn().getLocation()) > 0
&& !npc.isReturningToSpawnPoint())
return;
} else {
// If NPC with fixed coord
x1 = npc.getSpawn().getLocx();
y1 = npc.getSpawn().getLocy();
z1 = npc.getSpawn().getLocz();

if (!_actor.isInsideRadius(x1, y1, z1, range + range, true,
false))
npc.setisReturningToSpawnPoint(true);
else {
x1 += Rnd.nextInt(range * 2) - range;
y1 += Rnd.nextInt(range * 2) - range;
z1 = npc.getZ();
}
}

// _log.config("Curent pos ("+getX()+", "+getY()+"), moving to ("+x1+", "+y1+").");
// Move the actor to Location (x,y,z) server side AND client side by
// sending Server->Client packet CharMoveToLocation (broadcast)
moveTo(x1, y1, z1);
}
}

/**
* Manage AI attack thinks of a L2Attackable (called by onEvtThink).<BR>
* <BR>
*
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Update the attack timeout if actor is running</li> <li>If target is
* dead or timeout is expired, stop this attack and set the Intention to
* AI_INTENTION_ACTIVE</li> <li>Call all L2Object of its Faction inside the
* Faction Range</li> <li>Chose a target and order to attack it with magic
* skill or physical attack</li><BR>
* <BR>
*
* TODO: Manage casting rules to healer mobs (like Ant Nurses)
*
*/
private void thinkAttack() {
if (_attackTimeout < GameTimeController.getGameTicks()) {
// Check if the actor is running
if (_actor.isRunning()) {
// Set the actor movement type to walk and send Server->Client
// packet ChangeMoveType to all others L2PcInstance
_actor.setWalking();

// Calculate a new attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
}
}

L2Character originalAttackTarget = getAttackTarget();
// Check if target is dead or if timeout is expired to stop this attack
if (originalAttackTarget == null || originalAttackTarget.isAlikeDead()
|| _attackTimeout < GameTimeController.getGameTicks()) {
// Stop hating this target after the attack timeout or if target is
// dead
if (originalAttackTarget != null)
((L2Attackable) _actor).stopHating(originalAttackTarget);

// Set the AI Intention to AI_INTENTION_ACTIVE
setIntention(AI_INTENTION_ACTIVE);

_actor.setWalking();
return;
}



if (_actor.isAttackingDisabled())
return;

// Get 2 most hated chars
List<L2Character> hated = ((L2Attackable) _actor).get2MostHated();
if (_actor.isConfused()) {
if (hated != null)
hated.set(0, originalAttackTarget); // effect handles selection
else {
hated = new FastList<L2Character>();
hated.add(originalAttackTarget);
hated.add(null);
}
}

if (hated == null || hated.get(0) == null) {
setIntention(AI_INTENTION_ACTIVE);
return;
}
if (hated.get(0) != originalAttackTarget) {
setAttackTarget(hated.get(0));
}
_mostHatedAnalysis.update(hated.get(0));
_secondMostHatedAnalysis.update(hated.get(1));

// Get all information needed to choose between physical or magical
// attack
_actor.setTarget(_mostHatedAnalysis.character);
double dist2 = _actor.getPlanDistanceSq(
_mostHatedAnalysis.character.getX(),
_mostHatedAnalysis.character.getY());
int combinedCollision = _actor.getTemplate().collisionRadius
+ _mostHatedAnalysis.character.getTemplate().collisionRadius;
int range = _actor.getPhysicalAttackRange() + combinedCollision;

// Reconsider target next round if _actor hasn't got hits in for last 14
// seconds
if (!_actor.isMuted()
&& _attackTimeout - 160 < GameTimeController.getGameTicks()
&& _secondMostHatedAnalysis.character != null) {
if (Util.checkIfInRange(900, _actor, hated.get(1), true)) {
// take off 2* the amount the aggro is larger than second most
((L2Attackable) _actor)
.reduceHate(hated.get(0),
2 * (((L2Attackable) _actor).getHating(hated
.get(0)) - ((L2Attackable) _actor)
.getHating(hated.get(1))));
// Calculate a new attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
}
}
// Reconsider target during next round if actor is rooted and cannot
// reach mostHated but can
// reach secondMostHated
if (_actor.isRooted() && _secondMostHatedAnalysis.character != null) {
if (_selfAnalysis.isMage
&& dist2 > _selfAnalysis.maxCastRange
* _selfAnalysis.maxCastRange
&& _actor.getPlanDistanceSq(
_secondMostHatedAnalysis.character.getX(),
_secondMostHatedAnalysis.character.getY()) < _selfAnalysis.maxCastRange
* _selfAnalysis.maxCastRange) {
((L2Attackable) _actor)
.reduceHate(hated.get(0),
1 + (((L2Attackable) _actor).getHating(hated
.get(0)) - ((L2Attackable) _actor)
.getHating(hated.get(1))));
} else if (dist2 > range * range
&& _actor.getPlanDistanceSq(
_secondMostHatedAnalysis.character.getX(),
_secondMostHatedAnalysis.character.getY()) < range
* range) {
((L2Attackable) _actor)
.reduceHate(hated.get(0),
1 + (((L2Attackable) _actor).getHating(hated
.get(0)) - ((L2Attackable) _actor)
.getHating(hated.get(1))));
}
}

// Considering, if bigger range will be attempted
if ((dist2 < 10000 + combinedCollision * combinedCollision)
&& !_selfAnalysis.isFighter
&& !_selfAnalysis.isBalanced
&& (_selfAnalysis.hasLongRangeSkills || _selfAnalysis.isArcher || _selfAnalysis.isHealer)
&& (_mostHatedAnalysis.isBalanced || _mostHatedAnalysis.isFighter)
&& (_mostHatedAnalysis.character.isRooted() || _mostHatedAnalysis.isSlower)
&& (Config.GEODATA == 2 ? 20 : 12) >= Rnd.get(100) // chance
) {
int posX = _actor.getX();
int posY = _actor.getY();
int posZ = _actor.getZ();
double distance = Math.sqrt(dist2); // This way, we only do the sqrt
// if we need it

int signx = -1;
int signy = -1;
if (_actor.getX() > _mostHatedAnalysis.character.getX())
signx = 1;
if (_actor.getY() > _mostHatedAnalysis.character.getY())
signy = 1;
posX += Math
.round((float) ((signx * ((range / 2) + (Rnd.get(range)))) - distance));
posY += Math
.round((float) ((signy * ((range / 2) + (Rnd.get(range)))) - distance));
setIntention(CtrlIntention.AI_INTENTION_MOVE_TO,
new L2CharPosition(posX, posY, posZ, 0));
return;
}

// Cannot see target, needs to go closer, currently just goes to range
// 300 if mage
if ((dist2 > 310 * 310 + combinedCollision * combinedCollision)
&& this._selfAnalysis.hasLongRangeSkills
&& !GeoData.getInstance().canSeeTarget(_actor,
_mostHatedAnalysis.character)) {
if (!(_selfAnalysis.isMage && _actor.isMuted())) {
moveToPawn(_mostHatedAnalysis.character, 300);
return;
}
}

if (_mostHatedAnalysis.character.isMoving())
range += 50;
// Check if the actor is far from target
if (dist2 > range * range) {
if (!_actor.isMuted()
&& (_selfAnalysis.hasLongRangeSkills || !_selfAnalysis.healSkills
.isEmpty())) {
// check for long ranged skills and heal/buff skills
if (!_mostHatedAnalysis.isCanceled) {
for (L2Skill sk : _selfAnalysis.cancelSkills) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= 8) {
clientStopMoving(null);
_accessor.doCast(sk);
_mostHatedAnalysis.isCanceled = true;
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
return;
}
}
}
if (this._selfAnalysis.lastDebuffTick + 60 < GameTimeController
.getGameTicks()) {
for (L2Skill sk : _selfAnalysis.debuffSkills) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
int chance = 8;
if (_selfAnalysis.isFighter
&& _mostHatedAnalysis.isMage)
chance = 3;
if (_selfAnalysis.isFighter
&& _mostHatedAnalysis.isArcher)
chance = 12;
if (_selfAnalysis.isMage && !_mostHatedAnalysis.isMage)
chance = 10;
if (_selfAnalysis.isHealer)
chance = 12;
if (_mostHatedAnalysis.isMagicResistant)
chance /= 2;

if (Rnd.nextInt(100) <= chance) {
clientStopMoving(null);
_accessor.doCast(sk);
_selfAnalysis.lastDebuffTick = GameTimeController
.getGameTicks();
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
return;
}
}
}
if (!_mostHatedAnalysis.character.isMuted()) {
int chance = 8;
if (!(_mostHatedAnalysis.isMage || _mostHatedAnalysis.isBalanced))
chance = 3;
for (L2Skill sk : _selfAnalysis.muteSkills) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= chance) {
clientStopMoving(null);
_accessor.doCast(sk);
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
return;
}
}
}
if (_secondMostHatedAnalysis.character != null
&& !_secondMostHatedAnalysis.character.isMuted()
&& (_secondMostHatedAnalysis.isMage || _secondMostHatedAnalysis.isBalanced)) {
double secondHatedDist2 = _actor.getPlanDistanceSq(
_secondMostHatedAnalysis.character.getX(),
_secondMostHatedAnalysis.character.getY());
for (L2Skill sk : _selfAnalysis.muteSkills) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (secondHatedDist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= 2) {
_actor.setTarget(_secondMostHatedAnalysis.character);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(_mostHatedAnalysis.character);
return;
}
}
}
if (!_mostHatedAnalysis.character.isSleeping()) {
for (L2Skill sk : _selfAnalysis.sleepSkills) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= (_selfAnalysis.isHealer ? 10
: 1)) {
clientStopMoving(null);
_accessor.doCast(sk);
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
return;
}
}
}
if (_secondMostHatedAnalysis.character != null
&& !_secondMostHatedAnalysis.character.isSleeping()) {
double secondHatedDist2 = _actor.getPlanDistanceSq(
_secondMostHatedAnalysis.character.getX(),
_secondMostHatedAnalysis.character.getY());
for (L2Skill sk : _selfAnalysis.sleepSkills) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (secondHatedDist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= (_selfAnalysis.isHealer ? 10
: 3)) {
_actor.setTarget(_secondMostHatedAnalysis.character);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(_mostHatedAnalysis.character);
return;
}
}
}
if (!_mostHatedAnalysis.character.isRooted()) {
for (L2Skill sk : _selfAnalysis.rootSkills) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= (_mostHatedAnalysis.isSlower ? 3
: 8)) {
clientStopMoving(null);
_accessor.doCast(sk);
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
return;
}
}
}
if (!_mostHatedAnalysis.character.isAttackingDisabled()) {
for (L2Skill sk : _selfAnalysis.generalDisablers) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= ((_selfAnalysis.isFighter && _actor
.isRooted()) ? 15 : 7)) {
clientStopMoving(null);
_accessor.doCast(sk);
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
return;
}
}
}
if (_actor.getCurrentHp() < _actor.getMaxHp() * 0.4) {
for (L2Skill sk : _selfAnalysis.healSkills) {
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk))
continue;
int chance = 7;
if (_mostHatedAnalysis.character.isAttackingDisabled())
chance += 10;
if (_secondMostHatedAnalysis.character == null
|| _secondMostHatedAnalysis.character
.isAttackingDisabled())
chance += 10;
if (Rnd.nextInt(100) <= chance) {
_actor.setTarget(_actor);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(_mostHatedAnalysis.character);
return;
}
}
}

// chance decision for launching long range skills
int castingChance = 5;
if (_selfAnalysis.isMage || _selfAnalysis.isHealer)
castingChance = 50; // mages
if (_selfAnalysis.isBalanced) {
if (!_mostHatedAnalysis.isFighter) // advance to mages
castingChance = 15;
else
castingChance = 25; // stay away from fighters
}
if (_selfAnalysis.isFighter) {
if (_mostHatedAnalysis.isMage)
castingChance = 3;
else
castingChance = 7;
if (_actor.isRooted())
castingChance = 20; // doesn't matter if no success
// first round
}
for (L2Skill sk : _selfAnalysis.generalSkills) {

int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;

if (Rnd.nextInt(100) <= castingChance) {
clientStopMoving(null);
_accessor.doCast(sk);
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
return;
}
}
}

// Move the actor to Pawn server side AND client side by sending
// Server->Client packet MoveToPawn (broadcast)
if (_selfAnalysis.isMage && !_actor.isMuted()) {
// mages stay a bit further away if not muted or low mana
if ((_actor.getMaxMp() / 3) < _actor.getCurrentMp()) {
range = _selfAnalysis.maxCastRange;
if (dist2 < range * range) // don't move backwards here
return;
}
}
// healers do not even follow
if (_selfAnalysis.isHealer)
return;

if (_mostHatedAnalysis.character.isMoving())
range -= 100;
if (range < 5)
range = 5;
moveToPawn(_mostHatedAnalysis.character, range);
return;
}
// **************************************************
// Else, if this is close enough for physical attacks
else {
// In case many mobs are trying to hit from same place, move a bit,
// circling around the target
if (Rnd.nextInt(100) <= 33) // check it once per 3 seconds
{
for (L2Object nearby : _actor.getKnownList()
.getKnownCharactersInRadius(10)) {
if (nearby instanceof L2Attackable
&& nearby != _mostHatedAnalysis.character) {
int diffx = Rnd.get(combinedCollision,
combinedCollision + 40);
if (Rnd.get(10) < 5)
diffx = -diffx;
int diffy = Rnd.get(combinedCollision,
combinedCollision + 40);
if (Rnd.get(10) < 5)
diffy = -diffy;
moveTo(_mostHatedAnalysis.character.getX() + diffx,
_mostHatedAnalysis.character.getY() + diffy,
_mostHatedAnalysis.character.getZ());
return;
}
}
}

// Calculate a new attack timeout.
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();

// check for close combat skills && heal/buff skills

if (!_mostHatedAnalysis.isCanceled) {
for (L2Skill sk : _selfAnalysis.cancelSkills) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= 8) {
clientStopMoving(null);
_accessor.doCast(sk);
_mostHatedAnalysis.isCanceled = true;
return;
}
}
}
if (this._selfAnalysis.lastDebuffTick + 60 < GameTimeController
.getGameTicks()) {
for (L2Skill sk : _selfAnalysis.debuffSkills) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
int chance = 5;
if (_selfAnalysis.isFighter && _mostHatedAnalysis.isMage)
chance = 3;
if (_selfAnalysis.isFighter && _mostHatedAnalysis.isArcher)
chance = 3;
if (_selfAnalysis.isMage && !_mostHatedAnalysis.isMage)
chance = 4;
if (_selfAnalysis.isHealer)
chance = 12;
if (_mostHatedAnalysis.isMagicResistant)
chance /= 2;
if (sk.getCastRange() < 200)
chance += 3;
if (Rnd.nextInt(100) <= chance) {
clientStopMoving(null);
_accessor.doCast(sk);
_selfAnalysis.lastDebuffTick = GameTimeController
.getGameTicks();
return;
}
}
}
if (!_mostHatedAnalysis.character.isMuted()
&& (_mostHatedAnalysis.isMage || _mostHatedAnalysis.isBalanced)) {
for (L2Skill sk : _selfAnalysis.muteSkills) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= 7) {
clientStopMoving(null);
_accessor.doCast(sk);
return;
}
}
}
if (_secondMostHatedAnalysis.character != null
&& !_secondMostHatedAnalysis.character.isMuted()
&& (_secondMostHatedAnalysis.isMage || _secondMostHatedAnalysis.isBalanced)) {
double secondHatedDist2 = _actor.getPlanDistanceSq(
_secondMostHatedAnalysis.character.getX(),
_secondMostHatedAnalysis.character.getY());
for (L2Skill sk : _selfAnalysis.muteSkills) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (secondHatedDist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= 3) {
_actor.setTarget(_secondMostHatedAnalysis.character);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(_mostHatedAnalysis.character);
return;
}
}
}
if (!_mostHatedAnalysis.character.isSleeping()
&& _selfAnalysis.isHealer) {
for (L2Skill sk : _selfAnalysis.sleepSkills) {
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= 10) {
clientStopMoving(null);
_accessor.doCast(sk);
_attackTimeout = MAX_ATTACK_TIMEOUT
+ GameTimeController.getGameTicks();
return;
}
}
}
if (_secondMostHatedAnalysis.character != null
&& !_secondMostHatedAnalysis.character.isSleeping()) {
double secondHatedDist2 = _actor.getPlanDistanceSq(
_secondMostHatedAnalysis.character.getX(),
_secondMostHatedAnalysis.character.getY());
for (L2Skill sk : _selfAnalysis.sleepSkills) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (secondHatedDist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= (_selfAnalysis.isHealer ? 10 : 4)) {
_actor.setTarget(_secondMostHatedAnalysis.character);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(_mostHatedAnalysis.character);
return;
}
}
}
if (!_mostHatedAnalysis.character.isRooted()
&& _mostHatedAnalysis.isFighter && !_selfAnalysis.isFighter) {
for (L2Skill sk : _selfAnalysis.rootSkills) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= (_selfAnalysis.isHealer ? 10 : 4)) {
clientStopMoving(null);
_accessor.doCast(sk);
return;
}
}
}
if (!_mostHatedAnalysis.character.isAttackingDisabled()) {
for (L2Skill sk : _selfAnalysis.generalDisablers) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;
if (Rnd.nextInt(100) <= ((sk.getCastRange() < 200) ? 10 : 7)) {
clientStopMoving(null);
_accessor.doCast(sk);
return;
}
}
}
if (_actor.getCurrentHp() < _actor.getMaxHp()
* (_selfAnalysis.isHealer ? 0.7 : 0.4)) {
for (L2Skill sk : _selfAnalysis.healSkills) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk))
continue;
int chance = (_selfAnalysis.isHealer ? 15 : 7);
if (_mostHatedAnalysis.character.isAttackingDisabled())
chance += 10;
if (_secondMostHatedAnalysis.character == null
|| _secondMostHatedAnalysis.character
.isAttackingDisabled())
chance += 10;
if (Rnd.nextInt(100) <= chance) {
_actor.setTarget(_actor);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(_mostHatedAnalysis.character);
return;
}
}
}
for (L2Skill sk : _selfAnalysis.generalSkills) {
if ((_actor.isMuted() && sk.isMagic())
|| (_actor.isPhysicalMuted() && !sk.isMagic()))
continue;
int castRange = sk.getCastRange() + combinedCollision;
if (_actor.isSkillDisabled(sk.getId())
|| _actor.getCurrentMp() < _actor.getStat()
.getMpConsume(sk)
|| (dist2 > castRange * castRange))
continue;

// chance decision for launching general skills in melee fight
// close range skills should be higher, long range lower
int castingChance = 5;
if (_selfAnalysis.isMage || _selfAnalysis.isHealer) {
if (sk.getCastRange() < 200)
castingChance = 35;
else
castingChance = 25; // mages
}
if (_selfAnalysis.isBalanced) {
if (sk.getCastRange() < 200)
castingChance = 12;
else {
if (_mostHatedAnalysis.isMage) // hit mages
castingChance = 2;
else
castingChance = 5;
}

}
if (_selfAnalysis.isFighter) {
if (sk.getCastRange() < 200)
castingChance = 12;
else {
if (_mostHatedAnalysis.isMage)
castingChance = 1;
else
castingChance = 3;
}
}
if (Rnd.nextInt(100) <= castingChance) {
clientStopMoving(null);
_accessor.doCast(sk);
return;
}
}

// Finally, physical attacks
if (!_selfAnalysis.isHealer) {
clientStopMoving(null);
_accessor.doAttack(_mostHatedAnalysis.character);
}
}
}

/**
* Manage AI ...
Ответ
#4
а тут?
public class GuardLevelAI extends L2CharacterAI implements Runnable

замените на L2AttackableAI
Ответ
#5
Mifesto Написал:а тут?
public class GuardLevelAI extends L2CharacterAI implements Runnable

замените на L2AttackableAI

Менял. Такое вот выскакивает.
[SPOILER="
Открыть спойлер

Добавлено через 9 минут
Я не знаю где этот onSpawn(GuardLevelInstance.java:142) который он не может найти( и я в том числе).
Ответ
#6
energy Написал:Я не знаю где этот onSpawn(GuardLevelInstance.java:142) который он не может найти( и я в том числе).

public void onSpawn() {
setIsNoRndWalk(true);
super.onSpawn();
Ответ
#7
KapkapoB Написал:public void onSpawn() {
setIsNoRndWalk(true);
super.onSpawn();

Я имел ввиду почему оно не хочет принимать координаты спавна. Нада ли где-нибудь прописать еще эти координаты, кроме спавнлиста?
Ответ


Возможно похожие темы ...
Тема Автор Ответы Просмотры Последний пост
  Не могу запустить Game and Login server RandomSkill 16 6,001 07-09-2013, 10:11 PM
Последний пост: RandomSkill
  не могу перекрасить код java mortican 3 1,623 10-16-2012, 01:23 PM
Последний пост: halaymoon
  Не могу понть почему канс 100% Dr_Lector 22 6,664 07-14-2012, 01:55 PM
Последний пост: helly
  TVT не могу запустить Vlad2000 6 2,596 04-13-2012, 02:11 PM
Последний пост: Vlad2000
  [Help] Не могу зайти на свой сервер дальше выбора сервака Daitenshi 14 13,172 03-12-2012, 08:27 PM
Последний пост: L2scripts-Guard
  не могу зайти на сервер skil 7 3,707 10-12-2011, 05:16 PM
Последний пост: Mifesto
  Не могу зайти в сервер Apachai 8 9,139 07-30-2011, 11:21 AM
Последний пост: gorodetskiy
  L2open,не могу зопустить сервер! skil 4 2,465 03-11-2010, 08:00 PM
Последний пост: skil
  Не могу понять? skil 25 7,336 11-17-2009, 10:16 PM
Последний пост: FXAntiXrist
  не могу создать акк! CensoredCens 0 1,942 11-17-2009, 02:35 AM
Последний пост: CensoredCens

Перейти к форуму:


Пользователи, просматривающие эту тему: 2 Гость(ей)