 |
|

06.04.2016, 03:29
|
|
Участник форума
Регистрация: 05.04.2016
Сообщений: 222
С нами:
5318817
Репутация:
0
|
|
DefaultAI.java
Сообщение от Спойлер
PHP:
Код:
package l2p.gameserver.ai;
import l2p.commons.collections.CollectionUtils;
import l2p.commons.collections.LazyArrayList;
import l2p.commons.lang.reference.HardReference;
import l2p.commons.math.random.RndSelector;
import l2p.commons.threading.RunnableImpl;
import l2p.commons.util.Rnd;
import l2p.gameserver.configs.Config;
import l2p.gameserver.ThreadPoolManager;
import l2p.gameserver.configs.proprties.AIConfig;
import l2p.gameserver.data.xml.holder.NpcHolder;
import l2p.gameserver.geodata.GeoEngine;
import l2p.gameserver.model.AggroList.AggroInfo;
import l2p.gameserver.model.Creature;
import l2p.gameserver.model.MinionList;
import l2p.gameserver.model.Playable;
import l2p.gameserver.model.Player;
import l2p.gameserver.model.Skill;
import l2p.gameserver.model.World;
import l2p.gameserver.model.WorldRegion;
import l2p.gameserver.model.Zone.ZoneType;
import l2p.gameserver.model.entity.SevenSigns;
import l2p.gameserver.model.instances.MinionInstance;
import l2p.gameserver.model.instances.MonsterInstance;
import l2p.gameserver.model.instances.NpcInstance;
import l2p.gameserver.model.quest.QuestEventType;
import l2p.gameserver.model.quest.QuestState;
import l2p.gameserver.serverpackets.MagicSkillUse;
import l2p.gameserver.serverpackets.StatusUpdate;
import l2p.gameserver.stats.Stats;
import l2p.gameserver.taskmanager.AiTaskManager;
import l2p.gameserver.utils.Location;
import l2p.gameserver.utils.NpcUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;
public class DefaultAI extends CharacterAI
{
protected static final Logger _log = LoggerFactory.getLogger(DefaultAI.class);
public static enum TaskType
{
MOVE,
ATTACK,
CAST,
BUFF
}
public static final int TaskDefaultWeight = 10000;
public static class Task
{
public TaskType type;
public Skill skill;
public HardReference
target;
public Location loc;
public boolean pathfind;
public int weight = TaskDefaultWeight;
}
public void addTaskCast(Creature target, Skill skill)
{
Task task = new Task();
task.type = TaskType.CAST;
task.target = target.getRef();
task.skill = skill;
_tasks.add(task);
_def_think = true;
}
public void addTaskBuff(Creature target, Skill skill)
{
Task task = new Task();
task.type = TaskType.BUFF;
task.target = target.getRef();
task.skill = skill;
_tasks.add(task);
_def_think = true;
}
public void addTaskAttack(Creature target)
{
Task task = new Task();
task.type = TaskType.ATTACK;
task.target = target.getRef();
_tasks.add(task);
_def_think = true;
}
public void addTaskAttack(Creature target, Skill skill, int weight)
{
Task task = new Task();
task.type = skill.isOffensive() ? TaskType.CAST : TaskType.BUFF;
task.target = target.getRef();
task.skill = skill;
task.weight = weight;
_tasks.add(task);
_def_think = true;
}
public void addTaskMove(Location loc, boolean pathfind)
{
Task task = new Task();
task.type = TaskType.MOVE;
task.loc = loc;
task.pathfind = pathfind;
_tasks.add(task);
_def_think = true;
}
protected void addTaskMove(int locX, int locY, int locZ, boolean pathfind)
{
addTaskMove(new Location(locX, locY, locZ), pathfind);
}
private static class TaskComparator implements Comparator
{
private static final Comparator instance = new TaskComparator();
public static Comparator getInstance()
{
return instance;
}
@Override
public int compare(Task o1, Task o2)
{
if(o1 == null || o2 == null)
return 0;
return o2.weight - o1.weight;
}
}
protected class Teleport extends RunnableImpl
{
Location _destination;
public Teleport(Location destination)
{
_destination = destination;
}
@Override
public void runImpl() throws Exception
{
NpcInstance actor = getActor();
if(actor != null)
actor.teleToLocation(_destination);
}
}
protected class RunningTask extends RunnableImpl
{
@Override
public void runImpl() throws Exception
{
NpcInstance actor = getActor();
if(actor != null)
actor.setRunning();
_runningTask = null;
}
}
protected class MadnessTask extends RunnableImpl
{
@Override
public void runImpl() throws Exception
{
NpcInstance actor = getActor();
if(actor != null)
actor.stopConfused();
_madnessTask = null;
}
}
protected class NearestTargetComparator implements Comparator
{
private final Creature actor;
public NearestTargetComparator(Creature actor)
{
this.actor = actor;
}
@Override
public int compare(Creature o1, Creature o2)
{
double diff = actor.getDistance3D(o1) - actor.getDistance3D(o2);
if(diff 0 ? 1 : 0;
}
}
protected long AI_TASK_ATTACK_DELAY = AIConfig.AiTaskDelay;
protected long AI_TASK_ACTIVE_DELAY = AIConfig.AiTaskActiveDelay;
protected long AI_TASK_DELAY_CURRENT = AI_TASK_ACTIVE_DELAY;
protected int MAX_PURSUE_RANGE;
protected ScheduledFuture
_aiTask;
protected ScheduledFuture
_runningTask;
protected ScheduledFuture
_madnessTask;
/** The flag used to indicate that a thinking action is in progress */
private boolean _thinking = false;
/** Показывает, есть ли задания */
protected boolean _def_think = false;
/** The L2NpcInstance aggro counter */
protected long _globalAggro;
protected long _randomAnimationEnd;
protected int _pathfindFails;
/** Список заданий */
protected final NavigableSet
_tasks = new ConcurrentSkipListSet<>(TaskComparator.getInstance());
protected final Skill[] _damSkills, _dotSkills, _debuffSkills, _healSkills, _buffSkills, _stunSkills;
protected long _lastActiveCheck;
protected long _checkAggroTimestamp = 0;
/** Время актуальности состояния атаки */
protected long _attackTimeout;
protected long _lastFactionNotifyTime = 0;
protected long _minFactionNotifyInterval = 10000;
protected final Comparator
_nearestTargetComparator;
public DefaultAI(NpcInstance actor)
{
super(actor);
setAttackTimeout(Long.MAX_VALUE);
NpcInstance npc = getActor();
_damSkills = npc.getTemplate().getDamageSkills();
_dotSkills = npc.getTemplate().getDotSkills();
_debuffSkills = npc.getTemplate().getDebuffSkills();
_buffSkills = npc.getTemplate().getBuffSkills();
_stunSkills = npc.getTemplate().getStunSkills();
_healSkills = npc.getTemplate().getHealSkills();
_nearestTargetComparator = new NearestTargetComparator(actor);
// Preload some AI params
MAX_PURSUE_RANGE = actor.getParameter("MaxPursueRange", actor.isRaid() ? AIConfig.MaxPursueRangeRaid : npc.isUnderground() ? AIConfig.MaxPursueUnderGroundRange : AIConfig.MaxPursueRange);
_minFactionNotifyInterval = actor.getParameter("FactionNotifyInterval", 10000);
}
@Override
public void runImpl() throws Exception
{
if(_aiTask == null)
return;
// проверяем, если NPC вышел в неактивный регион, отключаем AI
if(!isGlobalAI() && System.currentTimeMillis() - _lastActiveCheck > 60000L)
{
_lastActiveCheck = System.currentTimeMillis();
NpcInstance actor = getActor();
WorldRegion region = actor == null ? null : actor.getCurrentRegion();
if(region == null || !region.isActive())
{
stopAITask();
return;
}
}
onEvtThink();
}
@Override
//public final synchronized void startAITask()
public synchronized void startAITask()
{
if(_aiTask == null)
{
AI_TASK_DELAY_CURRENT = AI_TASK_ACTIVE_DELAY;
_aiTask = AiTaskManager.getInstance().scheduleAtFixedRate(this, 0L, AI_TASK_DELAY_CURRENT);
}
}
//protected final synchronized void switchAITask(long NEW_DELAY)
protected synchronized void switchAITask(long NEW_DELAY)
{
if(_aiTask == null)
return;
if(AI_TASK_DELAY_CURRENT != NEW_DELAY)
{
_aiTask.cancel(false);
AI_TASK_DELAY_CURRENT = NEW_DELAY;
_aiTask = AiTaskManager.getInstance().scheduleAtFixedRate(this, 0L, AI_TASK_DELAY_CURRENT);
}
}
@Override
public final synchronized void stopAITask()
{
if(_aiTask != null)
{
_aiTask.cancel(false);
_aiTask = null;
}
}
/**
* Определяет, может ли этот тип АИ видеть персонажей в режиме Silent Move.
* @param target L2Playable цель
* @return true если цель видна в режиме Silent Move
*/
protected boolean canSeeInSilentMove(Playable target) {
return getActor().getParameter("canSeeInSilentMove", false) || !target.isSilentMoving();
}
protected boolean canSeeInHide(Playable target)
{
return getActor().getParameter("canSeeInHide", false) || !target.isInvisible();
}
protected boolean checkAggression(Creature target)
{
NpcInstance actor = getActor();
if(getIntention() != CtrlIntention.AI_INTENTION_ACTIVE || !isGlobalAggro())
return false;
if(target.isAlikeDead())
return false;
if(target.isNpc() && target.isInvul())
return false;
if(target.isPlayer() && target.getPlayer().isInAwayingMode() && !Config.AWAY_PLAYER_TAKE_AGGRO)
return false;
if(target.isPlayable())
{
if(!canSeeInSilentMove((Playable) target))
return false;
if(!canSeeInHide((Playable) target))
return false;
if(actor.getFaction().getName().equalsIgnoreCase("varka_silenos_clan") && target.getPlayer().getVarka() > 0)
return false;
if(actor.getFaction().getName().equalsIgnoreCase("ketra_orc_clan") && target.getPlayer().getKetra() > 0)
return false;
/*if(target.isFollow && !target.isPlayer() && target.getFollowTarget() != null && target.getFollowTarget().isPlayer())
return;*/
if(target.isPlayer() && ((Player) target).isGM() && target.isInvisible())
return false;
if(((Playable) target).getNonAggroTime() > System.currentTimeMillis())
return false;
if(target.isPlayer() && !target.getPlayer().isActive())
return false;
if(actor.isMonster() && target.isInZonePeace())
return false;
}
AggroInfo ai = actor.getAggroList().get(target);
if(ai != null && ai.hate > 0)
{
if(!target.isInRangeZ(actor.getSpawnedLoc(), MAX_PURSUE_RANGE))
return false;
}
else if(!actor.isAggressive() || !target.isInRangeZ(actor.getSpawnedLoc(), actor.getAggroRange()))
return false;
if(!canAttackCharacter(target))
return false;
if(!GeoEngine.canSeeTarget(actor, target, false))
return false;
actor.getAggroList().addDamageHate(target, 0, 2);
if((target.isSummon() || target.isPet()))
actor.getAggroList().addDamageHate(target.getPlayer(), 0, 1);
startRunningTask(AI_TASK_ATTACK_DELAY);
setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
return true;
}
protected void setIsInRandomAnimation(long time)
{
_randomAnimationEnd = System.currentTimeMillis() + time;
}
protected boolean randomAnimation()
{
NpcInstance actor = getActor();
if(actor.getParameter("noRandomAnimation", false))
return false;
if(actor.hasRandomAnimation() && !actor.isActionsDisabled() && !actor.isMoving && !actor.isInCombat() && Rnd.chance(AIConfig.RndAnimationRate))
{
setIsInRandomAnimation(3000);
actor.onRandomAnimation();
return true;
}
return false;
}
protected boolean randomWalk() {
NpcInstance actor = getActor();
return !actor.getParameter("noRandomWalk", false) && !actor.isMoving && maybeMoveToHome();
}
/**
* @return true если действие выполнено, false если нет
*/
protected boolean thinkActive() {
NpcInstance actor = getActor();
if (actor.isActionsDisabled())
return true;
if (_randomAnimationEnd > System.currentTimeMillis())
return true;
if (_def_think) {
if (doTask())
clearTasks();
return true;
}
long now = System.currentTimeMillis();
if (now - _checkAggroTimestamp > AIConfig.AggroCheckInterval) {
_checkAggroTimestamp = now;
boolean aggressive = Rnd.chance(actor.getParameter("SelfAggressive", actor.isAggressive() ? 100 : 0));
if (!actor.getAggroList().isEmpty() || aggressive) {
List
chars = World.getAroundCharacters(actor);
CollectionUtils.eqSort(chars, _nearestTargetComparator);
for (Creature cha : chars) {
if (aggressive || actor.getAggroList().get(cha) != null)
if (checkAggression(cha))
return true;
}
}
}
if (actor.isMinion()) {
MonsterInstance leader = ((MinionInstance) actor).getLeader();
if (leader != null) {
double distance = actor.getDistance(leader.getX(), leader.getY());
if (distance > 1000)
actor.teleToLocation(leader.getMinionPosition());
else if (distance > 200)
addTaskMove(leader.getMinionPosition(), false);
return true;
}
}
return randomAnimation() || randomWalk();
}
@Override
protected void onIntentionIdle()
{
NpcInstance actor = getActor();
// Удаляем все задания
clearTasks();
actor.stopMove();
actor.getAggroList().clear(true);
setAttackTimeout(Long.MAX_VALUE);
setAttackTarget(null);
changeIntention(CtrlIntention.AI_INTENTION_IDLE, null, null);
}
@Override
protected void onIntentionActive()
{
NpcInstance actor = getActor();
actor.stopMove();
setAttackTimeout(Long.MAX_VALUE);
if(getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)
{
switchAITask(AI_TASK_ACTIVE_DELAY);
changeIntention(CtrlIntention.AI_INTENTION_ACTIVE, null, null);
}
onEvtThink();
}
@Override
protected void onIntentionAttack(Creature target)
{
NpcInstance actor = getActor();
// Удаляем все задания
clearTasks();
actor.stopMove();
setAttackTarget(target);
setAttackTimeout(getMaxAttackTimeout() + System.currentTimeMillis());
setGlobalAggro(0);
if(getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
{
changeIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
switchAITask(AI_TASK_ATTACK_DELAY);
}
onEvtThink();
}
protected boolean canAttackCharacter(Creature target)
{
return target.isPlayable();
}
protected boolean checkTarget(Creature target, int range)
{
NpcInstance actor = getActor();
if(target == null || target.isAlikeDead() || !actor.isInRangeZ(target, range))
return false;
// если не видим чаров в хайде - не атакуем их
final boolean hided = target.isPlayable() && !canSeeInHide((Playable)target);
if(!hided && actor.isConfused())
return true;
//В состоянии атаки атакуем всех, на кого у нас есть хейт
if(getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
{
AggroInfo ai = actor.getAggroList().get(target);
if (ai != null)
{
if (hided)
{
ai.hate = 0; // очищаем хейт
return false;
}
return ai.hate > 0;
}
return false;
}
return canAttackCharacter(target);
}
public void setAttackTimeout(long time)
{
_attackTimeout = time;
}
protected long getAttackTimeout()
{
return _attackTimeout;
}
protected void thinkAttack()
{
NpcInstance actor = getActor();
if(actor.isDead())
return;
Location loc = actor.getSpawnedLoc();
if(!actor.isInRange(loc, MAX_PURSUE_RANGE))
{
returnHome();
return;
}
if(doTask() && !actor.isAttackingNow() && !actor.isCastingNow())
{
if(!createNewTask())
{
if(System.currentTimeMillis() > getAttackTimeout())
returnHome();
}
}
}
@Override
protected void onEvtSpawn()
{
setGlobalAggro(System.currentTimeMillis() + getActor().getParameter("globalAggro", 10000L));
setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
}
@Override
protected void onEvtReadyToAct()
{
onEvtThink();
}
@Override
protected void onEvtArrivedTarget()
{
onEvtThink();
}
@Override
protected void onEvtArrived()
{
onEvtThink();
}
protected boolean tryMoveToTarget(Creature target)
{
NpcInstance actor = getActor();
if(target.isInvisible())
{
notifyEvent(CtrlEvent.EVT_THINK);
return false;
}
if(!actor.followToCharacter(target, actor.getPhysicalAttackRange(), true))
_pathfindFails++;
if(_pathfindFails >= getMaxPathfindFails() && (System.currentTimeMillis() > getAttackTimeout() - getMaxAttackTimeout() + getTeleportTimeout()) && actor.isInRange(target, MAX_PURSUE_RANGE))
{
_pathfindFails = 0;
if(target.isPlayable())
{
AggroInfo hate = actor.getAggroList().get(target);
if(hate == null || hate.hate System.currentTimeMillis())
return;
if(actor.isRaid() && (actor.isInZonePeace() || actor.isInZoneBattle() || actor.isInZone(ZoneType.SIEGE)))
{
teleportHome();
return;
}
_thinking = true;
try
{
if(!AIConfig.BlockActiveTasks && getIntention() == CtrlIntention.AI_INTENTION_ACTIVE)
thinkActive();
else if(getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
thinkAttack();
}
finally
{
_thinking = false;
}
}
@Override
protected void onEvtDead(Creature killer)
{
NpcInstance actor = getActor();
int transformer = actor.getParameter("transformOnDead", 0);
int chance = actor.getParameter("transformChance", 100);
if(transformer > 0 && Rnd.chance(chance))
{
NpcInstance npc = NpcUtils.spawnSingle(transformer, actor.getLoc(), actor.getReflection()) ;
if(killer != null && killer.isPlayable())
{
npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, killer, 100);
killer.setTarget(npc);
killer.sendPacket(npc.makeStatusUpdate(StatusUpdate.CUR_HP, StatusUpdate.MAX_HP));
}
}
super.onEvtDead(killer);
}
@Override
protected void onEvtClanAttacked(Creature attacked, Creature attacker, int damage)
{
if(getIntention() != CtrlIntention.AI_INTENTION_ACTIVE || !isGlobalAggro())
return;
notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, 2);
}
@Override
protected void onEvtAttacked(Creature attacker, int damage)
{
NpcInstance actor = getActor();
if(attacker == null || actor.isDead())
return;
int transformer = actor.getParameter("transformOnUnderAttack", 0);
if(transformer > 0)
{
int chance = actor.getParameter("transformChance", 5);
if(chance == 100 || ((MonsterInstance) actor).getChampion() == 0 && actor.getCurrentHpPercents() > 50 && Rnd.chance(chance))
{
MonsterInstance npc = (MonsterInstance) NpcHolder.getInstance().getTemplate(transformer).getNewInstance();
npc.setSpawnedLoc(actor.getLoc());
npc.setReflection(actor.getReflection());
npc.setChampion(((MonsterInstance) actor).getChampion());
npc.setCurrentHpMp(npc.getMaxHp(), npc.getMaxMp(), true);
npc.spawnMe(npc.getSpawnedLoc());
npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, 100);
actor.doDie(actor);
actor.decayMe();
attacker.setTarget(npc);
attacker.sendPacket(npc.makeStatusUpdate(StatusUpdate.CUR_HP, StatusUpdate.MAX_HP));
return;
}
}
Player player = attacker.getPlayer();
if(player != null)
{
//FIXME [G1ta0] затычка для 7 печатей, при атаке монстра 7 печатей телепортирует персонажа в ближайший город
if((SevenSigns.getInstance().isSealValidationPeriod() || SevenSigns.getInstance().isCompResultsPeriod()) && actor.isSevenSignsMonster())
{
int pcabal = SevenSigns.getInstance().getPlayerCabal(player);
int wcabal = SevenSigns.getInstance().getCabalHighestScore();
if(pcabal != wcabal && wcabal != SevenSigns.CABAL_NULL)
{
player.sendMessage("You have been teleported to the nearest town because you not signed for winning cabal.");
player.teleToClosestTown();
return;
}
}
List
quests = player.getQuestsForEvent(actor, QuestEventType.ATTACKED_WITH_QUEST);
if(quests != null)
for(QuestState qs : quests)
qs.getQuest().notifyAttack(actor, qs);
}
//Добавляем только хейт, урон, если атакующий - игровой персонаж, будет добавлен в L2NpcInstance.onReduceCurrentHp
actor.getAggroList().addDamageHate(attacker, 0, damage);
// Обычно 1 хейт добавляется хозяину суммона, чтобы после смерти суммона моб накинулся на хозяина.
if(damage > 0 && (attacker.isSummon() || attacker.isPet()))
actor.getAggroList().addDamageHate(attacker.getPlayer(), 0, actor.getParameter("searchingMaster", false) ? damage : 1);
if(getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
{
if(!actor.isRunning())
startRunningTask(AI_TASK_ATTACK_DELAY);
setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
}
notifyFriends(attacker, damage);
}
@Override
protected void onEvtAggression(Creature attacker, int aggro)
{
NpcInstance actor = getActor();
if(attacker == null || actor.isDead())
return;
actor.getAggroList().addDamageHate(attacker, 0, aggro);
// Обычно 1 хейт добавляется хозяину суммона, чтобы после смерти суммона моб накинулся на хозяина.
if(aggro > 0 && (attacker.isSummon() || attacker.isPet()))
actor.getAggroList().addDamageHate(attacker.getPlayer(), 0, actor.getParameter("searchingMaster", false) ? aggro : 1);
if(getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
{
if(!actor.isRunning())
startRunningTask(AI_TASK_ATTACK_DELAY);
setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
}
}
protected boolean maybeMoveToHome()
{
NpcInstance actor = getActor();
if(actor.isDead())
return false;
boolean randomWalk = actor.hasRandomWalk();
Location sloc = actor.getSpawnedLoc();
// Random walk or not?
if(randomWalk && (!AIConfig.RndWalk || !Rnd.chance(AIConfig.RndWalkRate)))
return false;
boolean isInRange = actor.isInRangeZ(sloc, AIConfig.MaxDriftRange);
if(!randomWalk && isInRange)
return false;
Location pos = Location.findPointToStay(actor, sloc, 0, AIConfig.MaxDriftRange);
actor.setWalking();
// Телепортируемся домой, только если далеко от дома
if(!actor.moveToLocation(pos.x, pos.y, pos.z, 0, true) && !isInRange)
teleportHome();
return true;
}
protected void returnHome()
{
returnHome(true, false);
}
protected void teleportHome()
{
returnHome(true, true);
}
protected void returnHome(boolean clearAggro, boolean teleport)
{
NpcInstance actor = getActor();
Location sloc = actor.getSpawnedLoc();
// Удаляем все задания
clearTasks();
actor.stopMove();
if(clearAggro)
actor.getAggroList().clear(true);
setAttackTimeout(Long.MAX_VALUE);
setAttackTarget(null);
changeIntention(CtrlIntention.AI_INTENTION_ACTIVE, null, null);
if(teleport)
{
actor.broadcastPacketToOthers(new MagicSkillUse(actor, actor, 2036, 1, 500, 0));
actor.teleToLocation(sloc.x, sloc.y, GeoEngine.getHeight(sloc, actor.getGeoIndex()));
}
else
{
if(!clearAggro)
actor.setRunning();
else
actor.setWalking();
addTaskMove(sloc, false);
}
}
protected Creature prepareTarget()
{
NpcInstance actor = getActor();
if(actor.isConfused())
return getAttackTarget();
// Для "двинутых" боссов, иногда, выбираем случайную цель
if(Rnd.chance(actor.getParameter("isMadness", 0)))
{
Creature randomHated = actor.getAggroList().getRandomHated();
if(randomHated != null)
{
setAttackTarget(randomHated);
if(_madnessTask == null && !actor.isConfused())
{
actor.startConfused();
_madnessTask = ThreadPoolManager.getInstance().schedule(new MadnessTask(), 10000);
}
return randomHated;
}
}
// Новая цель исходя из агрессивности
List
hateList = actor.getAggroList().getHateList();
Creature hated = null;
for(Creature cha : hateList)
{
// Если у Монстра есть скилл "Searching Master" он должен атаковать хозяина пета в первую очередь.
if((cha.isPet() || cha.isSummon()) && cha.getPlayer() != null)
if(getActor().getSkillLevel(6019) == 1 && checkTarget(cha.getPlayer(), MAX_PURSUE_RANGE))
cha = cha.getPlayer();
//Не подходит, очищаем хейт
if(!checkTarget(cha, MAX_PURSUE_RANGE))
{
actor.getAggroList().remove(cha, true);
continue;
}
hated = cha;
break;
}
if(hated != null)
{
setAttackTarget(hated);
return hated;
}
return null;
}
protected boolean canUseSkill(Skill skill, Creature target, double distance)
{
NpcInstance actor = getActor();
if(skill == null || skill.isNotUsedByAI())
return false;
if(skill.getTargetType() == Skill.SkillTargetType.TARGET_SELF && target != actor)
return false;
int castRange = skill.getAOECastRange();
if(castRange 200)
return false;
if(actor.isSkillDisabled(skill) || actor.isMuted(skill) || actor.isUnActiveSkill(skill.getId()))
return false;
double mpConsume2 = skill.getMpConsume2();
if(skill.isMagic())
mpConsume2 = actor.calcStat(Stats.MP_MAGIC_SKILL_CONSUME, mpConsume2, target, skill);
else
mpConsume2 = actor.calcStat(Stats.MP_PHYSICAL_SKILL_CONSUME, mpConsume2, target, skill);
if(actor.getCurrentMp()
rnd = new RndSelector<>(skills.length);
double weight;
for(Skill skill : skills)
{
weight = skill.getSimpleDamage(actor, target) * skill.getAOECastRange() / distance;
if(weight
rnd = new RndSelector<>(skills.length);
double weight;
for(Skill skill : skills)
{
if(skill.getSameByStackType(target) != null)
continue;
if((weight = 100. * skill.getAOECastRange() / distance)
rnd = new RndSelector<>(skills.length);
double weight;
for(Skill skill : skills)
{
if(skill.getSameByStackType(target) != null)
continue;
if((weight = skill.getPower())
rnd = new RndSelector<>(skills.length);
double weight;
for(Skill skill : skills)
{
if((weight = Math.abs(skill.getPower() - hpReduced))
skillMap, Creature target, double distance, Skill[] skills)
{
if(skills == null || skills.length == 0 || target == null)
return;
for(Skill sk : skills)
addDesiredSkill(skillMap, target, distance, sk);
}
protected void addDesiredSkill(Map
skillMap, Creature target, double distance, Skill skill)
{
if(skill == null || target == null || !canUseSkill(skill, target))
return;
int weight = (int) -Math.abs(skill.getAOECastRange() - distance);
if(skill.getAOECastRange() >= distance)
weight += 1000000;
else if(skill.isNotTargetAoE() && skill.getTargets(getActor(), target, false).size() == 0)
return;
skillMap.put(skill, weight);
}
protected void addDesiredHeal(Map
skillMap, Skill[] skills)
{
if(skills == null || skills.length == 0)
return;
NpcInstance actor = getActor();
double hpReduced = actor.getMaxHp() - actor.getCurrentHp();
double hpPercent = actor.getCurrentHpPercents();
if(hpReduced
skillMap, Skill[] skills)
{
if(skills == null || skills.length == 0)
return;
NpcInstance actor = getActor();
for(Skill sk : skills)
if(canUseSkill(sk, actor))
skillMap.put(sk, 1000000);
}
protected Skill selectTopSkill(Map
skillMap)
{
if(skillMap == null || skillMap.isEmpty())
return null;
int nWeight, topWeight = Integer.MIN_VALUE;
for(Skill next : skillMap.keySet())
if((nWeight = skillMap.get(next)) > topWeight)
topWeight = nWeight;
if(topWeight == Integer.MIN_VALUE)
return null;
Skill[] skills = new Skill[skillMap.size()];
nWeight = 0;
for(Map.Entry
e : skillMap.entrySet())
{
if(e.getValue() skill.getAOECastRange() + 60)
{
target = null;
if(skill.isOffensive())
{
LazyArrayList
targets = LazyArrayList.newInstance();
for(Creature cha : actor.getAggroList().getHateList())
{
if(!checkTarget(cha, skill.getAOECastRange() + 60) || !canUseSkill(skill, cha))
continue;
targets.add(cha);
}
if(!targets.isEmpty())
target = targets.get(Rnd.get(targets.size()));
LazyArrayList.recycle(targets);
}
}
if(target == null)
return false;
// Добавить новое задание
if(skill.isOffensive())
addTaskCast(target, skill);
else
addTaskBuff(target, skill);
return true;
}
// Смена цели, если необходимо
if(actor.isMovementDisabled() && distance > actor.getPhysicalAttackRange() + 40)
{
target = null;
LazyArrayList
targets = LazyArrayList.newInstance();
for(Creature cha : actor.getAggroList().getHateList())
{
if(!checkTarget(cha, actor.getPhysicalAttackRange() + 40))
continue;
targets.add(cha);
}
if(!targets.isEmpty())
target = targets.get(Rnd.get(targets.size()));
LazyArrayList.recycle(targets);
}
if(target == null)
return false;
// Добавить новое задание
addTaskAttack(target);
return true;
}
@Override
public boolean isActive()
{
return _aiTask != null;
}
protected void clearTasks()
{
_def_think = false;
_tasks.clear();
}
/** переход в режим бега через определенный интервал времени */
protected void startRunningTask(long interval)
{
NpcInstance actor = getActor();
if(actor != null && _runningTask == null && !actor.isRunning())
_runningTask = ThreadPoolManager.getInstance().schedule(new RunningTask(), interval);
}
protected boolean isGlobalAggro()
{
if(_globalAggro == 0)
return true;
if(_globalAggro _minFactionNotifyInterval)
{
_lastFactionNotifyTime = System.currentTimeMillis();
if(actor.isMinion())
{
//Оповестить лидера об атаке
MonsterInstance master = ((MinionInstance) actor).getLeader();
if(master != null)
{
if(!master.isDead() && master.isVisible())
master.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, damage);
//Оповестить минионов лидера об атаке
MinionList minionList = master.getMinionList();
if(minionList != null)
minionList.getAliveMinions().stream().filter(minion -> minion != actor).forEach(minion -> minion.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, damage));
}
}
//Оповестить своих минионов об атаке
MinionList minionList = actor.getMinionList();
if(minionList != null && minionList.hasAliveMinions())
for(MinionInstance minion : minionList.getAliveMinions())
minion.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, damage);
//Оповестить социальных мобов
for(NpcInstance npc : activeFactionTargets())
npc.getAI().notifyEvent(CtrlEvent.EVT_CLAN_ATTACKED, new Object[] { actor, attacker, damage });
}
}
protected List
activeFactionTargets()
{
NpcInstance actor = getActor();
if(actor.getFaction().isNone())
return Collections.emptyList();
return World.getAroundNpc(actor).stream().filter(npc -> !npc.isDead()).filter(npc -> npc.isInFaction(actor)).filter(npc -> npc.isInRangeZ(actor, npc.getFaction().getRange())).filter(npc -> GeoEngine.canSeeTarget(npc, actor, false)).collect(Collectors.toCollection(() -> new LazyArrayList<>()));
}
protected boolean defaultThinkBuff(int rateSelf, int rateFriends)
{
NpcInstance actor = getActor();
if(actor.isDead())
return true;
//TODO сделать более разумный выбор баффа, сначала выбирать подходящие а потом уже рандомно 1 из них
if(Rnd.chance(rateSelf))
{
double actorHp = actor.getCurrentHpPercents();
Skill[] skills = actorHp 10 ? Rnd.chance(getRateDEBUFF()) ? selectUsableSkills(target, distance, _debuffSkills) : null : null;
Skill[] stun = Rnd.chance(getRateSTUN()) ? selectUsableSkills(target, distance, _stunSkills) : null;
Skill[] heal = actorHp
rnd = new RndSelector<>();
if(!actor.isAMuted())
rnd.add(null, getRatePHYS());
rnd.add(dam, getRateDAM());
rnd.add(dot, getRateDOT());
rnd.add(debuff, getRateDEBUFF());
rnd.add(heal, getRateHEAL());
rnd.add(buff, getRateBUFF());
rnd.add(stun, getRateSTUN());
Skill[] selected = rnd.select();
if(selected != null)
{
if(selected == dam || selected == dot)
return chooseTaskAndTargets(selectTopSkillByDamage(actor, target, distance, selected), target, distance);
if(selected == debuff || selected == stun)
return chooseTaskAndTargets(selectTopSkillByDebuff(target, distance, selected), target, distance);
if(selected == buff)
return chooseTaskAndTargets(selectTopSkillByBuff(actor, selected), actor, distance);
if(selected == heal)
return chooseTaskAndTargets(selectTopSkillByHeal(actor, selected), actor, distance);
}
// TODO сделать лечение и баф дружественных целей
return chooseTaskAndTargets(null, target, distance);
}
public int getRatePHYS()
{
return 100;
}
public int getRateDOT()
{
return 0;
}
public int getRateDEBUFF()
{
return 0;
}
public int getRateDAM()
{
return 0;
}
public int getRateSTUN()
{
return 0;
}
public int getRateBUFF()
{
return 0;
}
public int getRateHEAL()
{
return 0;
}
public boolean getIsMobile()
{
return !getActor().getParameter("isImmobilized", false);
}
public int getMaxPathfindFails()
{
return 3;
}
/**
* Задержка, перед переключением в активный режим после атаки, если цель не найдена (вне зоны досягаемости, убита, очищен хейт)
* @return
*/
public int getMaxAttackTimeout()
{
return 15000;
}
/**
* Задержка, перед телепортом к цели, если не удается дойти
* @return
*/
public int getTeleportTimeout()
{
return 10000;
}
}
Сообщение от Спойлер
PHP:
Код:
package l2p.gameserver.ai;
import static java.lang.Integer.MIN_VALUE;
import static java.lang.Long.MAX_VALUE;
import static java.lang.Math.abs;
import static java.lang.System.currentTimeMillis;
import static java.util.Arrays.copyOf;
import static java.util.Collections.emptyList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;
import static l2p.commons.collections.CollectionUtils.eqSort;
import l2p.commons.collections.LazyArrayList;
import static l2p.commons.collections.LazyArrayList.newInstance;
import static l2p.commons.collections.LazyArrayList.recycle;
import l2p.commons.lang.reference.HardReference;
import l2p.commons.math.random.RndSelector;
import l2p.commons.threading.RunnableImpl;
import static l2p.commons.util.Rnd.chance;
import static l2p.commons.util.Rnd.get;
import static l2p.gameserver.ai.CtrlEvent.EVT_AGGRESSION;
import static l2p.gameserver.ai.CtrlEvent.EVT_CLAN_ATTACKED;
import static l2p.gameserver.ai.CtrlEvent.EVT_THINK;
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_IDLE;
import static l2p.gameserver.ai.DefaultAI.TaskType.ATTACK;
import static l2p.gameserver.ai.DefaultAI.TaskType.BUFF;
import static l2p.gameserver.ai.DefaultAI.TaskType.CAST;
import static l2p.gameserver.ai.DefaultAI.TaskType.MOVE;
import static l2p.gameserver.configs.Config.AWAY_PLAYER_TAKE_AGGRO;
import static l2p.gameserver.configs.proprties.AIConfig.AggroCheckInterval;
import static l2p.gameserver.configs.proprties.AIConfig.AiTaskActiveDelay;
import static l2p.gameserver.configs.proprties.AIConfig.AiTaskDelay;
import static l2p.gameserver.configs.proprties.AIConfig.BlockActiveTasks;
import static l2p.gameserver.configs.proprties.AIConfig.MaxDriftRange;
import static l2p.gameserver.configs.proprties.AIConfig.MaxPursueRange;
import static l2p.gameserver.configs.proprties.AIConfig.MaxPursueRangeRaid;
import static l2p.gameserver.configs.proprties.AIConfig.MaxPursueUnderGroundRange;
import static l2p.gameserver.configs.proprties.AIConfig.RndAnimationRate;
import static l2p.gameserver.configs.proprties.AIConfig.RndWalk;
import static l2p.gameserver.configs.proprties.AIConfig.RndWalkRate;
import static l2p.gameserver.data.xml.holder.NpcHolder.getInstance;
import static l2p.gameserver.geodata.GeoEngine.canMoveToCoord;
import static l2p.gameserver.geodata.GeoEngine.canSeeTarget;
import static l2p.gameserver.geodata.GeoEngine.getHeight;
import static l2p.gameserver.geodata.GeoEngine.moveCheckForAI;
import l2p.gameserver.model.AggroList.AggroInfo;
import l2p.gameserver.model.Creature;
import l2p.gameserver.model.MinionList;
import l2p.gameserver.model.Playable;
import l2p.gameserver.model.Player;
import l2p.gameserver.model.Skill;
import static l2p.gameserver.model.Skill.SkillTargetType.TARGET_AURA;
import static l2p.gameserver.model.Skill.SkillTargetType.TARGET_SELF;
import static l2p.gameserver.model.World.getAroundCharacters;
import static l2p.gameserver.model.World.getAroundNpc;
import l2p.gameserver.model.WorldRegion;
import static l2p.gameserver.model.Zone.ZoneType.SIEGE;
import static l2p.gameserver.model.entity.SevenSigns.CABAL_NULL;
import l2p.gameserver.model.instances.MinionInstance;
import l2p.gameserver.model.instances.MonsterInstance;
import l2p.gameserver.model.instances.NpcInstance;
import static l2p.gameserver.model.quest.QuestEventType.ATTACKED_WITH_QUEST;
import l2p.gameserver.model.quest.QuestState;
import l2p.gameserver.serverpackets.MagicSkillUse;
import static l2p.gameserver.serverpackets.StatusUpdate.CUR_HP;
import static l2p.gameserver.serverpackets.StatusUpdate.MAX_HP;
import static l2p.gameserver.stats.Stats.MP_MAGIC_SKILL_CONSUME;
import static l2p.gameserver.stats.Stats.MP_PHYSICAL_SKILL_CONSUME;
import l2p.gameserver.utils.Location;
import static l2p.gameserver.utils.Location.findPointToStay;
import static l2p.gameserver.utils.NpcUtils.spawnSingle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class DefaultAI extends CharacterAI
{
protected static final Logger _log = LoggerFactory.getLogger(DefaultAI.class);
/**
* @return the AI_TASK_ATTACK_DELAY
*/
public long getAI_TASK_ATTACK_DELAY() {
return AI_TASK_ATTACK_DELAY;
}
/**
* @param AI_TASK_ATTACK_DELAY the AI_TASK_ATTACK_DELAY to set
*/
public void setAI_TASK_ATTACK_DELAY(long AI_TASK_ATTACK_DELAY) {
this.AI_TASK_ATTACK_DELAY = AI_TASK_ATTACK_DELAY;
}
/**
* @return the AI_TASK_ACTIVE_DELAY
*/
public long getAI_TASK_ACTIVE_DELAY() {
return AI_TASK_ACTIVE_DELAY;
}
/**
* @param AI_TASK_ACTIVE_DELAY the AI_TASK_ACTIVE_DELAY to set
*/
public void setAI_TASK_ACTIVE_DELAY(long AI_TASK_ACTIVE_DELAY) {
this.AI_TASK_ACTIVE_DELAY = AI_TASK_ACTIVE_DELAY;
}
/**
* @return the AI_TASK_DELAY_CURRENT
*/
public long getAI_TASK_DELAY_CURRENT() {
return AI_TASK_DELAY_CURRENT;
}
/**
* @param AI_TASK_DELAY_CURRENT the AI_TASK_DELAY_CURRENT to set
*/
public void setAI_TASK_DELAY_CURRENT(long AI_TASK_DELAY_CURRENT) {
this.AI_TASK_DELAY_CURRENT = AI_TASK_DELAY_CURRENT;
}
/**
* @return the MAX_PURSUE_RANGE
*/
public int getMAX_PURSUE_RANGE() {
return MAX_PURSUE_RANGE;
}
/**
* @param MAX_PURSUE_RANGE the MAX_PURSUE_RANGE to set
*/
public void setMAX_PURSUE_RANGE(int MAX_PURSUE_RANGE) {
this.MAX_PURSUE_RANGE = MAX_PURSUE_RANGE;
}
/**
* @return the _aiTask
*/
public ScheduledFuture
getAiTask() {
return _aiTask;
}
/**
* @param _aiTask the _aiTask to set
*/
public void setAiTask(ScheduledFuture
_aiTask) {
this._aiTask = _aiTask;
}
/**
* @return the _runningTask
*/
public ScheduledFuture
getRunningTask() {
return _runningTask;
}
/**
* @param _runningTask the _runningTask to set
*/
public void setRunningTask(ScheduledFuture
_runningTask) {
this._runningTask = _runningTask;
}
/**
* @return the _madnessTask
*/
public ScheduledFuture
getMadnessTask() {
return _madnessTask;
}
/**
* @param _madnessTask the _madnessTask to set
*/
public void setMadnessTask(ScheduledFuture
_madnessTask) {
this._madnessTask = _madnessTask;
}
/**
* @return the _thinking
*/
public boolean isThinking() {
return _thinking;
}
/**
* @param _thinking the _thinking to set
*/
public void setThinking(boolean _thinking) {
this._thinking = _thinking;
}
/**
* @return the _def_think
*/
public boolean isDef_think() {
return _def_think;
}
/**
* @param _def_think the _def_think to set
*/
public void setDef_think(boolean _def_think) {
this._def_think = _def_think;
}
/**
* @return the _globalAggro
*/
public long getGlobalAggro() {
return _globalAggro;
}
/**
* @return the _randomAnimationEnd
*/
public long getRandomAnimationEnd() {
return _randomAnimationEnd;
}
/**
* @param _randomAnimationEnd the _randomAnimationEnd to set
*/
public void setRandomAnimationEnd(long _randomAnimationEnd) {
this._randomAnimationEnd = _randomAnimationEnd;
}
/**
* @return the _pathfindFails
*/
public int getPathfindFails() {
return _pathfindFails;
}
/**
* @param _pathfindFails the _pathfindFails to set
*/
public void setPathfindFails(int _pathfindFails) {
this._pathfindFails = _pathfindFails;
}
/**
* @return the _tasks
*/
public NavigableSet
getTasks() {
return _tasks;
}
/**
* @param _tasks the _tasks to set
*/
public void setTasks(NavigableSet
_tasks) {
this._tasks = _tasks;
}
/**
* @return the _damSkills
*/
public Skill[] getDamSkills() {
return _damSkills;
}
/**
* @param _damSkills the _damSkills to set
*/
public void setDamSkills(Skill[] _damSkills) {
this._damSkills = _damSkills;
}
/**
* @return the _dotSkills
*/
public Skill[] getDotSkills() {
return _dotSkills;
}
/**
* @param _dotSkills the _dotSkills to set
*/
public void setDotSkills(Skill[] _dotSkills) {
this._dotSkills = _dotSkills;
}
/**
* @return the _debuffSkills
*/
public Skill[] getDebuffSkills() {
return _debuffSkills;
}
/**
* @param _debuffSkills the _debuffSkills to set
*/
public void setDebuffSkills(Skill[] _debuffSkills) {
this._debuffSkills = _debuffSkills;
}
/**
* @return the _healSkills
*/
public Skill[] getHealSkills() {
return _healSkills;
}
/**
* @param _healSkills the _healSkills to set
*/
public void setHealSkills(Skill[] _healSkills) {
this._healSkills = _healSkills;
}
/**
* @return the _buffSkills
*/
public Skill[] getBuffSkills() {
return _buffSkills;
}
/**
* @param _buffSkills the _buffSkills to set
*/
public void setBuffSkills(Skill[] _buffSkills) {
this._buffSkills = _buffSkills;
}
/**
* @return the _stunSkills
*/
public Skill[] getStunSkills() {
return _stunSkills;
}
/**
* @param _stunSkills the _stunSkills to set
*/
public void setStunSkills(Skill[] _stunSkills) {
this._stunSkills = _stunSkills;
}
/**
* @return the _lastActiveCheck
*/
public long getLastActiveCheck() {
return _lastActiveCheck;
}
/**
* @param _lastActiveCheck the _lastActiveCheck to set
*/
public void setLastActiveCheck(long _lastActiveCheck) {
this._lastActiveCheck = _lastActiveCheck;
}
/**
* @return the _checkAggroTimestamp
*/
public long getCheckAggroTimestamp() {
return _checkAggroTimestamp;
}
/**
* @param _checkAggroTimestamp the _checkAggroTimestamp to set
*/
public void setCheckAggroTimestamp(long _checkAggroTimestamp) {
this._checkAggroTimestamp = _checkAggroTimestamp;
}
/**
* @return the _lastFactionNotifyTime
*/
public long getLastFactionNotifyTime() {
return _lastFactionNotifyTime;
}
/**
* @param _lastFactionNotifyTime the _lastFactionNotifyTime to set
*/
public void setLastFactionNotifyTime(long _lastFactionNotifyTime) {
this._lastFactionNotifyTime = _lastFactionNotifyTime;
}
/**
* @return the _minFactionNotifyInterval
*/
public long getMinFactionNotifyInterval() {
return _minFactionNotifyInterval;
}
/**
* @param _minFactionNotifyInterval the _minFactionNotifyInterval to set
*/
public void setMinFactionNotifyInterval(long _minFactionNotifyInterval) {
this._minFactionNotifyInterval = _minFactionNotifyInterval;
}
/**
* @return the _nearestTargetComparator
*/
public Comparator
getNearestTargetComparator() {
return _nearestTargetComparator;
}
/**
* @param _nearestTargetComparator the _nearestTargetComparator to set
*/
public void setNearestTargetComparator(Comparator
_nearestTargetComparator) {
this._nearestTargetComparator = _nearestTargetComparator;
}
public static enum TaskType
{
MOVE,
ATTACK,
CAST,
BUFF
}
public static final int TaskDefaultWeight = 10000;
public static class Task
{
private TaskType type;
private Skill skill;
private HardReference
target;
private Location loc;
private boolean pathfind;
private int weight = TaskDefaultWeight;
/**
* @return the type
*/
public TaskType getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(TaskType type) {
this.type = type;
}
/**
* @return the skill
*/
public Skill getSkill() {
return skill;
}
/**
* @param skill the skill to set
*/
public void setSkill(Skill skill) {
this.skill = skill;
}
/**
* @return the target
*/
public HardReference getTarget() {
return target;
}
/**
* @param target the target to set
*/
public void setTarget(HardReference target) {
this.target = target;
}
/**
* @return the loc
*/
public Location getLoc() {
return loc;
}
/**
* @param loc the loc to set
*/
public void setLoc(Location loc) {
this.loc = loc;
}
/**
* @return the pathfind
*/
public boolean isPathfind() {
return pathfind;
}
/**
* @param pathfind the pathfind to set
*/
public void setPathfind(boolean pathfind) {
this.pathfind = pathfind;
}
/**
* @return the weight
*/
public int getWeight() {
return weight;
}
/**
* @param weight the weight to set
*/
public void setWeight(int weight) {
this.weight = weight;
}
}
public void addTaskCast(Creature target, Skill skill)
{
Task task = new Task();
task.setType(CAST);
task.setTarget(target.getRef());
task.setSkill(skill);
getTasks().add(task);
setDef_think(true);
}
public void addTaskBuff(Creature target, Skill skill)
{
Task task = new Task();
task.setType(BUFF);
task.setTarget(target.getRef());
task.setSkill(skill);
getTasks().add(task);
setDef_think(true);
}
public void addTaskAttack(Creature target)
{
Task task = new Task();
task.setType(ATTACK);
task.setTarget(target.getRef());
getTasks().add(task);
setDef_think(true);
}
public void addTaskAttack(Creature target, Skill skill, int weight)
{
Task task = new Task();
task.setType(skill.isOffensive() ? CAST : BUFF);
task.setTarget(target.getRef());
task.setSkill(skill);
task.setWeight(weight);
getTasks().add(task);
setDef_think(true);
}
public void addTaskMove(Location loc, boolean pathfind)
{
Task task = new Task();
task.setType(MOVE);
task.setLoc(loc);
task.setPathfind(pathfind);
getTasks().add(task);
setDef_think(true);
}
protected void addTaskMove(int locX, int locY, int locZ, boolean pathfind)
{
addTaskMove(new Location(locX, locY, locZ), pathfind);
}
private static class TaskComparator implements Comparator
{
private static final Comparator instance = new TaskComparator();
public static Comparator getInstance()
{
return instance;
}
@Override
public int compare(Task o1, Task o2)
{
if(o1 == null || o2 == null)
return 0;
return o2.getWeight() - o1.getWeight();
}
}
protected class Teleport extends RunnableImpl
{
private Location _destination;
public Teleport(Location destination)
{
_destination = destination;
}
@Override
public void runImpl() throws Exception
{
NpcInstance actor = getActor();
if(actor != null)
actor.teleToLocation(getDestination());
}
/**
* @return the _destination
*/
public Location getDestination() {
return _destination;
}
/**
* @param _destination the _destination to set
*/
public void setDestination(Location _destination) {
this._destination = _destination;
}
}
protected class RunningTask extends RunnableImpl
{
@Override
public void runImpl() throws Exception
{
NpcInstance actor = getActor();
if(actor != null)
actor.setRunning();
setRunningTask(null);
}
}
protected class MadnessTask extends RunnableImpl
{
@Override
public void runImpl() throws Exception
{
NpcInstance actor = getActor();
if(actor != null)
actor.stopConfused();
setMadnessTask(null);
}
}
protected class NearestTargetComparator implements Comparator
{
private final Creature actor;
public NearestTargetComparator(Creature actor)
{
this.actor = actor;
}
@Override
public int compare(Creature o1, Creature o2)
{
double diff = actor.getDistance3D(o1) - actor.getDistance3D(o2);
if(diff 0 ? 1 : 0;
}
}
private long AI_TASK_ATTACK_DELAY = AiTaskDelay;
private long AI_TASK_ACTIVE_DELAY = AiTaskActiveDelay;
private long AI_TASK_DELAY_CURRENT = AI_TASK_ACTIVE_DELAY;
private int MAX_PURSUE_RANGE;
private ScheduledFuture
_aiTask;
private ScheduledFuture
_runningTask;
private ScheduledFuture
_madnessTask;
/** The flag used to indicate that a thinking action is in progress */
private boolean _thinking = false;
/** Показывает, есть ли задания */
private boolean _def_think = false;
/** The L2NpcInstance aggro counter */
private long _globalAggro;
private long _randomAnimationEnd;
private int _pathfindFails;
/** Список заданий */
private NavigableSet
_tasks = new ConcurrentSkipListSet<>(getInstance());
private Skill[] _damSkills;
private Skill[] _dotSkills;
/** Время актуальности состояния атаки */
private Skill[] _debuffSkills;
private Skill[] _healSkills;
private Skill[] _buffSkills;
private Skill[] _stunSkills;
private long _lastActiveCheck;
private long _checkAggroTimestamp = 0;
private long _attackTimeout;
private long _lastFactionNotifyTime = 0;
private long _minFactionNotifyInterval = 10000;
private Comparator
_nearestTargetComparator;
public DefaultAI(NpcInstance actor)
{
super(actor);
setAttackTimeout(MAX_VALUE);
NpcInstance npc = getActor();
_damSkills = npc.getTemplate().getDamageSkills();
_dotSkills = npc.getTemplate().getDotSkills();
_debuffSkills = npc.getTemplate().getDebuffSkills();
_buffSkills = npc.getTemplate().getBuffSkills();
_stunSkills = npc.getTemplate().getStunSkills();
_healSkills = npc.getTemplate().getHealSkills();
_nearestTargetComparator = new NearestTargetComparator(actor);
// Preload some AI params
MAX_PURSUE_RANGE = actor.getParameter("MaxPursueRange", actor.isRaid() ? MaxPursueRangeRaid : npc.isUnderground() ? MaxPursueUnderGroundRange : MaxPursueRange);
_minFactionNotifyInterval = actor.getParameter("FactionNotifyInterval", 10000);
}
@Override
public void runImpl() throws Exception
{
if(getAiTask() == null)
return;
// проверяем, если NPC вышел в неактивный регион, отключаем AI
if(!isGlobalAI() && currentTimeMillis() - getLastActiveCheck() > 60000L)
{
setLastActiveCheck(currentTimeMillis());
NpcInstance actor = getActor();
WorldRegion region = actor == null ? null : actor.getCurrentRegion();
if(region == null || !region.isActive())
{
stopAITask();
return;
}
}
onEvtThink();
}
@Override
//public final synchronized void startAITask()
public synchronized void startAITask()
{
if(getAiTask() == null)
{
setAI_TASK_DELAY_CURRENT(getAI_TASK_ACTIVE_DELAY());
setAiTask(getInstance().scheduleAtFixedRate(this, 0L, getAI_TASK_DELAY_CURRENT()));
}
}
//protected final synchronized void switchAITask(long NEW_DELAY)
protected synchronized void switchAITask(long NEW_DELAY)
{
if(getAiTask() == null)
return;
if(getAI_TASK_DELAY_CURRENT() != NEW_DELAY)
{
getAiTask().cancel(false);
setAI_TASK_DELAY_CURRENT(NEW_DELAY);
setAiTask(getInstance().scheduleAtFixedRate(this, 0L, getAI_TASK_DELAY_CURRENT()));
}
}
@Override
public final synchronized void stopAITask()
{
if(getAiTask() != null)
{
getAiTask().cancel(false);
setAiTask(null);
}
}
/**
* Определяет, может ли этот тип АИ видеть персонажей в режиме Silent Move.
* @param target L2Playable цель
* @return true если цель видна в режиме Silent Move
*/
protected boolean canSeeInSilentMove(Playable target) {
return getActor().getParameter("canSeeInSilentMove", false) || !target.isSilentMoving();
}
protected boolean canSeeInHide(Playable target)
{
return getActor().getParameter("canSeeInHide", false) || !target.isInvisible();
}
protected boolean checkAggression(Creature target)
{
NpcInstance actor = getActor();
if(getIntention() != AI_INTENTION_ACTIVE || !isGlobalAggro())
return false;
if(target.isAlikeDead())
return false;
if(target.isNpc() && target.isInvul())
return false;
if(target.isPlayer() && target.getPlayer().isInAwayingMode() && !AWAY_PLAYER_TAKE_AGGRO)
return false;
if(target.isPlayable())
{
if(!canSeeInSilentMove((Playable) target))
return false;
if(!canSeeInHide((Playable) target))
return false;
if(actor.getFaction().getName().equalsIgnoreCase("varka_silenos_clan") && target.getPlayer().getVarka() > 0)
return false;
if(actor.getFaction().getName().equalsIgnoreCase("ketra_orc_clan") && target.getPlayer().getKetra() > 0)
return false;
/*if(target.isFollow && !target.isPlayer() && target.getFollowTarget() != null && target.getFollowTarget().isPlayer())
return;*/
if(target.isPlayer() && ((Player) target).isGM() && target.isInvisible())
return false;
if(((Playable) target).getNonAggroTime() > currentTimeMillis())
return false;
if(target.isPlayer() && !target.getPlayer().isActive())
return false;
if(actor.isMonster() && target.isInZonePeace())
return false;
}
AggroInfo ai = actor.getAggroList().get(target);
if(ai != null && ai.hate > 0)
{
if(!target.isInRangeZ(actor.getSpawnedLoc(), MAX_PURSUE_RANGE))
return false;
}
else if(!actor.isAggressive() || !target.isInRangeZ(actor.getSpawnedLoc(), actor.getAggroRange()))
return false;
if(!canAttackCharacter(target))
return false;
if(!canSeeTarget(actor, target, false))
return false;
actor.getAggroList().addDamageHate(target, 0, 2);
if((target.isSummon() || target.isPet()))
actor.getAggroList().addDamageHate(target.getPlayer(), 0, 1);
startRunningTask(getAI_TASK_ATTACK_DELAY());
setIntention(AI_INTENTION_ATTACK, target);
return true;
}
protected void setIsInRandomAnimation(long time)
{
setRandomAnimationEnd(currentTimeMillis() + time);
}
protected boolean randomAnimation()
{
NpcInstance actor = getActor();
if(actor.getParameter("noRandomAnimation", false))
return false;
if(actor.hasRandomAnimation() && !actor.isActionsDisabled() && !actor.isMoving && !actor.isInCombat() && chance(RndAnimationRate))
{
setIsInRandomAnimation(3000);
actor.onRandomAnimation();
return true;
}
return false;
}
protected boolean randomWalk() {
NpcInstance actor = getActor();
return !actor.getParameter("noRandomWalk", false) && !actor.isMoving && maybeMoveToHome();
}
/**
* @return true если действие выполнено, false если нет
*/
protected boolean thinkActive() {
NpcInstance actor = getActor();
if (actor.isActionsDisabled())
return true;
if (getRandomAnimationEnd() > currentTimeMillis())
return true;
if (isDef_think()) {
if (doTask())
clearTasks();
return true;
}
long now = currentTimeMillis();
if (now - getCheckAggroTimestamp() > AggroCheckInterval) {
setCheckAggroTimestamp(now);
boolean aggressive = chance(actor.getParameter("SelfAggressive", actor.isAggressive() ? 100 : 0));
if (!actor.getAggroList().isEmpty() || aggressive) {
List
chars = getAroundCharacters(actor);
eqSort(chars, getNearestTargetComparator());
for (Creature cha : chars) {
if (aggressive || actor.getAggroList().get(cha) != null)
if (checkAggression(cha))
return true;
}
}
}
if (actor.isMinion()) {
MonsterInstance leader = ((MinionInstance) actor).getLeader();
if (leader != null) {
double distance = actor.getDistance(leader.getX(), leader.getY());
if (distance > 1000)
actor.teleToLocation(leader.getMinionPosition());
else if (distance > 200)
addTaskMove(leader.getMinionPosition(), false);
return true;
}
}
return randomAnimation() || randomWalk();
}
@Override
protected void onIntentionIdle()
{
NpcInstance actor = getActor();
// Удаляем все задания
clearTasks();
actor.stopMove();
actor.getAggroList().clear(true);
setAttackTimeout(MAX_VALUE);
setAttackTarget(null);
changeIntention(AI_INTENTION_IDLE, null, null);
}
@Override
protected void onIntentionActive()
{
NpcInstance actor = getActor();
actor.stopMove();
setAttackTimeout(MAX_VALUE);
if(getIntention() != AI_INTENTION_ACTIVE)
{
switchAITask(getAI_TASK_ACTIVE_DELAY());
changeIntention(AI_INTENTION_ACTIVE, null, null);
}
onEvtThink();
}
@Override
protected void onIntentionAttack(Creature target)
{
NpcInstance actor = getActor();
// Удаляем все задания
clearTasks();
actor.stopMove();
setAttackTarget(target);
setAttackTimeout(getMaxAttackTimeout() + currentTimeMillis());
setGlobalAggro(0);
if(getIntention() != AI_INTENTION_ATTACK)
{
changeIntention(AI_INTENTION_ATTACK, target, null);
switchAITask(getAI_TASK_ATTACK_DELAY());
}
onEvtThink();
}
protected boolean canAttackCharacter(Creature target)
{
return target.isPlayable();
}
protected boolean checkTarget(Creature target, int range)
{
NpcInstance actor = getActor();
if(target == null || target.isAlikeDead() || !actor.isInRangeZ(target, range))
return false;
// если не видим чаров в хайде - не атакуем их
final boolean hided = target.isPlayable() && !canSeeInHide((Playable)target);
if(!hided && actor.isConfused())
return true;
//В состоянии атаки атакуем всех, на кого у нас есть хейт
if(getIntention() == AI_INTENTION_ATTACK)
{
AggroInfo ai = actor.getAggroList().get(target);
if (ai != null)
{
if (hided)
{
ai.hate = 0; // очищаем хейт
return false;
}
return ai.hate > 0;
}
return false;
}
return canAttackCharacter(target);
}
public void setAttackTimeout(long time)
{
_attackTimeout = time;
}
protected long getAttackTimeout()
{
return _attackTimeout;
}
protected void thinkAttack()
{
NpcInstance actor = getActor();
if(actor.isDead())
return;
Location loc = actor.getSpawnedLoc();
if(!actor.isInRange(loc, MAX_PURSUE_RANGE))
{
returnHome();
return;
}
if(doTask() && !actor.isAttackingNow() && !actor.isCastingNow())
{
if(!createNewTask())
{
if(currentTimeMillis() > getAttackTimeout())
returnHome();
}
}
}
@Override
protected void onEvtSpawn()
{
setGlobalAggro(currentTimeMillis() + getActor().getParameter("globalAggro", 10000L));
setIntention(AI_INTENTION_ACTIVE);
}
@Override
protected void onEvtReadyToAct()
{
onEvtThink();
}
@Override
protected void onEvtArrivedTarget()
{
onEvtThink();
}
@Override
protected void onEvtArrived()
{
onEvtThink();
}
protected boolean tryMoveToTarget(Creature target)
{
NpcInstance actor = getActor();
if(target.isInvisible())
{
notifyEvent(EVT_THINK);
return false;
}
if(!actor.followToCharacter(target, actor.getPhysicalAttackRange(), true))
setPathfindFails(getPathfindFails() + 1);
if(getPathfindFails() >= getMaxPathfindFails() && (currentTimeMillis() > getAttackTimeout() - getMaxAttackTimeout() + getTeleportTimeout()) && actor.isInRange(target, getMAX_PURSUE_RANGE()))
{
setPathfindFails(0);
if(target.isPlayable())
{
AggroInfo hate = actor.getAggroList().get(target);
if(hate == null || hate.hate currentTimeMillis())
return;
if(actor.isRaid() && (actor.isInZonePeace() || actor.isInZoneBattle() || actor.isInZone(SIEGE)))
{
teleportHome();
return;
}
setThinking(true);
try
{
if(!BlockActiveTasks && getIntention() == AI_INTENTION_ACTIVE)
thinkActive();
else if(getIntention() == AI_INTENTION_ATTACK)
thinkAttack();
}
finally
{
setThinking(false);
}
}
@Override
protected void onEvtDead(Creature killer)
{
NpcInstance actor = getActor();
int transformer = actor.getParameter("transformOnDead", 0);
int chance = actor.getParameter("transformChance", 100);
if(transformer > 0 && chance(chance))
{
NpcInstance npc = spawnSingle(transformer, actor.getLoc(), actor.getReflection()) ;
if(killer != null && killer.isPlayable())
{
npc.getAI().notifyEvent(EVT_AGGRESSION, killer, 100);
killer.setTarget(npc);
killer.sendPacket(npc.makeStatusUpdate(CUR_HP, MAX_HP));
}
}
super.onEvtDead(killer);
}
@Override
protected void onEvtClanAttacked(Creature attacked, Creature attacker, int damage)
{
if(getIntention() != AI_INTENTION_ACTIVE || !isGlobalAggro())
return;
notifyEvent(EVT_AGGRESSION, attacker, 2);
}
@Override
protected void onEvtAttacked(Creature attacker, int damage)
{
NpcInstance actor = getActor();
if(attacker == null || actor.isDead())
return;
int transformer = actor.getParameter("transformOnUnderAttack", 0);
if(transformer > 0)
{
int chance = actor.getParameter("transformChance", 5);
if(chance == 100 || ((MonsterInstance) actor).getChampion() == 0 && actor.getCurrentHpPercents() > 50 && chance(chance))
{
MonsterInstance npc = (MonsterInstance) getInstance().getTemplate(transformer).getNewInstance();
npc.setSpawnedLoc(actor.getLoc());
npc.setReflection(actor.getReflection());
npc.setChampion(((MonsterInstance) actor).getChampion());
npc.setCurrentHpMp(npc.getMaxHp(), npc.getMaxMp(), true);
npc.spawnMe(npc.getSpawnedLoc());
npc.getAI().notifyEvent(EVT_AGGRESSION, attacker, 100);
actor.doDie(actor);
actor.decayMe();
attacker.setTarget(npc);
attacker.sendPacket(npc.makeStatusUpdate(CUR_HP, MAX_HP));
return;
}
}
Player player = attacker.getPlayer();
if(player != null)
{
//FIXME [G1ta0] затычка для 7 печатей, при атаке монстра 7 печатей телепортирует персонажа в ближайший город
if((getInstance().isSealValidationPeriod() || getInstance().isCompResultsPeriod()) && actor.isSevenSignsMonster())
{
int pcabal = getInstance().getPlayerCabal(player);
int wcabal = getInstance().getCabalHighestScore();
if(pcabal != wcabal && wcabal != CABAL_NULL)
{
player.sendMessage("You have been teleported to the nearest town because you not signed for winning cabal.");
player.teleToClosestTown();
return;
}
}
List
quests = player.getQuestsForEvent(actor, ATTACKED_WITH_QUEST);
if(quests != null)
for(QuestState qs : quests)
qs.getQuest().notifyAttack(actor, qs);
}
//Добавляем только хейт, урон, если атакующий - игровой персонаж, будет добавлен в L2NpcInstance.onReduceCurrentHp
actor.getAggroList().addDamageHate(attacker, 0, damage);
// Обычно 1 хейт добавляется хозяину суммона, чтобы после смерти суммона моб накинулся на хозяина.
if(damage > 0 && (attacker.isSummon() || attacker.isPet()))
actor.getAggroList().addDamageHate(attacker.getPlayer(), 0, actor.getParameter("searchingMaster", false) ? damage : 1);
if(getIntention() != AI_INTENTION_ATTACK)
{
if(!actor.isRunning())
startRunningTask(getAI_TASK_ATTACK_DELAY());
setIntention(AI_INTENTION_ATTACK, attacker);
}
notifyFriends(attacker, damage);
}
@Override
protected void onEvtAggression(Creature attacker, int aggro)
{
NpcInstance actor = getActor();
if(attacker == null || actor.isDead())
return;
actor.getAggroList().addDamageHate(attacker, 0, aggro);
// Обычно 1 хейт добавляется хозяину суммона, чтобы после смерти суммона моб накинулся на хозяина.
if(aggro > 0 && (attacker.isSummon() || attacker.isPet()))
actor.getAggroList().addDamageHate(attacker.getPlayer(), 0, actor.getParameter("searchingMaster", false) ? aggro : 1);
if(getIntention() != AI_INTENTION_ATTACK)
{
if(!actor.isRunning())
startRunningTask(getAI_TASK_ATTACK_DELAY());
setIntention(AI_INTENTION_ATTACK, attacker);
}
}
protected boolean maybeMoveToHome()
{
NpcInstance actor = getActor();
if(actor.isDead())
return false;
boolean randomWalk = actor.hasRandomWalk();
Location sloc = actor.getSpawnedLoc();
// Random walk or not?
if(randomWalk && (!RndWalk || !chance(RndWalkRate)))
return false;
boolean isInRange = actor.isInRangeZ(sloc, MaxDriftRange);
if(!randomWalk && isInRange)
return false;
Location pos = findPointToStay(actor, sloc, 0, MaxDriftRange);
actor.setWalking();
// Телепортируемся домой, только если далеко от дома
if(!actor.moveToLocation(pos.x, pos.y, pos.z, 0, true) && !isInRange)
teleportHome();
return true;
}
protected void returnHome()
{
returnHome(true, false);
}
protected void teleportHome()
{
returnHome(true, true);
}
protected void returnHome(boolean clearAggro, boolean teleport)
{
NpcInstance actor = getActor();
Location sloc = actor.getSpawnedLoc();
// Удаляем все задания
clearTasks();
actor.stopMove();
if(clearAggro)
actor.getAggroList().clear(true);
setAttackTimeout(MAX_VALUE);
setAttackTarget(null);
changeIntention(AI_INTENTION_ACTIVE, null, null);
if(teleport)
{
actor.broadcastPacketToOthers(new MagicSkillUse(actor, actor, 2036, 1, 500, 0));
actor.teleToLocation(sloc.x, sloc.y, getHeight(sloc, actor.getGeoIndex()));
}
else
{
if(!clearAggro)
actor.setRunning();
else
actor.setWalking();
addTaskMove(sloc, false);
}
}
protected Creature prepareTarget()
{
NpcInstance actor = getActor();
if(actor.isConfused())
return getAttackTarget();
// Для "двинутых" боссов, иногда, выбираем случайную цель
if(chance(actor.getParameter("isMadness", 0)))
{
Creature randomHated = actor.getAggroList().getRandomHated();
if(randomHated != null)
{
setAttackTarget(randomHated);
if(getMadnessTask() == null && !actor.isConfused())
{
actor.startConfused();
setMadnessTask(getInstance().schedule(new MadnessTask(), 10000));
}
return randomHated;
}
}
// Новая цель исходя из агрессивности
List
hateList = actor.getAggroList().getHateList();
Creature hated = null;
for(Creature cha : hateList)
{
// Если у Монстра есть скилл "Searching Master" он должен атаковать хозяина пета в первую очередь.
if((cha.isPet() || cha.isSummon()) && cha.getPlayer() != null)
if(getActor().getSkillLevel(6019) == 1 && checkTarget(cha.getPlayer(), getMAX_PURSUE_RANGE()))
cha = cha.getPlayer();
//Не подходит, очищаем хейт
if(!checkTarget(cha, MAX_PURSUE_RANGE))
{
actor.getAggroList().remove(cha, true);
continue;
}
hated = cha;
break;
}
if(hated != null)
{
setAttackTarget(hated);
return hated;
}
return null;
}
protected boolean canUseSkill(Skill skill, Creature target, double distance)
{
NpcInstance actor = getActor();
if(skill == null || skill.isNotUsedByAI())
return false;
if(skill.getTargetType() == TARGET_SELF && target != actor)
return false;
int castRange = skill.getAOECastRange();
if(castRange 200)
return false;
if(actor.isSkillDisabled(skill) || actor.isMuted(skill) || actor.isUnActiveSkill(skill.getId()))
return false;
double mpConsume2 = skill.getMpConsume2();
if(skill.isMagic())
mpConsume2 = actor.calcStat(MP_MAGIC_SKILL_CONSUME, mpConsume2, target, skill);
else
mpConsume2 = actor.calcStat(MP_PHYSICAL_SKILL_CONSUME, mpConsume2, target, skill);
if(actor.getCurrentMp()
rnd = new RndSelector<>(skills.length);
double weight;
for(Skill skill : skills)
{
weight = skill.getSimpleDamage(actor, target) * skill.getAOECastRange() / distance;
if(weight
rnd = new RndSelector<>(skills.length);
double weight;
for(Skill skill : skills)
{
if(skill.getSameByStackType(target) != null)
continue;
if((weight = 100. * skill.getAOECastRange() / distance)
rnd = new RndSelector<>(skills.length);
double weight;
for(Skill skill : skills)
{
if(skill.getSameByStackType(target) != null)
continue;
if((weight = skill.getPower())
rnd = new RndSelector<>(skills.length);
double weight;
for(Skill skill : skills)
{
if((weight = abs(skill.getPower() - hpReduced))
skillMap, Creature target, double distance, Skill[] skills)
{
if(skills == null || skills.length == 0 || target == null)
return;
for(Skill sk : skills)
addDesiredSkill(skillMap, target, distance, sk);
}
protected void addDesiredSkill(Map
skillMap, Creature target, double distance, Skill skill)
{
if(skill == null || target == null || !canUseSkill(skill, target))
return;
int weight = (int) -abs(skill.getAOECastRange() - distance);
if(skill.getAOECastRange() >= distance)
weight += 1000000;
else if(skill.isNotTargetAoE() && skill.getTargets(getActor(), target, false).isEmpty())
return;
skillMap.put(skill, weight);
}
protected void addDesiredHeal(Map
skillMap, Skill[] skills)
{
if(skills == null || skills.length == 0)
return;
NpcInstance actor = getActor();
double hpReduced = actor.getMaxHp() - actor.getCurrentHp();
double hpPercent = actor.getCurrentHpPercents();
if(hpReduced
skillMap, Skill[] skills)
{
if(skills == null || skills.length == 0)
return;
NpcInstance actor = getActor();
for(Skill sk : skills)
if(canUseSkill(sk, actor))
skillMap.put(sk, 1000000);
}
protected Skill selectTopSkill(Map
skillMap)
{
if(skillMap == null || skillMap.isEmpty())
return null;
int nWeight, topWeight = MIN_VALUE;
for(Skill next : skillMap.keySet())
if((nWeight = skillMap.get(next)) > topWeight)
topWeight = nWeight;
if(topWeight == MIN_VALUE)
return null;
Skill[] skills = new Skill[skillMap.size()];
nWeight = 0;
for(Map.Entry
e : skillMap.entrySet())
{
if(e.getValue() skill.getAOECastRange() + 60)
{
target = null;
if(skill.isOffensive())
{
LazyArrayList
targets = newInstance();
for(Creature cha : actor.getAggroList().getHateList())
{
if(!checkTarget(cha, skill.getAOECastRange() + 60) || !canUseSkill(skill, cha))
continue;
targets.add(cha);
}
if(!targets.isEmpty())
target = targets.get(get(targets.size()));
recycle(targets);
}
}
if(target == null)
return false;
// Добавить новое задание
if(skill.isOffensive())
addTaskCast(target, skill);
else
addTaskBuff(target, skill);
return true;
}
// Смена цели, если необходимо
if(actor.isMovementDisabled() && distance > actor.getPhysicalAttackRange() + 40)
{
target = null;
LazyArrayList
targets = newInstance();
for(Creature cha : actor.getAggroList().getHateList())
{
if(!checkTarget(cha, actor.getPhysicalAttackRange() + 40))
continue;
targets.add(cha);
}
if(!targets.isEmpty())
target = targets.get(get(targets.size()));
recycle(targets);
}
if(target == null)
return false;
// Добавить новое задание
addTaskAttack(target);
return true;
}
@Override
public boolean isActive()
{
return getAiTask() != null;
}
protected void clearTasks()
{
setDef_think(false);
getTasks().clear();
}
/** переход в режим бега через определенный интервал времени
* @param interval */
protected void startRunningTask(long interval)
{
NpcInstance actor = getActor();
if(actor != null && getRunningTask() == null && !actor.isRunning())
setRunningTask(getInstance().schedule(new RunningTask(), (int) interval));
}
protected boolean isGlobalAggro()
{
if(getGlobalAggro() == 0)
return true;
if(getGlobalAggro() getMinFactionNotifyInterval())
{
setLastFactionNotifyTime(currentTimeMillis());
if(actor.isMinion())
{
//Оповестить лидера об атаке
MonsterInstance master = ((MinionInstance) actor).getLeader();
if(master != null)
{
if(!master.isDead() && master.isVisible())
master.getAI().notifyEvent(EVT_AGGRESSION, attacker, damage);
//Оповестить минионов лидера об атаке
MinionList minionList = master.getMinionList();
if(minionList != null)
minionList.getAliveMinions().stream().filter(minion -> minion != actor).forEach(minion -> minion.getAI().notifyEvent(EVT_AGGRESSION, attacker, damage));
}
}
//Оповестить своих минионов об атаке
MinionList minionList = actor.getMinionList();
if(minionList != null && minionList.hasAliveMinions())
for(MinionInstance minion : minionList.getAliveMinions())
minion.getAI().notifyEvent(EVT_AGGRESSION, attacker, damage);
//Оповестить социальных мобов
for(NpcInstance npc : activeFactionTargets())
npc.getAI().notifyEvent(EVT_CLAN_ATTACKED, new Object[] { actor, attacker, damage });
}
}
protected List
activeFactionTargets()
{
NpcInstance actor = getActor();
if(actor.getFaction().isNone())
return emptyList();
return getAroundNpc(actor).stream().filter(npc -> !npc.isDead()).filter(npc -> npc.isInFaction(actor)).filter(npc -> npc.isInRangeZ(actor, npc.getFaction().getRange())).filter(npc -> canSeeTarget(npc, actor, false)).collect(Collectors.toCollection(() -> new LazyArrayList<>()));
}
protected boolean defaultThinkBuff(int rateSelf, int rateFriends)
{
NpcInstance actor = getActor();
if(actor.isDead())
return true;
//TODO сделать более разумный выбор баффа, сначала выбирать подходящие а потом уже рандомно 1 из них
if(chance(rateSelf))
{
double actorHp = actor.getCurrentHpPercents();
Skill[] skills = actorHp 10 ? chance(getRateDEBUFF()) ? selectUsableSkills(target, distance, getDebuffSkills()) : null : null;
Skill[] stun = chance(getRateSTUN()) ? selectUsableSkills(target, distance, getStunSkills()) : null;
Skill[] heal = actorHp
rnd = new RndSelector<>();
if(!actor.isAMuted())
rnd.add(null, getRatePHYS());
rnd.add(dam, getRateDAM());
rnd.add(dot, getRateDOT());
rnd.add(debuff, getRateDEBUFF());
rnd.add(heal, getRateHEAL());
rnd.add(buff, getRateBUFF());
rnd.add(stun, getRateSTUN());
Skill[] selected = rnd.select();
if(selected != null)
{
if(selected == dam || selected == dot)
return chooseTaskAndTargets(selectTopSkillByDamage(actor, target, distance, selected), target, distance);
if(selected == debuff || selected == stun)
return chooseTaskAndTargets(selectTopSkillByDebuff(target, distance, selected), target, distance);
if(selected == buff)
return chooseTaskAndTargets(selectTopSkillByBuff(actor, selected), actor, distance);
if(selected == heal)
return chooseTaskAndTargets(selectTopSkillByHeal(actor, selected), actor, distance);
}
// TODO сделать лечение и баф дружественных целей
return chooseTaskAndTargets(null, target, distance);
}
public int getRatePHYS()
{
return 100;
}
public int getRateDOT()
{
return 0;
}
public int getRateDEBUFF()
{
return 0;
}
public int getRateDAM()
{
return 0;
}
public int getRateSTUN()
{
return 0;
}
public int getRateBUFF()
{
return 0;
}
public int getRateHEAL()
{
return 0;
}
public boolean getIsMobile()
{
return !getActor().getParameter("isImmobilized", false);
}
public int getMaxPathfindFails()
{
return 3;
}
/**
* Задержка, перед переключением в активный режим после атаки, если цель не найдена (вне зоны досягаемости, убита, очищен хейт)
* @return
*/
public int getMaxAttackTimeout()
{
return 15000;
}
/**
* Задержка, перед телепортом к цели, если не удается дойти
* @return
*/
public int getTeleportTimeout()
{
return 10000;
}
|
|
|

06.04.2016, 03:37
|
|
Участник форума
Регистрация: 05.04.2016
Сообщений: 222
С нами:
5318817
Репутация:
0
|
|
PlayableAI.java
Сообщение от Спойлер
PHP:
Код:
package l2p.gameserver.ai;
import l2p.commons.threading.RunnableImpl;
import l2p.gameserver.ThreadPoolManager;
import l2p.gameserver.geodata.GeoEngine;
import l2p.gameserver.model.Creature;
import l2p.gameserver.model.GameObject;
import l2p.gameserver.model.Playable;
import l2p.gameserver.model.Player;
import l2p.gameserver.model.Skill;
import l2p.gameserver.model.Skill.NextAction;
import l2p.gameserver.model.Skill.SkillType;
import l2p.gameserver.model.Summon;
import l2p.gameserver.serverpackets.MyTargetSelected;
import l2p.gameserver.serverpackets.components.SystemMsg;
import l2p.gameserver.utils.Location;
import java.util.concurrent.ScheduledFuture;
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;
public class PlayableAI extends CharacterAI
{
private volatile int thinking = 0; // to prevent recursive thinking
protected Object _intention_arg0 = null, _intention_arg1 = null;
protected Skill _skill;
private nextAction _nextAction;
private Object _nextAction_arg0;
private Object _nextAction_arg1;
private boolean _nextAction_arg2;
private boolean _nextAction_arg3;
protected boolean _forceUse;
private boolean _dontMove;
private ScheduledFuture
_followTask;
public PlayableAI(Playable 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(Skill skill, Creature 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;
Playable actor = getActor();
if(nextAction == null || actor.isActionsDisabled())
return false;
Skill skill;
Creature target;
GameObject object;
switch(nextAction)
{
case ATTACK:
if(nextAction_arg0 == null)
return false;
target = (Creature) 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 = (Skill) nextAction_arg0;
target = (Creature) 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(PlayableAI.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(null);
break;
case INTERACT:
if(nextAction_arg0 == null)
return false;
object = (GameObject) nextAction_arg0;
clearNextAction();
onIntentionInteract(object);
break;
case PICKUP:
if(nextAction_arg0 == null)
return false;
object = (GameObject) 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:
thinkFollow();
break;
default:
onEvtThink();
break;
}
}
@Override
protected final void onEvtThink()
{
Playable actor = getActor();
if(actor.isActionsDisabled())
return;
try
{
if(thinking++ > 1)
return;
switch(getIntention())
{
case AI_INTENTION_ACTIVE:
thinkActive();
break;
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;
case AI_INTENTION_FOLLOW:
thinkFollow();
break;
}
}
catch(Exception e)
{
_log.error("Unexpected error in PlayableAI.onEvtThink()", e);
}
finally
{
thinking--;
}
}
protected void thinkActive()
{
}
protected void thinkFollow()
{
Playable actor = getActor();
Creature target = (Creature) _intention_arg0;
Integer offset = (Integer) _intention_arg1;
//Находимся слишком далеко цели, либо цель не пригодна для следования
if(target == null || target.isAlikeDead() || actor.getDistance(target) > 4000 || offset == null)
{
clientActionFailed();
return;
}
//Уже следуем за этой целью
if(actor.isFollow && actor.getFollowTarget() == target)
{
clientActionFailed();
return;
}
//Находимся достаточно близко или не можем двигаться - побежим потом ?
if(actor.isInRange(target, offset + 20) || actor.isMovementDisabled())
clientActionFailed();
if(_followTask != null)
{
_followTask.cancel(false);
_followTask = null;
}
_followTask = ThreadPoolManager.getInstance().schedule(new ThinkFollow(), 250L);
}
protected class ThinkFollow extends RunnableImpl
{
@Override
public void runImpl() throws Exception
{
Playable actor = getActor();
if(getIntention() != AI_INTENTION_FOLLOW)
{
// Если пет прекратил преследование, меняем статус, чтобы не пришлось щелкать на кнопку следования 2 раза.
if((actor.isPet() || actor.isSummon()) && getIntention() == AI_INTENTION_ACTIVE)
((Summon) actor).setFollowMode(false);
return;
}
Creature target = (Creature) _intention_arg0;
int offset = _intention_arg1 instanceof Integer ? (Integer) _intention_arg1 : 0;
if(target == null || !target.isVisible() || target.isAlikeDead() || actor.getDistance(target) > 4000)
{
setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
return;
}
Player player = actor.getPlayer();
if(player == null || player.isLogoutStarted() || (actor.isPet() || actor.isSummon()) && 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().schedule(this, 250L);
}
}
protected class ExecuteFollow extends RunnableImpl
{
private Creature _target;
private int _range;
public ExecuteFollow(Creature target, int range)
{
_target = target;
_range = range;
}
@Override
public void runImpl()
{
if(_target.isDoor())
_actor.moveToLocation(_target.getLoc(), 40, true);
else
_actor.followToCharacter(_target, _range, true);
}
}
@Override
protected void onIntentionInteract(GameObject object)
{
Playable actor = getActor();
if(actor.isActionsDisabled())
{
setNextAction(nextAction.INTERACT, object, null, false, false);
clientActionFailed();
return;
}
clearNextAction();
changeIntention(AI_INTENTION_INTERACT, object, null);
onEvtThink();
}
protected void thinkInteract()
{
Playable actor = getActor();
GameObject target = (GameObject) _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())
((Player) 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(GameObject object)
{
Playable actor = getActor();
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 Playable actor = getActor();
final GameObject target = (GameObject) _intention_arg0;
if(target == null)
{
setIntention(AI_INTENTION_ACTIVE);
return;
}
if(actor.isInRange(target, 30) && Math.abs(actor.getZ() - target.getZ()) 200 || Math.abs(actor.getZ() - attack_target.getZ()) > 200))
{
actor.sendPacket(SystemMsg.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))
{
if(!canSee)
{
actor.sendPacket(SystemMsg.CANNOT_SEE_TARGET);
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
return;
}
clientStopMoving(false);
actor.doAttack(attack_target);
}
else if(!_dontMove)
ThreadPoolManager.getInstance().execute(new ExecuteFollow(attack_target, range - 20));
else
actor.sendActionFailed();
}
protected void thinkCast(boolean checkRange)
{
Playable actor = getActor();
Creature target = getAttackTarget();
if(_skill.getSkillType() == SkillType.CRAFT || _skill.isToggle())
{
if(_skill.checkCondition(actor, target, _forceUse, _dontMove, true))
actor.doCast(_skill, target, _forceUse);
return;
}
if(target == null || target.isDead() != _skill.getCorpse() && !_skill.isNotTargetAoE())
{
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
return;
}
if(!checkRange)
{
// Если скилл имеет следующее действие, назначим это действие после окончания действия скилла
if(_skill.getNextAction() == NextAction.ATTACK && !actor.equals(target))
setNextAction(nextAction.ATTACK, target, null, _forceUse, false);
else
clearNextAction();
clientStopMoving();
if(_skill.checkCondition(actor, target, _forceUse, _dontMove, true))
actor.doCast(_skill, target, _forceUse);
else
{
setNextIntention();
if(getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
thinkAttack(true);
}
return;
}
int range = actor.getMagicalAttackRange(_skill);
if(range 200 || Math.abs(actor.getZ() - target.getZ()) > 200))
{
actor.sendPacket(SystemMsg.CANNOT_SEE_TARGET);
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
return;
}
range += actor.getMinDistance(target);
if(actor.isFakeDeath())
actor.breakFakeDeath();
if(actor.isInRangeZ(target, range) || noRangeSkill)
{
if(!noRangeSkill && !canSee)
{
actor.sendPacket(SystemMsg.CANNOT_SEE_TARGET);
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
return;
}
// Если скилл имеет следующее действие, назначим это действие после окончания действия скилла
if(_skill.getNextAction() == NextAction.ATTACK && !actor.equals(target) && !_forceUse)
{
//clearNextAction();
setNextAction(nextAction.ATTACK, target, null, _forceUse, false);
}
else
clearNextAction();
if(_skill.checkCondition(actor, target, _forceUse, _dontMove, true))
{
clientStopMoving(false);
actor.doCast(_skill, target, _forceUse);
}
else
{
setNextIntention();
if(getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
thinkAttack(true);
}
}
else if(!_dontMove)
ThreadPoolManager.getInstance().execute(new ExecuteFollow(target, range - 20));
else
{
actor.sendPacket(SystemMsg.YOUR_TARGET_IS_OUT_OF_RANGE);
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
}
}
@Override
protected void onEvtDead(Creature killer)
{
clearNextAction();
super.onEvtDead(killer);
}
@Override
protected void onEvtFakeDeath()
{
clearNextAction();
super.onEvtFakeDeath();
}
public void lockTarget(Creature target)
{
Playable actor = getActor();
if(target == null || target.isDead())
actor.setAggressionTarget(null);
else if(actor.getAggressionTarget() == null)
{
GameObject actorStoredTarget = actor.getTarget();
actor.setAggressionTarget(target);
actor.setTarget(target);
clearNextAction();
// DS: агрессия только перекидывает видимую цель, но не обрывает текущую атаку/каст
/*if (getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
setAttackTarget(target);
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(GameObject target, boolean forceUse, boolean dontMove)
{
Playable actor = getActor();
if(target.isCreature() && (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(Skill skill, Creature target, boolean forceUse, boolean dontMove)
{
Playable actor = getActor();
// Если скилл альтернативного типа (например, бутылка на хп),
// то он может использоваться во время каста других скиллов, или во время атаки, или на бегу.
// Поэтому пропускаем дополнительные проверки.
if(skill.altUse() || skill.isToggle())
{
if((skill.isToggle() || skill.isHandler()) && (actor.isOutOfControl() || actor.isStunned() || actor.isSleeping() || actor.isParalyzed() || actor.isAlikeDead()))
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);
}
@Override
public Playable getActor()
{
return (Playable) super.getActor();
}
}
Сообщение от Спойлер
PHP:
Код:
package l2p.gameserver.ai;
import static java.lang.Math.abs;
import static java.lang.Math.max;
import java.util.concurrent.ScheduledFuture;
import l2p.commons.threading.RunnableImpl;
import static l2p.gameserver.ThreadPoolManager.getInstance;
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 static l2p.gameserver.ai.PlayableAI.nextAction.ATTACK;
import static l2p.gameserver.ai.PlayableAI.nextAction.CAST;
import static l2p.gameserver.ai.PlayableAI.nextAction.INTERACT;
import static l2p.gameserver.ai.PlayableAI.nextAction.PICKUP;
import static l2p.gameserver.geodata.GeoEngine.canSeeTarget;
import l2p.gameserver.model.Creature;
import l2p.gameserver.model.GameObject;
import l2p.gameserver.model.Playable;
import l2p.gameserver.model.Player;
import l2p.gameserver.model.Skill;
import static l2p.gameserver.model.Skill.SkillType.CRAFT;
import static l2p.gameserver.model.Skill.SkillType.TAKECASTLE;
import static l2p.gameserver.model.Skill.SkillType.TAKEFORTRESS;
import l2p.gameserver.model.Summon;
import l2p.gameserver.serverpackets.MyTargetSelected;
import static l2p.gameserver.serverpackets.components.SystemMsg.CANNOT_SEE_TARGET;
import static l2p.gameserver.serverpackets.components.SystemMsg.YOUR_TARGET_IS_OUT_OF_RANGE;
import l2p.gameserver.utils.Location;
public class PlayableAI extends CharacterAI
{
private volatile int thinking = 0; // to prevent recursive thinking
private Object _intention_arg0 = null;
private Object _intention_arg1 = null;
private Skill _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 PlayableAI(Playable actor)
{
super(actor);
}
/**
* @return the thinking
*/
public int getThinking() {
return thinking;
}
/**
* @param thinking the thinking to set
*/
public void setThinking(int thinking) {
this.thinking = thinking;
}
/**
* @return the _intention_arg0
*/
public Object getIntention_arg0() {
return _intention_arg0;
}
/**
* @param _intention_arg0 the _intention_arg0 to set
*/
public void setIntention_arg0(Object _intention_arg0) {
this._intention_arg0 = _intention_arg0;
}
/**
* @return the _intention_arg1
*/
public Object getIntention_arg1() {
return _intention_arg1;
}
/**
* @param _intention_arg1 the _intention_arg1 to set
*/
public void setIntention_arg1(Object _intention_arg1) {
this._intention_arg1 = _intention_arg1;
}
/**
* @return the _skill
*/
public Skill getSkill() {
return _skill;
}
/**
* @param _skill the _skill to set
*/
public void setSkill(Skill _skill) {
this._skill = _skill;
}
/**
* @return the _nextAction
*/
public nextAction getNextAction() {
return _nextAction;
}
/**
* @param _nextAction the _nextAction to set
*/
public void setNextAction(nextAction _nextAction) {
this._nextAction = _nextAction;
}
/**
* @return the _nextAction_arg0
*/
public Object getNextAction_arg0() {
return _nextAction_arg0;
}
/**
* @param _nextAction_arg0 the _nextAction_arg0 to set
*/
public void setNextAction_arg0(Object _nextAction_arg0) {
this._nextAction_arg0 = _nextAction_arg0;
}
/**
* @return the _nextAction_arg1
*/
public Object getNextAction_arg1() {
return _nextAction_arg1;
}
/**
* @param _nextAction_arg1 the _nextAction_arg1 to set
*/
public void setNextAction_arg1(Object _nextAction_arg1) {
this._nextAction_arg1 = _nextAction_arg1;
}
/**
* @return the _nextAction_arg2
*/
public boolean isNextAction_arg2() {
return _nextAction_arg2;
}
/**
* @param _nextAction_arg2 the _nextAction_arg2 to set
*/
public void setNextAction_arg2(boolean _nextAction_arg2) {
this._nextAction_arg2 = _nextAction_arg2;
}
/**
* @return the _nextAction_arg3
*/
public boolean isNextAction_arg3() {
return _nextAction_arg3;
}
/**
* @param _nextAction_arg3 the _nextAction_arg3 to set
*/
public void setNextAction_arg3(boolean _nextAction_arg3) {
this._nextAction_arg3 = _nextAction_arg3;
}
/**
* @return the _forceUse
*/
public boolean isForceUse() {
return _forceUse;
}
/**
* @param _forceUse the _forceUse to set
*/
public void setForceUse(boolean _forceUse) {
this._forceUse = _forceUse;
}
/**
* @return the _dontMove
*/
public boolean isDontMove() {
return _dontMove;
}
/**
* @param _dontMove the _dontMove to set
*/
public void setDontMove(boolean _dontMove) {
this._dontMove = _dontMove;
}
/**
* @return the _followTask
*/
public ScheduledFuture
getFollowTask() {
return _followTask;
}
/**
* @param _followTask the _followTask to set
*/
public void setFollowTask(ScheduledFuture
_followTask) {
this._followTask = _followTask;
}
public enum nextAction
{
ATTACK,
CAST,
MOVE,
REST,
PICKUP,
INTERACT
}
@Override
public void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
super.changeIntention(intention, arg0, arg1);
setIntention_arg0(arg0);
setIntention_arg1(arg1);
}
@Override
public void setIntention(CtrlIntention intention, Object arg0, Object arg1)
{
setIntention_arg0(null);
setIntention_arg1(null);
super.setIntention(intention, arg0, arg1);
}
@Override
protected void onIntentionCast(Skill skill, Creature target)
{
setSkill(skill);
super.onIntentionCast(skill, target);
}
@Override
public void setNextAction(nextAction action, Object arg0, Object arg1, boolean arg2, boolean arg3)
{
setNextAction(action);
setNextAction_arg0(arg0);
setNextAction_arg1(arg1);
setNextAction_arg2(arg2);
setNextAction_arg3(arg3);
}
public boolean setNextIntention()
{
nextAction nextAction = getNextAction();
Object nextAction_arg0 = getNextAction_arg0();
Object nextAction_arg1 = getNextAction_arg1();
boolean nextAction_arg2 = isNextAction_arg2();
boolean nextAction_arg3 = isNextAction_arg3();
Playable actor = getActor();
if(nextAction == null || actor.isActionsDisabled())
return false;
Skill skill;
Creature target;
GameObject object;
switch(nextAction)
{
case ATTACK:
if(nextAction_arg0 == null)
return false;
target = (Creature) nextAction_arg0;
setForceUse(nextAction_arg2);
setDontMove(nextAction_arg3);
clearNextAction();
setIntention(AI_INTENTION_ATTACK, target);
break;
case CAST:
if(nextAction_arg0 == null || nextAction_arg1 == null)
return false;
skill = (Skill) nextAction_arg0;
target = (Creature) nextAction_arg1;
setForceUse(nextAction_arg2);
setDontMove(nextAction_arg3);
clearNextAction();
if(!skill.checkCondition(actor, target, _forceUse, _dontMove, true))
{
if(skill.getNextAction() == ATTACK && !actor.equals(target))
{
setNextAction(ATTACK, target, null, isForceUse(), 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(null);
break;
case INTERACT:
if(nextAction_arg0 == null)
return false;
object = (GameObject) nextAction_arg0;
clearNextAction();
onIntentionInteract(object);
break;
case PICKUP:
if(nextAction_arg0 == null)
return false;
object = (GameObject) nextAction_arg0;
clearNextAction();
onIntentionPickUp(object);
break;
default:
return false;
}
return true;
}
@Override
public void clearNextAction()
{
setNextAction(null);
setNextAction_arg0(null);
setNextAction_arg1(null);
setNextAction_arg2(false);
setNextAction_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:
thinkFollow();
break;
default:
onEvtThink();
break;
}
}
@Override
protected final void onEvtThink()
{
Playable actor = getActor();
if(actor.isActionsDisabled())
return;
try
{
if(thinking++ > 1)
return;
switch(getIntention())
{
case AI_INTENTION_ACTIVE:
thinkActive();
break;
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;
case AI_INTENTION_FOLLOW:
thinkFollow();
break;
}
}
catch(Exception e)
{
_log.error("Unexpected error in PlayableAI.onEvtThink()", e);
}
finally
{
setThinking(getThinking() - 1);
}
}
protected void thinkActive()
{
}
protected void thinkFollow()
{
Playable actor = getActor();
Creature target = (Creature) getIntention_arg0();
Integer offset = (Integer) getIntention_arg1();
//Находимся слишком далеко цели, либо цель не пригодна для следования
if(target == null || target.isAlikeDead() || actor.getDistance(target) > 4000 || offset == null)
{
clientActionFailed();
return;
}
//Уже следуем за этой целью
if(actor.isFollow && actor.getFollowTarget() == target)
{
clientActionFailed();
return;
}
//Находимся достаточно близко или не можем двигаться - побежим потом ?
if(actor.isInRange(target, offset + 20) || actor.isMovementDisabled())
clientActionFailed();
if(getFollowTask() != null)
{
getFollowTask().cancel(false);
setFollowTask(null);
}
setFollowTask(getInstance().schedule(new ThinkFollow(), 250L));
}
protected class ThinkFollow extends RunnableImpl
{
@Override
public void runImpl() throws Exception
{
Playable actor = getActor();
if(getIntention() != AI_INTENTION_FOLLOW)
{
// Если пет прекратил преследование, меняем статус, чтобы не пришлось щелкать на кнопку следования 2 раза.
if((actor.isPet() || actor.isSummon()) && getIntention() == AI_INTENTION_ACTIVE)
((Summon) actor).setFollowMode(false);
return;
}
Creature target = (Creature) getIntention_arg0();
int offset = getIntention_arg1() instanceof Integer ? (Integer) getIntention_arg1() : 0;
if(target == null || !target.isVisible() || target.isAlikeDead() || actor.getDistance(target) > 4000)
{
setIntention(AI_INTENTION_ACTIVE);
return;
}
Player player = actor.getPlayer();
if(player == null || player.isLogoutStarted() || (actor.isPet() || actor.isSummon()) && player.getPet() != actor)
{
setIntention(AI_INTENTION_ACTIVE);
return;
}
if(!actor.isInRange(target, offset + 20) && (!actor.isFollow || actor.getFollowTarget() != target))
actor.followToCharacter(target, offset, false);
setFollowTask(getInstance().schedule(this, 250L));
}
}
protected class ExecuteFollow extends RunnableImpl
{
private final Creature _target;
private final int _range;
public ExecuteFollow(Creature target, int range)
{
_target = target;
_range = range;
}
@Override
public void runImpl()
{
if(_target.isDoor())
_actor.moveToLocation(_target.getLoc(), 40, true);
else
_actor.followToCharacter(_target, _range, true);
}
}
@Override
protected void onIntentionInteract(GameObject object)
{
Playable actor = getActor();
if(actor.isActionsDisabled())
{
setNextAction(INTERACT, object, null, false, false);
clientActionFailed();
return;
}
clearNextAction();
changeIntention(AI_INTENTION_INTERACT, object, null);
onEvtThink();
}
protected void thinkInteract()
{
Playable actor = getActor();
GameObject target = (GameObject) getIntention_arg0();
if(target == null)
{
setIntention(AI_INTENTION_ACTIVE);
return;
}
int range = (int) (max(30, actor.getMinDistance(target)) + 20);
if(actor.isInRangeZ(target, range))
{
if(actor.isPlayer())
((Player) actor).doInteract(target);
setIntention(AI_INTENTION_ACTIVE);
}
else
{
actor.moveToLocation(target.getLoc(), 40, true);
setNextAction(INTERACT, target, null, false, false);
}
}
@Override
protected void onIntentionPickUp(GameObject object)
{
Playable actor = getActor();
if(actor.isActionsDisabled())
{
setNextAction(PICKUP, object, null, false, false);
clientActionFailed();
return;
}
clearNextAction();
changeIntention(AI_INTENTION_PICK_UP, object, null);
onEvtThink();
}
protected void thinkPickUp()
{
final Playable actor = getActor();
final GameObject target = (GameObject) getIntention_arg0();
if(target == null)
{
setIntention(AI_INTENTION_ACTIVE);
return;
}
if(actor.isInRange(target, 30) && abs(actor.getZ() - target.getZ()) 200 || abs(actor.getZ() - attack_target.getZ()) > 200))
{
actor.sendPacket(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))
{
if(!canSee)
{
actor.sendPacket(CANNOT_SEE_TARGET);
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
return;
}
clientStopMoving(false);
actor.doAttack(attack_target);
}
else if(!isDontMove())
getInstance().execute(new ExecuteFollow(attack_target, range - 20));
else
actor.sendActionFailed();
}
protected void thinkCast(boolean checkRange)
{
Playable actor = getActor();
Creature target = getAttackTarget();
if(getSkill().getSkillType() == CRAFT || getSkill().isToggle())
{
if(getSkill().checkCondition(actor, target, isForceUse(), isDontMove(), true))
actor.doCast(getSkill(), target, isForceUse());
return;
}
if(target == null || target.isDead() != getSkill().getCorpse() && !_skill.isNotTargetAoE())
{
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
return;
}
if(!checkRange)
{
// Если скилл имеет следующее действие, назначим это действие после окончания действия скилла
if(getSkill().getNextAction() == ATTACK && !actor.equals(target))
setNextAction(ATTACK, target, null, isForceUse(), false);
else
clearNextAction();
clientStopMoving();
if(getSkill().checkCondition(actor, target, isForceUse(), isDontMove(), true))
actor.doCast(getSkill(), target, isForceUse());
else
{
setNextIntention();
if(getIntention() == AI_INTENTION_ATTACK)
thinkAttack(true);
}
return;
}
int range = actor.getMagicalAttackRange(getSkill());
if(range 200 || abs(actor.getZ() - target.getZ()) > 200))
{
actor.sendPacket(CANNOT_SEE_TARGET);
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
return;
}
range += actor.getMinDistance(target);
if(actor.isFakeDeath())
actor.breakFakeDeath();
if(actor.isInRangeZ(target, range) || noRangeSkill)
{
if(!noRangeSkill && !canSee)
{
actor.sendPacket(CANNOT_SEE_TARGET);
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
return;
}
// Если скилл имеет следующее действие, назначим это действие после окончания действия скилла
if(getSkill().getNextAction() == ATTACK && !actor.equals(target) && !isForceUse())
{
//clearNextAction();
setNextAction(ATTACK, target, null, isForceUse(), false);
}
else
clearNextAction();
if(getSkill().checkCondition(actor, target, isForceUse(), isDontMove(), true))
{
clientStopMoving(false);
actor.doCast(getSkill(), target, isForceUse());
}
else
{
setNextIntention();
if(getIntention() == AI_INTENTION_ATTACK)
thinkAttack(true);
}
}
else if(!isDontMove())
getInstance().execute(new ExecuteFollow(target, range - 20));
else
{
actor.sendPacket(YOUR_TARGET_IS_OUT_OF_RANGE);
setIntention(AI_INTENTION_ACTIVE);
actor.sendActionFailed();
}
}
@Override
protected void onEvtDead(Creature killer)
{
clearNextAction();
super.onEvtDead(killer);
}
@Override
protected void onEvtFakeDeath()
{
clearNextAction();
super.onEvtFakeDeath();
}
public void lockTarget(Creature target)
{
Playable actor = getActor();
if(target == null || target.isDead())
actor.setAggressionTarget(null);
else if(actor.getAggressionTarget() == null)
{
GameObject actorStoredTarget = actor.getTarget();
actor.setAggressionTarget(target);
actor.setTarget(target);
clearNextAction();
// DS: агрессия только перекидывает видимую цель, но не обрывает текущую атаку/каст
/*if (getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
setAttackTarget(target);
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(GameObject target, boolean forceUse, boolean dontMove)
{
Playable actor = getActor();
if(target.isCreature() && (actor.isActionsDisabled() || actor.isAttackingDisabled()))
{
// Если не можем атаковать, то атаковать позже
setNextAction(ATTACK, target, null, forceUse, false);
actor.sendActionFailed();
return;
}
setDontMove(dontMove);
setForceUse(forceUse);
clearNextAction();
setIntention(AI_INTENTION_ATTACK, target);
}
@Override
public void Cast(Skill skill, Creature target, boolean forceUse, boolean dontMove)
{
Playable actor = getActor();
// Если скилл альтернативного типа (например, бутылка на хп),
// то он может использоваться во время каста других скиллов, или во время атаки, или на бегу.
// Поэтому пропускаем дополнительные проверки.
if(skill.altUse() || skill.isToggle())
{
if((skill.isToggle() || skill.isHandler()) && (actor.isOutOfControl() || actor.isStunned() || actor.isSleeping() || actor.isParalyzed() || actor.isAlikeDead()))
clientActionFailed();
else
actor.altUseSkill(skill, target);
return;
}
// Если не можем кастовать, то использовать скилл позже
if(actor.isActionsDisabled())
{
//if(!actor.isSkillDisabled(skill.getId()))
setNextAction(CAST, skill, target, forceUse, dontMove);
clientActionFailed();
return;
}
//_actor.stopMove(null);
setForceUse(forceUse);
setDontMove(dontMove);
clearNextAction();
setIntention(AI_INTENTION_CAST, skill, target);
}
@Override
public Playable getActor()
{
return (Playable) super.getActor();
}
}
|
|
|

06.04.2016, 03:46
|
|
Участник форума
Регистрация: 05.04.2016
Сообщений: 222
С нами:
5318817
Репутация:
0
|
|
Сообщение от Спойлер
PHP:
Код:
package ai
;
import l2p
.
commons
.
util
.
Rnd
;
import l2p
.
gameserver
.
ai
.
CtrlIntention
;
import l2p
.
gameserver
.
ai
.
Fighter
;
import l2p
.
gameserver
.
configs
.
proprties
.
ServerConfig
;
import l2p
.
gameserver
.
geodata
.
GeoEngine
;
import l2p
.
gameserver
.
model
.
Creature
;
import l2p
.
gameserver
.
model
.
Playable
;
import l2p
.
gameserver
.
model
.
instances
.
NpcInstance
;
import l2p
.
gameserver
.
tables
.
SkillTable
;
/**
* Created by STIGMATED
* Date: 12.11.11 Time: 12:43
*/
public
class
BossRandomAi
extends
Fighter
{
// private static final int BossId = 91107;
private
int current_point
=
-
1
;
private
long _lastAction
;
// private static final long Teleport_period = 7200000; //30 * 60 * 1000= 30 min
// private long _lastTeleport = System.currentTimeMillis();
String
[
]
_attackText
=
{
"Эй, ты офигел чтоли! "
,
"Эээ.. мне же больно :("
,
"Папа у Васи осёл в математике!"
,
"Советую вам не следовать моим советам."
,
"Мы с тревогой смотрим на будущее, а будущее с тревогой смотрит на нас."
,
"Мне чужого не надо….Но свое я заберу, чье бы оно ни было!!!"
,
"Успокойся и не ной. Всё равно ты будешь мой"
,
"Кстати, а тут все из разных мест? Или из одного?"
,
"Как сервер, стоит играть?"
,
"Ничто так не защищает мои зубы 12 часов днём и 12 часов ночью, как уважительное отношение к окружающим."
,
"Она: Все, я обиделась, тебе на меня наплевать, пойду в интернет и буду изменять тебе в онлайне! Касперского возьми!"
,
"Есть еще похер в похеровницах."
,
"Жизнь нужно прожить так, чтобы Боги в восторге предложили еще одну…"
,
"В погоне за зайцем многие подстреливают волка, испытывая минутное удовольствие, но в целом охота в данном случае является бессмысленной, поскольку они лишили цели волка и не достигли своей."
,
"Неважно, что вам говорят - вам говорят не всю правду"
,
"Все люди братья. Но некоторые сестры"
,
"Начни день весело: улыбни своё лицо!"
,
"Все имеют право на тупость, просто некоторые очень злоупотребляют."
,
"Никто не знает столько, сколько не знаю я!"
,
"Снимаю. Поpчy."
,
"Громче голову поворачивай!"
,
"Что вы на меня свое лицо вытаращили?"
,
"Я вас не спрашиваю, где вы были! Я спрашиваю, откуда вы идете?"
,
"Чтоб не киснуть - надо квасить!"
,
"Смерть застала его живым."
,
"Фаллический символ всегда лучше, чем символический фаллос."
,
"Неудачи преследуют всех. Но догоняют лишь неудачников."
,
"Жизнь прекрасна, рефлексы условны, а истина относительна."
,
"Первая заповедь холостяка: Не трогай пыль и она тебя не тронет."
,
"Спокойно! Уж в грязь лицом я не промахнусь!"
,
"В Китае нет понятия «изменил». Есть понятие «перепутал»."
,
"Труднее всего человеку дается то, что дается не ему."
}
;
public
BossRandomAi
(
Creature actor
)
{
super
(
(
NpcInstance
)
actor
)
;
}
@Override
protected
boolean
thinkActive
(
)
{
return
true
;
}
@Override
protected
void
onEvtAttacked
(
Creature attacker
,
int damage
)
{
NpcInstance actor
=
getActor
(
)
;
if
(
attacker
==
null
||
attacker
.
getPlayer
(
)
==
null
)
return
;
actor
.
startAttackStanceTask
(
)
;
// Ругаемся и кастуем скилл не чаще, чем раз в 3 секунды
if
(
System
.
currentTimeMillis
(
)
-
_lastAction
>
27000
)
{
int chance
=
Rnd
.
get
(
0
,
100
)
;
if
(
chance
400
)
return
false
;
// if ((Functions.getItemCount((L2Playable) target, 32100) != 0) || (Functions.getItemCount((L2Playable) target, 700) != 0))
// return;
if
(
!
GeoEngine
.
canSeeTarget
(
actor
,
target
,
actor
.
isFlying
(
)
)
)
return
false
;
// target.addDamageHate((Creature)actor, 0, 1);
setIntention
(
CtrlIntention
.
AI_INTENTION_ATTACK
,
target
)
;
String lang
=
ServerConfig
.
DefaultLang
;
if
(
lang
.
equalsIgnoreCase
(
"en"
)
)
actor
.
Say
(
"Die, noob!"
)
;
else
{
actor
.
Say
(
"Умри, Умри!"
)
;
}
if
(
(
getIntention
(
)
!=
CtrlIntention
.
AI_INTENTION_ACTIVE
)
&&
(
this
.
current_point
>
-
1
)
)
this
.
current_point
-
=
1
;
return
true
;
}
@Override
protected
void
onEvtDead
(
Creature killer
)
{
NpcInstance actor
=
getActor
(
)
;
if
(
actor
==
null
)
return
;
// actor.deleteMe();
_log
.
info
(
"убили гада"
)
;
super
.
onEvtDead
(
killer
)
;
}
}
|
|
|

06.04.2016, 08:18
|
|
Познавший АНТИЧАТ
Регистрация: 23.02.2015
Сообщений: 1,608
С нами:
5904866
Репутация:
2646
|
|
юзать слф4ж в 2016?
|
|
|

06.04.2016, 14:50
|
|
Постоянный
Регистрация: 13.04.2015
Сообщений: 454
С нами:
5834308
Репутация:
2128
|
|
ждо за высер?
|
|
|

06.04.2016, 16:34
|
|
Флудер
Регистрация: 20.01.2015
Сообщений: 7,201
С нами:
5952720
Репутация:
6527
|
|
?
|
|
|

06.04.2016, 16:35
|
|
Флудер
Регистрация: 20.01.2015
Сообщений: 7,201
С нами:
5952720
Репутация:
6527
|
|
Сообщение от Wolfer
Для основы взял 2 варианта исходного кода L2jserver и Overworld, оба варианта хроник High Five
Сразу удивило различие в конструктивности и построении основ, что я немного запутался т.к. опыта в этой сфере нет...
И щас какую то хрень пишешь.
|
|
|

06.04.2016, 16:37
|
|
Познавший АНТИЧАТ
Регистрация: 07.11.2015
Сообщений: 1,730
С нами:
5534362
Репутация:
1617
|
|
лыжа и овер несравнимы... даже такой наркОмэн как я это понимаю))))
|
|
|

07.04.2016, 07:00
|
|
Участник форума
Регистрация: 05.04.2016
Сообщений: 222
С нами:
5318817
Репутация:
0
|
|
:-D спасибо за конструктивную критику
|
|
|

07.04.2016, 10:29
|
|
Новичок
Регистрация: 21.01.2010
Сообщений: 0
С нами:
8581120
Репутация:
0
|
|
[OFF]Анциент, или растаруев. [/OFF]
Очередные прокси недерландов, львова и других стран.
|
|
|
|
 |
|
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|