Sync with L2jServer HighFive Oct 2nd 2015.

This commit is contained in:
MobiusDev 2015-10-03 13:41:08 +00:00
parent c90d8eb135
commit 7259087da5
18 changed files with 469 additions and 602 deletions

View File

@ -182,8 +182,9 @@ GrandChaosTime = 10
MinionChaosTime = 10
# It removes STR,CON... bonuses.
# With this npcs will use the stats given directly from the xml.
IgnoreNpcStatFormulas = True
# With this enabled npcs will use the stats given directly from the xml.
# Default: False
IgnoreNpcStatFormulas = False
# ---------------------------------------------------------------------------

View File

@ -224,10 +224,8 @@ public class GeoData
{
if (target.isDoor())
{
// can always see doors :o
return true;
}
return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId());
}

View File

@ -33,7 +33,6 @@ import com.l2jserver.gameserver.GameTimeController;
import com.l2jserver.gameserver.GeoData;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.data.sql.impl.TerritoryTable;
import com.l2jserver.gameserver.data.xml.impl.NpcData;
import com.l2jserver.gameserver.enums.AISkillScope;
import com.l2jserver.gameserver.enums.AIType;
import com.l2jserver.gameserver.model.L2Object;
@ -51,7 +50,6 @@ import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2RaidBossInstance;
import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.effects.L2EffectType;
import com.l2jserver.gameserver.model.events.EventDispatcher;
import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableFactionCall;
@ -66,6 +64,7 @@ import com.l2jserver.util.Rnd;
/**
* This class manages AI of L2Attackable.
* @author Zoey76
*/
public class L2AttackableAI extends L2CharacterAI implements Runnable
{
@ -111,7 +110,6 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
private int _timePass = 0;
private int _chaosTime = 0;
private final L2NpcTemplate _skillrender;
private int _lastBuffTick;
// Fear parameters
private int _fearTime;
@ -124,7 +122,6 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
public L2AttackableAI(L2Attackable creature)
{
super(creature);
_skillrender = NpcData.getInstance().getTemplate(getActiveChar().getTemplate().getId());
_attackTimeout = Integer.MAX_VALUE;
_globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
}
@ -370,11 +367,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
super.changeIntention(AI_INTENTION_IDLE, null, null);
// Stop AI task and detach AI from NPC
if (_aiTask != null)
{
_aiTask.cancel(true);
_aiTask = null;
}
stopAITask();
// Cancel the AI
_actor.detachAI();
@ -403,11 +396,18 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
// self and buffs
if ((_lastBuffTick + 30) < GameTimeController.getInstance().getGameTicks())
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.BUFF))
for (Skill buff : getActiveChar().getTemplate().getAISkills(AISkillScope.BUFF))
{
if (cast(sk))
if (checkSkillCastConditions(getActiveChar(), buff))
{
break;
if (!_actor.isAffectedBySkill(buff.getId()))
{
_actor.setTarget(_actor);
_actor.doCast(buff);
_actor.setTarget(target);
_log.info(this.getActor().getName() + "used buff skill " + buff.getName() + " on " + _actor.getName());
break;
}
}
}
_lastBuffTick = GameTimeController.getInstance().getGameTicks();
@ -634,7 +634,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
else if (Rnd.nextInt(RANDOM_WALK_RATE) == 0)
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.BUFF))
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.BUFF))
{
if (cast(sk))
{
@ -651,7 +651,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
int z1 = 0;
final int range = Config.MAX_DRIFT_RANGE;
for (Skill sk : _skillrender.getAISkills(AISkillScope.BUFF))
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.BUFF))
{
if (cast(sk))
{
@ -659,7 +659,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
}
// If NPC with random coord in territory - old method (for backward compartibility)
// If NPC with random coord in territory - old method (for backward compatibility)
if ((npc.getSpawn().getX() == 0) && (npc.getSpawn().getY() == 0) && (npc.getSpawn().getSpawnTerritory() == null))
{
// Calculate a destination point in the spawn area
@ -708,8 +708,6 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
z1 = npc.getZ();
}
}
// _log.debug("Current pos ("+getX()+", "+getY()+"), moving to ("+x1+", "+y1+").");
// Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast)
final Location moveLoc = GeoData.getInstance().moveCheck(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceId());
@ -725,9 +723,8 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
* <li>Call all L2Object of its Faction inside the Faction Range</li>
* <li>Chose a target and order to attack it with magic skill or physical attack</li>
* </ul>
* TODO: Manage casting rules to healer mobs (like Ant Nurses)
*/
protected void thinkAttack()
protected synchronized void thinkAttack()
{
final L2Attackable npc = getActiveChar();
if (npc.isCastingNow())
@ -774,13 +771,11 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
Set<Integer> clans = getActiveChar().getTemplate().getClans();
if ((clans != null) && !clans.isEmpty())
{
int factionRange = npc.getTemplate().getClanHelpRange() + collision;
final int factionRange = npc.getTemplate().getClanHelpRange() + collision;
// Go through all L2Object that belong to its faction
Collection<L2Object> objs = npc.getKnownList().getKnownObjects().values();
try
{
for (L2Object obj : objs)
for (L2Object obj : npc.getKnownList().getKnownCharactersInRadius(factionRange))
{
if (obj instanceof L2Npc)
{
@ -792,7 +787,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
// Check if the L2Object is inside the Faction Range of the actor
if (npc.isInsideRadius(called, factionRange, true, false) && called.hasAI())
if (called.hasAI())
{
if ((Math.abs(originalAttackTarget.getZ() - called.getZ()) < 600) && npc.getAttackByList().contains(originalAttackTarget) && ((called.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (called.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE)) && (called.getInstanceId() == npc.getInstanceId()))
{
@ -815,7 +810,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
catch (NullPointerException e)
{
_log.warning(getClass().getSimpleName() + ": thinkAttack() faction call failed: " + e.getMessage());
_log.warning(getClass().getSimpleName() + ": There has been a problem trying to think the attack!");
}
}
@ -837,30 +832,22 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
final int combinedCollision = collision + mostHate.getTemplate().getCollisionRadius();
List<Skill> aiSuicideSkills = _skillrender.getAISkills(AISkillScope.SUICIDE);
final List<Skill> aiSuicideSkills = npc.getTemplate().getAISkills(AISkillScope.SUICIDE);
if (!aiSuicideSkills.isEmpty() && ((int) ((npc.getCurrentHp() / npc.getMaxHp()) * 100) < 30))
{
final Skill skill = aiSuicideSkills.get(Rnd.nextInt(aiSuicideSkills.size()));
if (Util.checkIfInRange(skill.getAffectRange(), getActiveChar(), mostHate, false) && (Rnd.get(100) < Rnd.get(npc.getMinSkillChance(), npc.getMaxSkillChance())))
final Skill skill = aiSuicideSkills.get(Rnd.get(aiSuicideSkills.size()));
if (Util.checkIfInRange(skill.getAffectRange(), getActiveChar(), mostHate, false) && npc.hasSkillChance())
{
if (cast(skill))
{
_log.info(this.getActor().getName() + " used suicide skill " + skill.getName());
return;
}
for (Skill sk : aiSuicideSkills)
{
if (cast(sk))
{
return;
}
}
}
}
// ------------------------------------------------------
// In case many mobs are trying to hit from same place, move a bit,
// circling around the target
// In case many mobs are trying to hit from same place, move a bit, circling around the target
// Note from Gnacik:
// On l2js because of that sometimes mobs don't attack player only running
// around player without any sense, so decrease chance for now
@ -1000,11 +987,12 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
}
if (!_skillrender.getAISkills(AISkillScope.GENERAL).isEmpty())
final List<Skill> generalSkills = npc.getTemplate().getAISkills(AISkillScope.GENERAL);
if (!generalSkills.isEmpty())
{
// -------------------------------------------------------------------------------
// Heal Condition
List<Skill> aiHealSkills = _skillrender.getAISkills(AISkillScope.HEAL);
final List<Skill> aiHealSkills = npc.getTemplate().getAISkills(AISkillScope.HEAL);
if (!aiHealSkills.isEmpty())
{
double percentage = (npc.getCurrentHp() / npc.getMaxHp()) * 100;
@ -1013,52 +1001,64 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
L2Character leader = npc.getLeader();
if ((leader != null) && !leader.isDead() && (Rnd.get(100) > ((leader.getCurrentHp() / leader.getMaxHp()) * 100)))
{
for (Skill sk : aiHealSkills)
for (Skill healSkill : aiHealSkills)
{
if (sk.getTargetType() == L2TargetType.SELF)
if (healSkill.getTargetType() == L2TargetType.SELF)
{
continue;
}
if (!checkSkillCastConditions(sk))
if (!checkSkillCastConditions(npc, healSkill))
{
continue;
}
if (!Util.checkIfInRange((sk.getCastRange() + collision + leader.getTemplate().getCollisionRadius()), npc, leader, false) && !isParty(sk) && !npc.isMovementDisabled())
if (!Util.checkIfInRange((healSkill.getCastRange() + collision + leader.getTemplate().getCollisionRadius()), npc, leader, false) && !isParty(healSkill) && !npc.isMovementDisabled())
{
moveToPawn(leader, sk.getCastRange() + collision + leader.getTemplate().getCollisionRadius());
moveToPawn(leader, healSkill.getCastRange() + collision + leader.getTemplate().getCollisionRadius());
return;
}
if (GeoData.getInstance().canSeeTarget(npc, leader))
{
clientStopMoving(null);
final L2Object target = npc.getTarget();
npc.setTarget(leader);
clientStopMoving(null);
npc.doCast(sk);
npc.doCast(healSkill);
npc.setTarget(target);
_log.info(this.getActor().getName() + " used heal skill " + healSkill.getName() + " on leader " + leader.getName());
return;
}
}
}
}
if (Rnd.get(100) < ((100 - percentage) / 3))
{
for (Skill sk : aiHealSkills)
{
if (!checkSkillCastConditions(sk))
if (!checkSkillCastConditions(npc, sk))
{
continue;
}
clientStopMoving(null);
final L2Object target = npc.getTarget();
npc.setTarget(npc);
npc.doCast(sk);
npc.setTarget(target);
_log.info(this.getActor().getName() + " used heal skill " + sk.getName() + " on itself");
return;
}
}
for (Skill sk : aiHealSkills)
{
if (!checkSkillCastConditions(sk))
if (!checkSkillCastConditions(npc, sk))
{
continue;
}
if (sk.getTargetType() == L2TargetType.ONE)
{
for (L2Character obj : npc.getKnownList().getKnownCharactersInRadius(sk.getCastRange() + collision))
@ -1068,24 +1068,29 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
continue;
}
L2Attackable targets = ((L2Attackable) obj);
if (!((L2Attackable) obj).isInMyClan(npc))
final L2Attackable targets = (L2Attackable) obj;
if (!targets.isInMyClan(npc))
{
continue;
}
percentage = (targets.getCurrentHp() / targets.getMaxHp()) * 100;
if (Rnd.get(100) < ((100 - percentage) / 10))
{
if (GeoData.getInstance().canSeeTarget(npc, targets))
{
clientStopMoving(null);
final L2Object target = npc.getTarget();
npc.setTarget(obj);
npc.doCast(sk);
npc.setTarget(target);
_log.info(this.getActor().getName() + " used heal skill " + sk.getName() + " on " + obj.getName());
return;
}
}
}
}
if (isParty(sk))
{
clientStopMoving(null);
@ -1094,9 +1099,10 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
}
}
// -------------------------------------------------------------------------------
// Res Skill Condition
List<Skill> aiResSkills = _skillrender.getAISkills(AISkillScope.RES);
final List<Skill> aiResSkills = npc.getTemplate().getAISkills(AISkillScope.RES);
if (!aiResSkills.isEmpty())
{
if (npc.isMinion())
@ -1110,28 +1116,35 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
{
continue;
}
if (!checkSkillCastConditions(sk))
if (!checkSkillCastConditions(npc, sk))
{
continue;
}
if (!Util.checkIfInRange((sk.getCastRange() + collision + leader.getTemplate().getCollisionRadius()), npc, leader, false) && !isParty(sk) && !npc.isMovementDisabled())
{
moveToPawn(leader, sk.getCastRange() + collision + leader.getTemplate().getCollisionRadius());
return;
}
if (GeoData.getInstance().canSeeTarget(npc, leader))
{
clientStopMoving(null);
final L2Object target = npc.getTarget();
npc.setTarget(leader);
npc.doCast(sk);
npc.setTarget(target);
_log.info(this.getActor().getName() + " used resurrection skill " + sk.getName() + " on leader " + leader.getName());
return;
}
}
}
}
for (Skill sk : aiResSkills)
{
if (!checkSkillCastConditions(sk))
if (!checkSkillCastConditions(npc, sk))
{
continue;
}
@ -1154,20 +1167,25 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
if (GeoData.getInstance().canSeeTarget(npc, targets))
{
clientStopMoving(null);
final L2Object target = npc.getTarget();
npc.setTarget(obj);
npc.doCast(sk);
npc.setTarget(target);
_log.info(this.getActor().getName() + " used heal skill " + sk.getName() + " on clan member " + obj.getName());
return;
}
}
}
}
if (isParty(sk))
{
clientStopMoving(null);
L2Object target = getAttackTarget();
final L2Object target = npc.getTarget();
npc.setTarget(npc);
npc.doCast(sk);
npc.setTarget(target);
_log.info(this.getActor().getName() + " used heal skill " + sk.getName() + " on party");
return;
}
}
@ -1195,69 +1213,35 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
setTimepass(0);
// --------------------------------------------------------------------------------
// Skill Use
List<Skill> aiGeneralSkills = _skillrender.getAISkills(AISkillScope.GENERAL);
if (!aiGeneralSkills.isEmpty())
// Long/Short Range skill usage.
if (!npc.getShortRangeSkills().isEmpty() && npc.hasSkillChance())
{
if (Rnd.get(100) < Rnd.get(npc.getMinSkillChance(), npc.getMaxSkillChance()))
final Skill shortRangeSkill = npc.getShortRangeSkills().get(Rnd.get(npc.getShortRangeSkills().size()));
if (checkSkillCastConditions(npc, shortRangeSkill))
{
Skill skills = aiGeneralSkills.get(Rnd.nextInt(aiGeneralSkills.size()));
if (cast(skills))
{
return;
}
for (Skill sk : aiGeneralSkills)
{
if (cast(sk))
{
return;
}
}
clientStopMoving(null);
npc.doCast(shortRangeSkill);
_log.info(this.getActor().getName() + " used short range skill " + shortRangeSkill.getName() + " on " + npc.getTarget().getName());
return;
}
// --------------------------------------------------------------------------------
// Long/Short Range skill usage.
if (npc.hasLSkill() || npc.hasSSkill())
}
if (!npc.getLongRangeSkills().isEmpty() && npc.hasSkillChance())
{
final Skill longRangeSkill = npc.getLongRangeSkills().get(Rnd.get(npc.getLongRangeSkills().size()));
if (checkSkillCastConditions(npc, longRangeSkill))
{
final List<Skill> shortRangeSkills = shortRangeSkillRender();
if (!shortRangeSkills.isEmpty() && npc.hasSSkill() && (dist2 <= 150) && (Rnd.get(100) <= npc.getSSkillChance()))
{
final Skill shortRangeSkill = shortRangeSkills.get(Rnd.get(shortRangeSkills.size()));
if ((shortRangeSkill != null) && cast(shortRangeSkill))
{
return;
}
for (Skill sk : shortRangeSkills)
{
if ((sk != null) && cast(sk))
{
return;
}
}
}
final List<Skill> longRangeSkills = longRangeSkillRender();
if (!longRangeSkills.isEmpty() && npc.hasLSkill() && (dist2 > 150) && (Rnd.get(100) <= npc.getLSkillChance()))
{
final Skill longRangeSkill = longRangeSkills.get(Rnd.get(longRangeSkills.size()));
if ((longRangeSkill != null) && cast(longRangeSkill))
{
return;
}
for (Skill sk : longRangeSkills)
{
if ((sk != null) && cast(sk))
{
return;
}
}
}
clientStopMoving(null);
npc.doCast(longRangeSkill);
_log.info(this.getActor().getName() + " used long range skill " + longRangeSkill.getName() + " on " + npc.getTarget().getName());
return;
}
}
// --------------------------------------------------------------------------------
// Starts Melee or Primary Skill
// Starts melee attack
if ((dist2 > range) || !GeoData.getInstance().canSeeTarget(npc, mostHate))
{
if (npc.isMovementDisabled())
@ -1279,54 +1263,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
return;
}
melee(npc.getPrimarySkillId());
}
private void melee(int type)
{
if (type != 0)
{
switch (type)
{
case -1:
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.GENERAL))
{
if (cast(sk))
{
return;
}
}
break;
}
case 1:
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.ATTACK))
{
if (cast(sk))
{
return;
}
}
break;
}
default:
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.GENERAL))
{
if (sk.getId() == getActiveChar().getPrimarySkillId())
{
if (cast(sk))
{
return;
}
}
}
break;
}
}
}
// Attacks target
_actor.doAttack(getAttackTarget());
}
@ -1338,16 +1275,11 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
final L2Attackable caster = getActiveChar();
if (caster.isCastingNow() && !sk.isSimultaneousCast())
if (!checkSkillCastConditions(caster, sk))
{
return false;
}
if (!checkSkillCastConditions(sk))
{
return false;
}
if (getAttackTarget() == null)
{
if (caster.getMostHated() != null)
@ -1829,6 +1761,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
/**
* This AI task will start when ACTOR cannot move and attack range larger than distance
*/
// TODO(Zoey76): Rework this method.
private void movementDisable()
{
final L2Attackable npc = getActiveChar();
@ -1854,16 +1787,16 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
// Check if activeChar has any skill
if (!_skillrender.getAISkills(AISkillScope.GENERAL).isEmpty())
if (!npc.getTemplate().getAISkills(AISkillScope.GENERAL).isEmpty())
{
// -------------------------------------------------------------
// Try to stop the target or disable the target as priority
int random = Rnd.get(100);
if (!getAttackTarget().isImmobilized() && (random < 2))
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.IMMOBILIZE))
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.IMMOBILIZE))
{
if (!checkSkillCastConditions(sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
if (!checkSkillCastConditions(npc, sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
{
continue;
}
@ -1886,9 +1819,9 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
// Same as Above, but with Mute/FEAR etc....
if (random < 5)
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.COT))
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.COT))
{
if (!checkSkillCastConditions(sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
if (!checkSkillCastConditions(npc, sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
{
continue;
}
@ -1910,9 +1843,9 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
// -------------------------------------------------------------
if (random < 8)
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.DEBUFF))
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.DEBUFF))
{
if (!checkSkillCastConditions(sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
if (!checkSkillCastConditions(npc, sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
{
continue;
}
@ -1935,9 +1868,9 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
// Some side effect skill like CANCEL or NEGATE
if (random < 9)
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.NEGATIVE))
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.NEGATIVE))
{
if (!checkSkillCastConditions(sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
if (!checkSkillCastConditions(npc, sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
{
continue;
}
@ -1960,9 +1893,9 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
// Start ATK SKILL when nothing can be done
if ((npc.isMovementDisabled() || (npc.getAiType() == AIType.MAGE) || (npc.getAiType() == AIType.HEALER)))
{
for (Skill sk : _skillrender.getAISkills(AISkillScope.ATTACK))
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.ATTACK))
{
if (!checkSkillCastConditions(sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
if (!checkSkillCastConditions(npc, sk) || (((sk.getCastRange() + npc.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius()) <= dist2) && !canAura(sk)))
{
continue;
}
@ -2034,22 +1967,29 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
melee(npc.getPrimarySkillId());
// Attacks target
_actor.doAttack(getAttackTarget());
}
catch (NullPointerException e)
{
setIntention(AI_INTENTION_ACTIVE);
_log.warning(getClass().getSimpleName() + ": " + this + " - failed executing movementDisable(): " + e.getMessage());
_log.warning(getClass().getSimpleName() + ": " + this.getActor().getName() + " - failed executing movementDisable()!");
return;
}
}
/**
* @param caster the caster
* @param skill the skill to check.
* @return {@code true} if the skill is available for casting {@code false} otherwise.
*/
private boolean checkSkillCastConditions(Skill skill)
private boolean checkSkillCastConditions(L2Attackable caster, Skill skill)
{
if (caster.isCastingNow() && !skill.isSimultaneousCast())
{
return false;
}
// Not enough MP.
if (skill.getMpConsume() >= getActiveChar().getCurrentMp())
{
@ -2550,26 +2490,6 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
}
private List<Skill> longRangeSkillRender()
{
List<Skill> longRangeSkills = _skillrender.getAISkills(AISkillScope.LONG_RANGE);
if (longRangeSkills.isEmpty())
{
longRangeSkills = getActiveChar().getLongRangeSkill();
}
return longRangeSkills;
}
private List<Skill> shortRangeSkillRender()
{
List<Skill> shortRangeSkills = _skillrender.getAISkills(AISkillScope.SHORT_RANGE);
if (shortRangeSkills.isEmpty())
{
shortRangeSkills = getActiveChar().getShortRangeSkill();
}
return shortRangeSkills;
}
/**
* Manage AI thinking actions of a L2Attackable.
*/
@ -2603,7 +2523,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
}
catch (Exception e)
{
_log.warning(getClass().getSimpleName() + ": " + this + " - onEvtThink() failed: " + e.getMessage());
_log.warning(getClass().getSimpleName() + ": " + this.getActor().getName() + " - onEvtThink() failed!");
}
finally
{

View File

@ -1629,7 +1629,7 @@ public class L2CharacterAI extends AbstractAI
public boolean canParty(Skill sk)
{
if (sk.getTargetType() == L2TargetType.PARTY)
if (isParty(sk))
{
int count = 0;
int ccount = 0;

View File

@ -509,98 +509,100 @@ public class NpcData implements IXmlReader
Map<AISkillScope, List<Skill>> aiSkillLists = null;
for (Skill skill : skills.values())
{
if (!skill.isPassive())
if (skill.isPassive())
{
if (aiSkillLists == null)
{
aiSkillLists = new EnumMap<>(AISkillScope.class);
}
continue;
}
if (aiSkillLists == null)
{
aiSkillLists = new EnumMap<>(AISkillScope.class);
}
final List<AISkillScope> aiSkillScopes = new ArrayList<>();
final AISkillScope shortOrLongRangeScope = skill.getCastRange() <= 150 ? AISkillScope.SHORT_RANGE : AISkillScope.SHORT_RANGE;
if (skill.isSuicideAttack())
{
aiSkillScopes.add(AISkillScope.SUICIDE);
}
else
{
aiSkillScopes.add(AISkillScope.GENERAL);
List<AISkillScope> aiSkillScopes = new ArrayList<>();
final AISkillScope shortOrLongRangeScope = skill.getCastRange() <= 150 ? AISkillScope.SHORT_RANGE : AISkillScope.SHORT_RANGE;
if (skill.isSuicideAttack())
if (skill.isContinuous())
{
aiSkillScopes.add(AISkillScope.SUICIDE);
}
else
{
aiSkillScopes.add(AISkillScope.GENERAL);
if (skill.isContinuous())
if (!skill.isDebuff())
{
if (!skill.isDebuff())
{
aiSkillScopes.add(AISkillScope.BUFF);
}
else
{
aiSkillScopes.add(AISkillScope.DEBUFF);
aiSkillScopes.add(AISkillScope.COT);
aiSkillScopes.add(shortOrLongRangeScope);
}
aiSkillScopes.add(AISkillScope.BUFF);
}
else
{
if (skill.hasEffectType(L2EffectType.DISPEL, L2EffectType.DISPEL_BY_SLOT))
{
aiSkillScopes.add(AISkillScope.NEGATIVE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.HEAL))
{
aiSkillScopes.add(AISkillScope.HEAL);
}
else if (skill.hasEffectType(L2EffectType.PHYSICAL_ATTACK, L2EffectType.PHYSICAL_ATTACK_HP_LINK, L2EffectType.MAGICAL_ATTACK, L2EffectType.DEATH_LINK, L2EffectType.HP_DRAIN))
{
aiSkillScopes.add(AISkillScope.ATTACK);
aiSkillScopes.add(AISkillScope.UNIVERSAL);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.SLEEP))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
}
else if (skill.hasEffectType(L2EffectType.STUN, L2EffectType.ROOT))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.MUTE, L2EffectType.FEAR))
{
aiSkillScopes.add(AISkillScope.COT);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.PARALYZE))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.DMG_OVER_TIME, L2EffectType.DMG_OVER_TIME_PERCENT))
{
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.RESURRECTION))
{
aiSkillScopes.add(AISkillScope.RES);
}
else
{
aiSkillScopes.add(AISkillScope.UNIVERSAL);
}
aiSkillScopes.add(AISkillScope.DEBUFF);
aiSkillScopes.add(AISkillScope.COT);
aiSkillScopes.add(shortOrLongRangeScope);
}
}
else
{
if (skill.hasEffectType(L2EffectType.DISPEL, L2EffectType.DISPEL_BY_SLOT))
{
aiSkillScopes.add(AISkillScope.NEGATIVE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.HEAL))
{
aiSkillScopes.add(AISkillScope.HEAL);
}
else if (skill.hasEffectType(L2EffectType.PHYSICAL_ATTACK, L2EffectType.PHYSICAL_ATTACK_HP_LINK, L2EffectType.MAGICAL_ATTACK, L2EffectType.DEATH_LINK, L2EffectType.HP_DRAIN))
{
aiSkillScopes.add(AISkillScope.ATTACK);
aiSkillScopes.add(AISkillScope.UNIVERSAL);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.SLEEP))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
}
else if (skill.hasEffectType(L2EffectType.STUN, L2EffectType.ROOT))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.MUTE, L2EffectType.FEAR))
{
aiSkillScopes.add(AISkillScope.COT);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.PARALYZE))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.DMG_OVER_TIME, L2EffectType.DMG_OVER_TIME_PERCENT))
{
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.RESURRECTION))
{
aiSkillScopes.add(AISkillScope.RES);
}
else
{
aiSkillScopes.add(AISkillScope.UNIVERSAL);
}
}
}
for (AISkillScope aiSkillScope : aiSkillScopes)
{
List<Skill> aiSkills = aiSkillLists.get(aiSkillScope);
if (aiSkills == null)
{
aiSkills = new ArrayList<>();
aiSkillLists.put(aiSkillScope, aiSkills);
}
for (AISkillScope aiSkillScope : aiSkillScopes)
{
List<Skill> aiSkills = aiSkillLists.get(aiSkillScope);
if (aiSkills == null)
{
aiSkills = new ArrayList<>();
aiSkillLists.put(aiSkillScope, aiSkills);
}
aiSkills.add(skill);
}
aiSkills.add(skill);
}
}

View File

@ -48,7 +48,7 @@ import com.l2jserver.util.Rnd;
*/
public class AugmentationData
{
// Zoey76: TODO: Implement using IXmlReader.
// TODO(Zoey76): Implement using IXmlReader.
private static final Logger LOGGER = Logger.getLogger(AugmentationData.class.getName());
// stats
@ -350,7 +350,6 @@ public class AugmentationData
{
NamedNodeMap aNodeAttributes = null;
// System.out.println("We're going through the list now.");
for (Node n = l.getFirstChild(); n != null; n = n.getNextSibling())
{
if (n.getNodeName().equals("weapon"))
@ -359,7 +358,6 @@ public class AugmentationData
aWeaponType = aNodeAttributes.getNamedItem("type").getNodeValue();
// System.out.println("Now showing Augmentations for " + aWeaponType + " Weapons.");
for (Node c = n.getFirstChild(); c != null; c = c.getNextSibling())
{
if (c.getNodeName().equals("stone"))
@ -384,7 +382,6 @@ public class AugmentationData
aCategoryChance = Integer.parseInt(aNodeAttributes.getNamedItem("probability").getNodeValue());
// System.out.println("Stone Id: " + aStoneId + ", Variation Id: " + aVariationId + ", Category Chances: " + aCategoryChance);
for (Node e = j.getFirstChild(); e != null; e = e.getNextSibling())
{
if (e.getNodeName().equals("augment"))

View File

@ -18,6 +18,8 @@
*/
package com.l2jserver.gameserver.instancemanager;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@ -26,9 +28,11 @@ import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.entity.Duel;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jserver.util.Rnd;
public final class DuelManager
{
private static final List<String> ARENAS = Arrays.asList("OlympiadGrassyArena.xml", "OlympiadHerossVestigesArena.xml", "OlympiadOrbisArena.xml", "OlympiadThreeBridgesArena.xml");
private final Map<Integer, Duel> _duels = new ConcurrentHashMap<>();
private final AtomicInteger _currentDuelId = new AtomicInteger();
@ -209,6 +213,15 @@ public final class DuelManager
}
}
/**
* Gets new a random Olympiad Stadium instance name.
* @return an instance name
*/
public String getDuelArena()
{
return ARENAS.get(Rnd.get(ARENAS.size()));
}
public static final DuelManager getInstance()
{
return SingletonHolder._instance;

View File

@ -362,7 +362,7 @@ public class StatsSet implements IParserAdvUtils
Object val = _set.get(key);
if (val == null)
{
throw new IllegalArgumentException("Integer value required, but not specified");
throw new IllegalArgumentException("Long value required, but not specified");
}
if (val instanceof Number)
{
@ -374,7 +374,7 @@ public class StatsSet implements IParserAdvUtils
}
catch (Exception e)
{
throw new IllegalArgumentException("Integer value required, but found: " + val);
throw new IllegalArgumentException("Long value required, but found: " + val);
}
}
@ -396,7 +396,7 @@ public class StatsSet implements IParserAdvUtils
}
catch (Exception e)
{
throw new IllegalArgumentException("Integer value required, but found: " + val);
throw new IllegalArgumentException("Long value required, but found: " + val);
}
}
@ -414,7 +414,7 @@ public class StatsSet implements IParserAdvUtils
}
try
{
return (float) Double.parseDouble((String) val);
return Float.parseFloat((String) val);
}
catch (Exception e)
{
@ -436,7 +436,7 @@ public class StatsSet implements IParserAdvUtils
}
try
{
return (float) Double.parseDouble((String) val);
return Float.parseFloat((String) val);
}
catch (Exception e)
{
@ -450,7 +450,7 @@ public class StatsSet implements IParserAdvUtils
Object val = _set.get(key);
if (val == null)
{
throw new IllegalArgumentException("Float value required, but not specified");
throw new IllegalArgumentException("Double value required, but not specified");
}
if (val instanceof Number)
{
@ -462,7 +462,7 @@ public class StatsSet implements IParserAdvUtils
}
catch (Exception e)
{
throw new IllegalArgumentException("Float value required, but found: " + val);
throw new IllegalArgumentException("Double value required, but found: " + val);
}
}
@ -484,7 +484,7 @@ public class StatsSet implements IParserAdvUtils
}
catch (Exception e)
{
throw new IllegalArgumentException("Float value required, but found: " + val);
throw new IllegalArgumentException("Double value required, but found: " + val);
}
}
@ -607,7 +607,7 @@ public class StatsSet implements IParserAdvUtils
Object obj = _set.get(key);
if ((obj == null) || !(obj instanceof List<?>))
{
return Collections.EMPTY_LIST;
return Collections.emptyList();
}
return (List<MinionHolder>) obj;

View File

@ -111,6 +111,7 @@ public class L2Attackable extends L2Npc
// Misc
private boolean _mustGiveExpSp;
protected int _onKillDelay = 5000;
private long _lastAttack;
/**
* Creates an attackable NPC.
@ -1575,6 +1576,16 @@ public class L2Attackable extends L2Npc
return _onKillDelay;
}
public long getLastAttack()
{
return _lastAttack;
}
public void setLastAttack(long lastAttack)
{
_lastAttack = lastAttack;
}
/**
* Check if the server allows Random Animation.
*/

View File

@ -5409,7 +5409,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
// Remove all its Func objects from the L2Character calculator set
if (oldSkill != null)
{
// Stop casting if this skill is used right now
if ((getLastSkillCast() != null) && isCastingNow())
{
@ -5444,7 +5443,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
*/
public final Collection<Skill> getAllSkills()
{
return new ArrayList<>(_skills.values());
return _skills.values();
}
/**

View File

@ -20,7 +20,6 @@ package com.l2jserver.gameserver.model.actor;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -83,7 +82,6 @@ import com.l2jserver.gameserver.model.items.L2Weapon;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.olympiad.Olympiad;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.skills.targets.L2TargetType;
import com.l2jserver.gameserver.model.variables.NpcVariables;
import com.l2jserver.gameserver.model.zone.type.L2TownZone;
import com.l2jserver.gameserver.network.SystemMessageId;
@ -193,14 +191,6 @@ public class L2Npc extends L2Character
return getTemplate().getSpiritShotChance();
}
/**
* @return the primary attack skill Id
*/
public int getPrimarySkillId()
{
return getTemplate().getPrimarySkillId();
}
public int getMinSkillChance()
{
return getTemplate().getMinSkillChance();
@ -211,6 +201,15 @@ public class L2Npc extends L2Character
return getTemplate().getMaxSkillChance();
}
/**
* Verifies if the NPC can cast a skill given the minimum and maximum skill chances.
* @return {@code true} if the NPC has chances of casting a skill
*/
public boolean hasSkillChance()
{
return Rnd.get(100) < Rnd.get(getMinSkillChance(), getMaxSkillChance());
}
public boolean canMove()
{
return getTemplate().canMove();
@ -226,169 +225,57 @@ public class L2Npc extends L2Character
return getTemplate().getDodge();
}
public int getSSkillChance()
public List<Skill> getLongRangeSkills()
{
return getTemplate().getShortRangeSkillChance();
return getTemplate().getAISkills(AISkillScope.LONG_RANGE);
}
public int getLSkillChance()
public List<Skill> getShortRangeSkills()
{
return getTemplate().getLongRangeSkillChance();
}
public boolean hasLSkill()
{
return getTemplate().getLongRangeSkillId() > 0;
}
public boolean hasSSkill()
{
return getTemplate().getShortRangeSkillId() > 0;
}
public List<Skill> getLongRangeSkill()
{
final List<Skill> skilldata = new ArrayList<>();
if (getTemplate().getLongRangeSkillId() == 0)
{
return skilldata;
}
switch (getTemplate().getLongRangeSkillId())
{
case -1:
{
final Collection<Skill> skills = getAllSkills();
if (skills != null)
{
for (Skill sk : skills)
{
if ((sk == null) || sk.isPassive() || (sk.getTargetType() == L2TargetType.SELF))
{
continue;
}
if (sk.getCastRange() >= 200)
{
skilldata.add(sk);
}
}
}
break;
}
case 1:
{
for (Skill sk : getTemplate().getAISkills(AISkillScope.UNIVERSAL))
{
if (sk.getCastRange() >= 200)
{
skilldata.add(sk);
}
}
break;
}
default:
{
for (Skill sk : getAllSkills())
{
if (sk.getId() == getTemplate().getLongRangeSkillId())
{
skilldata.add(sk);
}
}
}
}
return skilldata;
}
public List<Skill> getShortRangeSkill()
{
final List<Skill> skilldata = new ArrayList<>();
if (getTemplate().getShortRangeSkillId() == 0)
{
return skilldata;
}
switch (getTemplate().getShortRangeSkillId())
{
case -1:
{
Collection<Skill> skills = getAllSkills();
if (skills != null)
{
for (Skill sk : skills)
{
if ((sk == null) || sk.isPassive() || (sk.getTargetType() == L2TargetType.SELF))
{
continue;
}
if (sk.getCastRange() <= 200)
{
skilldata.add(sk);
}
}
}
break;
}
case 1:
{
for (Skill sk : getTemplate().getAISkills(AISkillScope.UNIVERSAL))
{
if (sk.getCastRange() <= 200)
{
skilldata.add(sk);
}
}
break;
}
default:
{
for (Skill sk : getAllSkills())
{
if (sk.getId() == getTemplate().getShortRangeSkillId())
{
skilldata.add(sk);
}
}
}
}
return skilldata;
return getTemplate().getAISkills(AISkillScope.SHORT_RANGE);
}
/** Task launching the function onRandomAnimation() */
protected class RandomAnimationTask implements Runnable
protected static class RandomAnimationTask implements Runnable
{
private final L2Npc _npc;
protected RandomAnimationTask(L2Npc npc)
{
_npc = npc;
}
@Override
public void run()
{
try
{
if (isMob())
if (_npc.isMob())
{
// Cancel further animation timers until intention is changed to ACTIVE again.
if (getAI().getIntention() != AI_INTENTION_ACTIVE)
if (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)
{
return;
}
}
else
{
if (!isInActiveRegion())
if (!_npc.isInActiveRegion())
{
return;
}
}
if (!(isDead() || isStunned() || isSleeping() || isParalyzed()))
if (!(_npc.isDead() || _npc.isStunned() || _npc.isSleeping() || _npc.isParalyzed()))
{
onRandomAnimation(Rnd.get(2, 3));
_npc.onRandomAnimation(Rnd.get(2, 3));
}
startRandomAnimationTimer();
_npc.startRandomAnimationTimer();
}
catch (Exception e)
{
_log.log(Level.SEVERE, "", e);
_log.log(Level.SEVERE, "There has been an error trying to perform a random animation for NPC " + _npc.getId() + "!", e);
}
}
}
@ -425,7 +312,7 @@ public class L2Npc extends L2Character
int interval = Rnd.get(minWait, maxWait) * 1000;
// Create a RandomAnimation Task that will be launched after the calculated delay
_rAniTask = new RandomAnimationTask();
_rAniTask = new RandomAnimationTask(this);
ThreadPoolManager.getInstance().scheduleGeneral(_rAniTask, interval);
}

View File

@ -6774,8 +6774,8 @@ public final class L2PcInstance extends L2Playable
}
/**
* Set the _accessLevel of the L2PcInstance.
* @param level
* Set the access level for this player.
* @param level the access level
* @param broadcast
*/
public void setAccessLevel(int level, boolean broadcast)
@ -6793,7 +6793,7 @@ public final class L2PcInstance extends L2Playable
if (!AdminData.getInstance().hasAccessLevel(level))
{
_log.warning("Tryed to set unregistered access level " + level + " for " + toString() + ". Setting access level without privileges!");
_log.warning("Tried to set unregistered access level " + level + " for " + toString() + ". Setting access level without privileges!");
}
else if (level > 0)
{

View File

@ -89,11 +89,6 @@ public final class L2NpcTemplate extends L2CharTemplate implements IIdentifiable
private int _spiritShotChance;
private int _minSkillChance;
private int _maxSkillChance;
private int _primarySkillId;
private int _shortRangeSkillId;
private int _shortRangeSkillChance;
private int _longRangeSkillId;
private int _longRangeSkillChance;
private Map<Integer, Skill> _skills;
private Map<AISkillScope, List<Skill>> _aiSkillLists;
private Set<Integer> _clans;
@ -166,11 +161,6 @@ public final class L2NpcTemplate extends L2CharTemplate implements IIdentifiable
_minSkillChance = set.getInt("minSkillChance", 7);
_maxSkillChance = set.getInt("maxSkillChance", 15);
_primarySkillId = set.getInt("primarySkillId", 0);
_shortRangeSkillId = set.getInt("shortRangeSkillId", 0);
_shortRangeSkillChance = set.getInt("shortRangeSkillChance", 0);
_longRangeSkillId = set.getInt("longRangeSkillId", 0);
_longRangeSkillChance = set.getInt("longRangeSkillChance", 0);
_collisionRadiusGrown = set.getDouble("collisionRadiusGrown", 0);
_collisionHeightGrown = set.getDouble("collisionHeightGrown", 0);
@ -397,31 +387,6 @@ public final class L2NpcTemplate extends L2CharTemplate implements IIdentifiable
return _maxSkillChance;
}
public int getPrimarySkillId()
{
return _primarySkillId;
}
public int getShortRangeSkillId()
{
return _shortRangeSkillId;
}
public int getShortRangeSkillChance()
{
return _shortRangeSkillChance;
}
public int getLongRangeSkillId()
{
return _longRangeSkillId;
}
public int getLongRangeSkillChance()
{
return _longRangeSkillChance;
}
@Override
public Map<Integer, Skill> getSkills()
{
@ -430,18 +395,17 @@ public final class L2NpcTemplate extends L2CharTemplate implements IIdentifiable
public void setSkills(Map<Integer, Skill> skills)
{
_skills = skills != null ? Collections.unmodifiableMap(skills) : Collections.<Integer, Skill> emptyMap();
_skills = skills != null ? Collections.unmodifiableMap(skills) : Collections.emptyMap();
}
public List<Skill> getAISkills(AISkillScope aiSkillScope)
{
final List<Skill> aiSkills = _aiSkillLists.get(aiSkillScope);
return aiSkills != null ? aiSkills : Collections.<Skill> emptyList();
return _aiSkillLists.getOrDefault(aiSkillScope, Collections.emptyList());
}
public void setAISkillLists(Map<AISkillScope, List<Skill>> aiSkillLists)
{
_aiSkillLists = aiSkillLists != null ? Collections.unmodifiableMap(aiSkillLists) : Collections.<AISkillScope, List<Skill>> emptyMap();
_aiSkillLists = aiSkillLists != null ? Collections.unmodifiableMap(aiSkillLists) : Collections.emptyMap();
}
public Set<Integer> getClans()

View File

@ -20,7 +20,9 @@ package com.l2jserver.gameserver.model.entity;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -29,10 +31,17 @@ import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.enums.DuelResult;
import com.l2jserver.gameserver.enums.Team;
import com.l2jserver.gameserver.instancemanager.DuelManager;
import com.l2jserver.gameserver.instancemanager.InstanceManager;
import com.l2jserver.gameserver.instancemanager.ZoneManager;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.L2Summon;
import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jserver.gameserver.model.actor.instance.L2OlympiadManagerInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.zone.ZoneId;
import com.l2jserver.gameserver.model.zone.type.L2OlympiadStadiumZone;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
import com.l2jserver.gameserver.network.serverpackets.ExDuelEnd;
@ -43,6 +52,7 @@ import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jserver.gameserver.network.serverpackets.PlaySound;
import com.l2jserver.gameserver.network.serverpackets.SocialAction;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.util.Rnd;
public class Duel
{
@ -56,16 +66,20 @@ public class Duel
private static final PlaySound B04_S01 = new PlaySound(1, "B04_S01", 0, 0, 0, 0, 0);
private static final int PARTY_DUEL_DURATION = 300;
private static final int PLAYER_DUEL_DURATION = 120;
private final int _duelId;
private L2PcInstance _playerA;
private L2PcInstance _playerB;
private final boolean _partyDuel;
private final Calendar _duelEndTime;
private int _surrenderRequest = 0;
private int _countdown = 4;
private int _countdown = 5;
private boolean _finished = false;
private final List<PlayerCondition> _playerConditions = new CopyOnWriteArrayList<>();
private final Map<Integer, PlayerCondition> _playerConditions = new ConcurrentHashMap<>();
private int _duelInstanceId;
public Duel(L2PcInstance playerA, L2PcInstance playerB, int partyDuel, int duelId)
{
@ -75,21 +89,12 @@ public class Duel
_partyDuel = partyDuel == 1 ? true : false;
_duelEndTime = Calendar.getInstance();
if (_partyDuel)
{
_duelEndTime.add(Calendar.SECOND, 300);
}
else
{
_duelEndTime.add(Calendar.SECOND, 120);
}
_duelEndTime.add(Calendar.SECOND, _partyDuel ? PARTY_DUEL_DURATION : PLAYER_DUEL_DURATION);
setFinished(false);
if (_partyDuel)
{
// increase countdown so that start task can teleport players
_countdown++;
// inform players that they will be ported shortly
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.IN_A_MOMENT_YOU_WILL_BE_TRANSPORTED_TO_THE_SITE_WHERE_THE_DUEL_WILL_TAKE_PLACE);
broadcastToTeam1(sm);
@ -107,7 +112,7 @@ public class Duel
private double _cp;
private boolean _paDuel;
private int _x, _y, _z;
private List<Skill> _debuffs;
private Set<Skill> _debuffs;
public PlayerCondition(L2PcInstance player, boolean partyDuel)
{
@ -159,7 +164,7 @@ public class Duel
{
if (_debuffs == null)
{
_debuffs = new CopyOnWriteArrayList<>();
_debuffs = ConcurrentHashMap.newKeySet();
}
_debuffs.add(debuff);
@ -169,7 +174,7 @@ public class Duel
{
if (_paDuel)
{
_player.teleToLocation(new Location(_x, _y, _z));
_player.teleToLocation(_x, _y, _z);
}
}
@ -193,28 +198,33 @@ public class Duel
{
try
{
DuelResult status = _duel.checkEndDuelCondition();
if (status == DuelResult.Canceled)
switch (_duel.checkEndDuelCondition())
{
// do not schedule duel end if it was interrupted
setFinished(true);
_duel.endDuel(status);
}
else if (status != DuelResult.Continue)
{
setFinished(true);
playKneelAnimation();
ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleEndDuelTask(_duel, status), 5000);
}
else
{
ThreadPoolManager.getInstance().scheduleGeneral(this, 1000);
case Canceled:
{
// do not schedule duel end if it was interrupted
setFinished(true);
_duel.endDuel(DuelResult.Canceled);
break;
}
case Continue:
{
ThreadPoolManager.getInstance().scheduleGeneral(this, 1000);
break;
}
default:
{
setFinished(true);
playKneelAnimation();
ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleEndDuelTask(_duel, _duel.checkEndDuelCondition()), 5000);
InstanceManager.getInstance().destroyInstance(_duel.getDueldInstanceId());
break;
}
}
}
catch (Exception e)
{
_log.log(Level.SEVERE, "", e);
_log.log(Level.SEVERE, "There has been a problem while runing a duel task!", e);
}
}
}
@ -238,13 +248,13 @@ public class Duel
if (count == 4)
{
// players need to be teleportet first
// TODO: stadia manager needs a function to return an unused stadium for duels
// currently only teleports to the same stadium
_duel.teleportPlayers(-83760, -238825, -3331);
// Save player conditions before teleporting players
_duel.savePlayerConditions();
// give players 20 seconds to complete teleport and get ready (its ought to be 30 on offical..)
ThreadPoolManager.getInstance().scheduleGeneral(this, 20000);
_duel.teleportPlayers();
// give players 20 seconds to complete teleport and get ready (its ought to be 30 on official..)
ThreadPoolManager.getInstance().scheduleGeneral(this, _duel.isPartyDuel() ? 20000 : 1);
}
else if (count > 0) // duel not started yet - continue countdown
{
@ -287,6 +297,11 @@ public class Duel
}
}
public int getDueldInstanceId()
{
return _duelInstanceId;
}
/**
* Stops all players from attacking. Used for duel timeout / interrupt.
*/
@ -301,6 +316,19 @@ public class Duel
temp.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
temp.setTarget(null);
temp.sendPacket(af);
if (temp.hasSummon())
{
for (L2Summon summon : temp.getServitors().values())
{
if (!summon.isDead())
{
summon.abortCast();
summon.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
summon.setTarget(null);
summon.sendPacket(af);
}
}
}
}
for (L2PcInstance temp : _playerB.getParty().getMembers())
{
@ -308,6 +336,19 @@ public class Duel
temp.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
temp.setTarget(null);
temp.sendPacket(af);
if (temp.hasSummon())
{
for (L2Summon summon : temp.getServitors().values())
{
if (!summon.isDead())
{
summon.abortCast();
summon.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
summon.setTarget(null);
summon.sendPacket(af);
}
}
}
}
}
else
@ -320,6 +361,32 @@ public class Duel
_playerB.setTarget(null);
_playerA.sendPacket(af);
_playerB.sendPacket(af);
if (_playerA.hasSummon())
{
for (L2Summon summon : _playerA.getServitors().values())
{
if (!summon.isDead())
{
summon.abortCast();
summon.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
summon.setTarget(null);
summon.sendPacket(af);
}
}
}
if (_playerB.hasSummon())
{
for (L2Summon summon : _playerB.getServitors().values())
{
if (!summon.isDead())
{
summon.abortCast();
summon.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
summon.setTarget(null);
summon.sendPacket(af);
}
}
}
}
}
@ -353,9 +420,6 @@ public class Duel
*/
public void startDuel()
{
// Save player Conditions
savePlayerConditions();
if ((_playerA == null) || (_playerB == null) || _playerA.isInDuel() || _playerB.isInDuel())
{
_playerConditions.clear();
@ -365,8 +429,7 @@ public class Duel
if (_partyDuel)
{
// set isInDuel() state
// cancel all active trades, just in case? xD
// Set duel state and team
for (L2PcInstance temp : _playerA.getParty().getMembers())
{
temp.cancelActiveTrade();
@ -384,31 +447,35 @@ public class Duel
broadcastToTeam1(new ExDuelUpdateUserInfo(temp));
}
// Send duel Start packets
ExDuelReady ready = new ExDuelReady(1);
ExDuelStart start = new ExDuelStart(1);
// Send duel packets
broadcastToTeam1(ExDuelReady.PARTY_DUEL);
broadcastToTeam2(ExDuelReady.PARTY_DUEL);
broadcastToTeam1(ExDuelStart.PARTY_DUEL);
broadcastToTeam2(ExDuelStart.PARTY_DUEL);
broadcastToTeam1(ready);
broadcastToTeam2(ready);
broadcastToTeam1(start);
broadcastToTeam2(start);
// Open arena doors
for (L2DoorInstance door : InstanceManager.getInstance().getInstance(getDueldInstanceId()).getDoors())
{
if ((door != null) && !door.getOpen())
{
door.openMe();
}
}
}
else
{
// set isInDuel() state
// Set duel state and team
_playerA.setIsInDuel(_duelId);
_playerA.setTeam(Team.BLUE);
_playerB.setIsInDuel(_duelId);
_playerB.setTeam(Team.RED);
// Send duel Start packets
ExDuelReady ready = new ExDuelReady(0);
ExDuelStart start = new ExDuelStart(0);
broadcastToTeam1(ready);
broadcastToTeam2(ready);
broadcastToTeam1(start);
broadcastToTeam2(start);
// Send duel packets
broadcastToTeam1(ExDuelReady.PLAYER_DUEL);
broadcastToTeam2(ExDuelReady.PLAYER_DUEL);
broadcastToTeam1(ExDuelStart.PLAYER_DUEL);
broadcastToTeam2(ExDuelStart.PLAYER_DUEL);
broadcastToTeam1(new ExDuelUpdateUserInfo(_playerB));
broadcastToTeam2(new ExDuelUpdateUserInfo(_playerA));
@ -421,7 +488,7 @@ public class Duel
broadcastToTeam1(B04_S01);
broadcastToTeam2(B04_S01);
// start duelling task
// start dueling task
ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleDuelTask(this), 1000);
}
@ -434,17 +501,17 @@ public class Duel
{
for (L2PcInstance player : _playerA.getParty().getMembers())
{
_playerConditions.add(new PlayerCondition(player, _partyDuel));
_playerConditions.put(player.getObjectId(), new PlayerCondition(player, _partyDuel));
}
for (L2PcInstance player : _playerB.getParty().getMembers())
{
_playerConditions.add(new PlayerCondition(player, _partyDuel));
_playerConditions.put(player.getObjectId(), new PlayerCondition(player, _partyDuel));
}
}
else
{
_playerConditions.add(new PlayerCondition(_playerA, _partyDuel));
_playerConditions.add(new PlayerCondition(_playerB, _partyDuel));
_playerConditions.put(_playerA.getObjectId(), new PlayerCondition(_playerA, _partyDuel));
_playerConditions.put(_playerB.getObjectId(), new PlayerCondition(_playerB, _partyDuel));
}
}
@ -487,7 +554,7 @@ public class Duel
}
// restore player conditions
_playerConditions.forEach(c -> c.restoreCondition());
_playerConditions.values().forEach(c -> c.restoreCondition());
}
/**
@ -546,30 +613,46 @@ public class Duel
}
/**
* teleport all players to the given coordinates
* @param x
* @param y
* @param z
* Teleports all players to a free arena.
*/
public void teleportPlayers(int x, int y, int z)
public void teleportPlayers()
{
// TODO: adjust the values if needed... or implement something better (especially using more then 1 arena)
if (!_partyDuel)
{
return;
}
int offset = 0;
final String instanceName = DuelManager.getInstance().getDuelArena();
final L2OlympiadStadiumZone zone = ZoneManager.getInstance().getAllZones(L2OlympiadStadiumZone.class) //
.stream().filter(z -> z.getInstanceTemplate().equals(instanceName)).findFirst().orElse(null);
if (zone == null)
{
throw new RuntimeException("Unable to find a party duel arena!");
}
final List<Location> spawns = zone.getSpawns();
_duelInstanceId = InstanceManager.getInstance().createDynamicInstance(instanceName);
// Remove Olympiad buffers
for (L2Npc buffer : InstanceManager.getInstance().getInstance(getDueldInstanceId()).getNpcs())
{
if ((buffer instanceof L2OlympiadManagerInstance) && buffer.isVisible())
{
buffer.decayMe();
}
}
final Location spawn1 = spawns.get(Rnd.get(spawns.size() / 2));
for (L2PcInstance temp : _playerA.getParty().getMembers())
{
temp.teleToLocation(new Location((x + offset) - 180, y - 150, z));
offset += 40;
temp.teleToLocation(spawn1.getX(), spawn1.getY(), spawn1.getZ(), 0, _duelInstanceId, 0);
}
offset = 0;
final Location spawn2 = spawns.get(Rnd.get(spawns.size() / 2, spawns.size()));
for (L2PcInstance temp : _playerB.getParty().getMembers())
{
temp.teleToLocation(new Location((x + offset) - 180, y + 150, z));
offset += 40;
temp.teleToLocation(spawn2.getX(), spawn2.getY(), spawn2.getZ(), 0, _duelInstanceId, 0);
}
}
@ -664,7 +747,7 @@ public class Duel
}
/**
* Playback the bow animation for all loosers
* Playback the bow animation for all looser
*/
public void playKneelAnimation()
{
@ -794,17 +877,7 @@ public class Duel
break;
}
// Send end duel packet
ExDuelEnd duelEnd = null;
if (_partyDuel)
{
duelEnd = new ExDuelEnd(1);
}
else
{
duelEnd = new ExDuelEnd(0);
}
final ExDuelEnd duelEnd = _partyDuel ? ExDuelEnd.PARTY_DUEL : ExDuelEnd.PLAYER_DUEL;
broadcastToTeam1(duelEnd);
broadcastToTeam2(duelEnd);
@ -1014,7 +1087,7 @@ public class Duel
// if he's either playerA or playerB cancel the duel and port the players back
if ((player == _playerA) || (player == _playerB))
{
for (PlayerCondition cond : _playerConditions)
for (PlayerCondition cond : _playerConditions.values())
{
cond.teleportBack();
cond.getPlayer().setIsInDuel(0);
@ -1026,7 +1099,7 @@ public class Duel
else
// teleport the player back & delete his PlayerCondition record
{
final PlayerCondition cond = _playerConditions.stream().filter(c -> c.getPlayer() == player).findFirst().orElse(null);
final PlayerCondition cond = _playerConditions.get(player.getObjectId());
if (cond != null)
{
cond.teleportBack();
@ -1038,7 +1111,7 @@ public class Duel
public void onBuff(L2PcInstance player, Skill debuff)
{
final PlayerCondition cond = _playerConditions.stream().filter(c -> c.getPlayer() == player).findFirst().orElse(null);
final PlayerCondition cond = _playerConditions.get(player.getObjectId());
if (cond != null)
{
cond.registerDebuff(debuff);

View File

@ -19,15 +19,19 @@
package com.l2jserver.gameserver.network.serverpackets;
/**
* @author KenM
* Duel End packet implementation.
* @author KenM, Zoey76
*/
public class ExDuelEnd extends L2GameServerPacket
{
private final int _unk1;
public static final ExDuelEnd PLAYER_DUEL = new ExDuelEnd(false);
public static final ExDuelEnd PARTY_DUEL = new ExDuelEnd(true);
public ExDuelEnd(int unk1)
private final int _partyDuel;
private ExDuelEnd(boolean isPartyDuel)
{
_unk1 = unk1;
_partyDuel = isPartyDuel ? 1 : 0;
}
@Override
@ -36,6 +40,6 @@ public class ExDuelEnd extends L2GameServerPacket
writeC(0xFE);
writeH(0x50);
writeD(_unk1);
writeD(_partyDuel);
}
}

View File

@ -19,15 +19,19 @@
package com.l2jserver.gameserver.network.serverpackets;
/**
* @author KenM
* Duel Ready packet implementation.
* @author KenM, Zoey76
*/
public class ExDuelReady extends L2GameServerPacket
{
private final int _unk1;
public static final ExDuelReady PLAYER_DUEL = new ExDuelReady(false);
public static final ExDuelReady PARTY_DUEL = new ExDuelReady(true);
public ExDuelReady(int unk1)
private final boolean _partyDuel;
public ExDuelReady(boolean partyDuel)
{
_unk1 = unk1;
_partyDuel = partyDuel;
}
@Override
@ -36,6 +40,6 @@ public class ExDuelReady extends L2GameServerPacket
writeC(0xFE);
writeH(0x4E);
writeD(_unk1);
writeD(_partyDuel ? 1 : 0);
}
}

View File

@ -19,15 +19,19 @@
package com.l2jserver.gameserver.network.serverpackets;
/**
* @author KenM
* Duel Start packet implementation.
* @author KenM, Zoey76
*/
public class ExDuelStart extends L2GameServerPacket
{
private final int _unk1;
public static final ExDuelReady PLAYER_DUEL = new ExDuelReady(false);
public static final ExDuelReady PARTY_DUEL = new ExDuelReady(true);
public ExDuelStart(int unk1)
private final boolean _partyDuel;
public ExDuelStart(boolean partyDuel)
{
_unk1 = unk1;
_partyDuel = partyDuel;
}
@Override
@ -36,6 +40,6 @@ public class ExDuelStart extends L2GameServerPacket
writeC(0xFE);
writeH(0x4F);
writeD(_unk1);
writeD(_partyDuel ? 1 : 0);
}
}

View File

@ -24,7 +24,6 @@ import java.util.List;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.interfaces.IPositionable;
/**
@ -50,16 +49,7 @@ public final class MagicSkillUse extends L2GameServerPacket
_skillLevel = skillLevel;
_hitTime = hitTime;
_reuseDelay = reuseDelay;
Location skillWorldPos = null;
if (cha.isPlayer())
{
final L2PcInstance player = cha.getActingPlayer();
if (player.getCurrentSkillWorldPosition() != null)
{
skillWorldPos = player.getCurrentSkillWorldPosition();
}
}
_groundLocations = skillWorldPos != null ? Arrays.asList(skillWorldPos) : Collections.<Location> emptyList();
_groundLocations = cha.isPlayer() && (cha.getActingPlayer().getCurrentSkillWorldPosition() != null) ? Arrays.asList(cha.getActingPlayer().getCurrentSkillWorldPosition()) : Collections.<Location> emptyList();
}
public MagicSkillUse(L2Character cha, int skillId, int skillLevel, int hitTime, int reuseDelay)