Updated Eigis AI.
Contributed by notorionn.
This commit is contained in:
parent
12cde721e5
commit
90b927549a
@ -1,74 +1,117 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* Copyright (c) 2013 L2jMobius
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package ai.bosses.Eigis;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.l2jmobius.commons.threads.ThreadPool;
|
||||
import org.l2jmobius.commons.util.Rnd;
|
||||
import org.l2jmobius.gameserver.data.SpawnTable;
|
||||
import org.l2jmobius.gameserver.data.xml.NpcData;
|
||||
import org.l2jmobius.gameserver.data.xml.SkillData;
|
||||
import org.l2jmobius.gameserver.enums.SkillFinishType;
|
||||
import org.l2jmobius.gameserver.instancemanager.DBSpawnManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
|
||||
import org.l2jmobius.gameserver.model.Location;
|
||||
import org.l2jmobius.gameserver.model.Spawn;
|
||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||
import org.l2jmobius.gameserver.model.actor.Npc;
|
||||
import org.l2jmobius.gameserver.model.actor.Player;
|
||||
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
||||
import org.l2jmobius.gameserver.model.holders.SkillHolder;
|
||||
import org.l2jmobius.gameserver.model.skill.Skill;
|
||||
import org.l2jmobius.gameserver.model.zone.ZoneType;
|
||||
import org.l2jmobius.gameserver.model.zone.type.ArenaZone;
|
||||
import org.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;
|
||||
|
||||
import ai.AbstractNpcAI;
|
||||
|
||||
/**
|
||||
* @author NasSeKa
|
||||
* @author Notorion
|
||||
*/
|
||||
public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
// NPC
|
||||
// NPCs
|
||||
private static final int EIGIS = 29385;
|
||||
// Misc
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504);
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504, 0);
|
||||
private static final String EIGIS_ALIVE_VAR = "EIGIS_ALIVE";
|
||||
private static final int INVISIBLE_NPC = 18919;
|
||||
private static final int INVISIBLE_NPC_2 = 18920;
|
||||
// Skills
|
||||
private static final SkillHolder COMMON_SKILL = new SkillHolder(34108, 1); // Blade of Souls
|
||||
private static final SkillHolder SPECIAL_AOE_1 = new SkillHolder(34113, 1); // Dark Wave Spray
|
||||
private static final SkillHolder SPECIAL_AOE_VISUAL_2 = new SkillHolder(34114, 1); // Gate of Thousand Flashes
|
||||
private static final SkillHolder JUMP_TARGET_VISUAL_1 = new SkillHolder(34116, 1); // Dark Shadow Clash
|
||||
private static final SkillHolder JUMP_SKILL_DAMAGE_2 = new SkillHolder(34109, 1); // Blade Temptation
|
||||
private static final SkillHolder JUMP_IMPACT_VISUAL_3 = new SkillHolder(34110, 1); // Price for Temptation
|
||||
private static final SkillHolder NPC_AOE_SKILL = new SkillHolder(34115, 1); // Tear to Shreds
|
||||
private static final int NPC_LIFETIME = 6000;
|
||||
// Zone
|
||||
private static final ArenaZone EIGIS_ZONE = ZoneManager.getInstance().getZoneByName("Eigis_Seat_Zone", ArenaZone.class);
|
||||
// Barrier
|
||||
private static final int BARRIER_DURATION_MILLIS = 600000; // 10 minutes
|
||||
private static final int HIT_COUNT = 2000; // 2000 Number of attacks needed to destroy the barrier
|
||||
private static final int HIT_COUNT_RENEW = 500; // 500 hits in 60 seconds to continue without the barrier
|
||||
private static final int RENEW_DURATION_MILLIS = 600000; // 60 seconds of vulnerability
|
||||
private static final SkillHolder LIMIT_BARRIER = new SkillHolder(29515, 1);
|
||||
private boolean _barrierActivated = false;
|
||||
// Misc
|
||||
private boolean _specialActivated = false;
|
||||
private final AtomicBoolean _isUsingSpecialSkill = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill2 = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill3 = new AtomicBoolean(false);
|
||||
private boolean _bossInCombat = false;
|
||||
private final AtomicInteger _targetLossCount = new AtomicInteger(0);
|
||||
private boolean _vulnerablePhase = false;
|
||||
private final Map<Npc, Integer> _eigisHits = new ConcurrentHashMap<>();
|
||||
|
||||
public Eigis()
|
||||
{
|
||||
addAttackId(EIGIS);
|
||||
addSpawnId(EIGIS);
|
||||
addKillId(EIGIS);
|
||||
addAggroRangeEnterId(EIGIS);
|
||||
addExitZoneId(EIGIS_ZONE.getId());
|
||||
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final Calendar calendarEigisStart = Calendar.getInstance();
|
||||
final Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
Calendar calendarEigisStart = Calendar.getInstance();
|
||||
Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, 1); // Sunday
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
calendarEigisStart.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendarEigisStart.set(Calendar.MINUTE, 0);
|
||||
calendarEigisStart.set(Calendar.SECOND, 0);
|
||||
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, 2); // Monday
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
calendarEigisSeal.set(Calendar.HOUR_OF_DAY, 2);
|
||||
calendarEigisSeal.set(Calendar.MINUTE, 0);
|
||||
calendarEigisSeal.set(Calendar.SECOND, 0);
|
||||
|
||||
if (((currentTime > calendarEigisStart.getTimeInMillis()) && (currentTime < calendarEigisSeal.getTimeInMillis())) && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null) && GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, true))
|
||||
{
|
||||
spawnEigis();
|
||||
}
|
||||
else
|
||||
{
|
||||
despawnEigis();
|
||||
}
|
||||
|
||||
if (calendarEigisStart.getTimeInMillis() < currentTime)
|
||||
{
|
||||
calendarEigisStart.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
@ -78,32 +121,36 @@ public class Eigis extends AbstractNpcAI
|
||||
calendarEigisSeal.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
boolean bossAlive = GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false);
|
||||
if (bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
spawnEigis();
|
||||
}, calendarEigisStart.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
}
|
||||
else if (!bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) != null))
|
||||
{
|
||||
despawnEigis();
|
||||
}, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::spawnEigis, calendarEigisStart.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::despawnEigis, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
}
|
||||
|
||||
private void spawnEigis()
|
||||
{
|
||||
try
|
||||
if (!GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false) || (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
final NpcTemplate template = NpcData.getInstance().getTemplate(EIGIS);
|
||||
final Spawn spawn = new Spawn(template);
|
||||
spawn.setXYZ(EIGIS_LOCATION);
|
||||
spawn.setHeading(0);
|
||||
final Npc npc = addSpawn(EIGIS, EIGIS_LOCATION);
|
||||
npc.setRandomWalking(false);
|
||||
npc.setRandomAnimation(false);
|
||||
|
||||
final Spawn spawn = npc.getSpawn();
|
||||
spawn.setRespawnDelay(0);
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, false);
|
||||
spawn.startRespawn();
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, true);
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// LOGGER.info("Eigis spawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,17 +162,622 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
npc.deleteMe();
|
||||
}
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
// LOGGER.info("Eigis has been despawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onSpawn(Npc npc)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkCombatStatus", 1000, npc, null, true);
|
||||
startQuestTimer("checkPosition", 5000, npc, null, true);
|
||||
return super.onSpawn(npc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAggroRangeEnter(Npc npc, Player player, boolean isSummon)
|
||||
{
|
||||
_bossInCombat = true;
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
checkCombatStatus(npc);
|
||||
if (_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
return super.onAggroRangeEnter(npc, player, isSummon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onExitZone(Creature creature, ZoneType zone)
|
||||
{
|
||||
if ((creature instanceof Npc) && (creature.getId() == EIGIS))
|
||||
{
|
||||
final Npc npc = creature.asNpc();
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
cancelSpecialSkills(npc);
|
||||
}
|
||||
return super.onExitZone(creature, zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAttack(Npc npc, Player attacker, int damage, boolean isSummon, Skill skill)
|
||||
{
|
||||
if (!_barrierActivated)
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
|
||||
if (_vulnerablePhase)
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT_RENEW)
|
||||
{
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT)
|
||||
{
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
_vulnerablePhase = true;
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_bossInCombat = true;
|
||||
if (!_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
_specialActivated = true;
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
|
||||
return super.onAttack(npc, attacker, damage, isSummon, skill);
|
||||
}
|
||||
|
||||
private void activateSpecialMechanics(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
startQuestTimer("SkillsBalancer", 2000, npc, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onEvent(String event, Npc npc, Player player)
|
||||
{
|
||||
if ((npc != null) && (npc.getId() == EIGIS))
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case "activate_barrier":
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
_vulnerablePhase = false;
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "remove_barrier":
|
||||
{
|
||||
_barrierActivated = false;
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "EigisSkills1":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills2":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills3":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get())
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "SkillsBalancer":
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
if (!npc.isDead() && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
boolean hasValidTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target instanceof Player) && !target.isDead() && npc.isInsideRadius3D(target, 2000))
|
||||
{
|
||||
hasValidTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidTarget)
|
||||
{
|
||||
final int chance = getRandom(100);
|
||||
if (chance < 33)
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
else if (chance < 66)
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
startQuestTimer("SkillsBalancer", getRandom(10000, 35000), npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
resetSkillFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkTargetRange":
|
||||
{
|
||||
if ((player != null) && !player.isDead())
|
||||
{
|
||||
final double distance = player.calculateDistance3D(npc);
|
||||
if ((distance < 700) || (distance > 2000))
|
||||
{
|
||||
_targetLossCount.incrementAndGet();
|
||||
if (_targetLossCount.get() >= 2)
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomicReference<Player> newTarget = new AtomicReference<>();
|
||||
if (findTargetPlayer(npc, newTarget))
|
||||
{
|
||||
player = newTarget.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkCombatStatus":
|
||||
{
|
||||
checkCombatStatus(npc);
|
||||
break;
|
||||
}
|
||||
case "checkTargetLost":
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
break;
|
||||
}
|
||||
case "checkPosition":
|
||||
{
|
||||
if (!EIGIS_ZONE.isInsideZone(npc))
|
||||
{
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onEvent(event, npc, player);
|
||||
}
|
||||
|
||||
private void useEigisSkills1(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
final boolean useComeToMe = Rnd.get(100) < 48;
|
||||
if (useComeToMe)
|
||||
{
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.doCast(SPECIAL_AOE_1.getSkill());
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat && _isUsingSpecialSkill.get())
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, npc, SPECIAL_AOE_VISUAL_2.getSkillId(), 1, 0, 0));
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Presentation skill Lv.1 Teleport (34388)
|
||||
private void useComeToMeSkill(Npc npc)
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34388, 1));
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private void cancelDebuffs(Npc npc)
|
||||
{
|
||||
if ((npc == null) || npc.isDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.getEffectList().getEffects().forEach(effect ->
|
||||
{
|
||||
if (effect.getSkill().isDebuff())
|
||||
{
|
||||
npc.getEffectList().stopSkillEffects(SkillFinishType.REMOVED, effect.getSkill());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void useEigisSkills2(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill2.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 7000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (Rnd.get(100) < 45)
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Presentation Skill Lv.1 Tear to Shreds (34392)
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34392, 1));
|
||||
}, 3000);
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EIGIS_ZONE == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final Location bossLocation = npc.getLocation();
|
||||
final List<Npc> spawnedNpcs = new ArrayList<>();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
int x = bossLocation.getX() + (int) ((i - 2.5) * 500);
|
||||
int y = bossLocation.getY() + (int) ((j - 2.5) * 500);
|
||||
Location spawnLoc = new Location(x, y, bossLocation.getZ());
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final Npc newNpc = addSpawn(INVISIBLE_NPC, spawnLoc, false, NPC_LIFETIME);
|
||||
newNpc.setHeading(0);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final List<Npc> spawnedNpcsCopy = new ArrayList<>(spawnedNpcs);
|
||||
for (Npc npcYellow : spawnedNpcsCopy)
|
||||
{
|
||||
final Location loc = npcYellow.getLocation();
|
||||
final int npcHeading = getRandom(0, 65535);
|
||||
final Location spawnLoc = calculatePointFromLocation(loc.getX(), loc.getY(), loc.getZ(), 100, npcHeading);
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Npc newNpc = addSpawn(INVISIBLE_NPC_2, spawnLoc, false, NPC_LIFETIME);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.setHeading(npcHeading);
|
||||
newNpc.teleToLocation(newNpc.getX(), newNpc.getY(), newNpc.getZ(), npcHeading);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private Location calculatePointFromLocation(int x, int y, int z, int distance, int heading)
|
||||
{
|
||||
final double angle = Math.toRadians(heading * 0.0054931640625);
|
||||
final int newX = x + (int) (distance * Math.cos(angle));
|
||||
final int newY = y + (int) (distance * Math.sin(angle));
|
||||
return new Location(newX, newY, z);
|
||||
}
|
||||
|
||||
private void useEigisSkills3(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill3.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
final AtomicReference<Player> targetPlayer = new AtomicReference<>();
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!findTargetPlayer(npc, targetPlayer))
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
return;
|
||||
}
|
||||
|
||||
final Player target = targetPlayer.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
npc.setTarget(target);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_TARGET_VISUAL_1.getSkillId(), 1, 3000, 0));
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.doCast(JUMP_SKILL_DAMAGE_2.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_IMPACT_VISUAL_3.getSkillId(), 1, 2000, 0));
|
||||
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}, 6000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private boolean findTargetPlayer(Npc npc, AtomicReference<Player> targetPlayer)
|
||||
{
|
||||
final List<Player> playersInRange = new ArrayList<>();
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && (target instanceof Player))
|
||||
{
|
||||
final Player player = (Player) target;
|
||||
final double distance = npc.calculateDistance3D(player);
|
||||
if ((distance >= 700) && (distance <= 2000))
|
||||
{
|
||||
playersInRange.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!playersInRange.isEmpty())
|
||||
{
|
||||
targetPlayer.set(playersInRange.get(Rnd.get(playersInRange.size())));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resetSkillFlags()
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
|
||||
private void cancelSkill3(Npc npc)
|
||||
{
|
||||
cancelQuestTimer("checkTargetRange", npc, null);
|
||||
resetSkillFlags();
|
||||
}
|
||||
|
||||
private void cancelSpecialSkills(Npc npc)
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
|
||||
private void checkCombatStatus(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
boolean hasTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
hasTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTarget)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkTargetLost", 8000, npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_specialActivated)
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
_specialActivated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onKill(Npc npc, Player killer, boolean isSummon)
|
||||
{
|
||||
if (npc.getId() == EIGIS)
|
||||
{
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
}
|
||||
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
npc.setInvul(false);
|
||||
_eigisHits.clear();
|
||||
_barrierActivated = false;
|
||||
_vulnerablePhase = false;
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
cancelQuestTimer("checkCombatStatus", npc, null);
|
||||
cancelQuestTimer("checkPosition", npc, null);
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
return super.onKill(npc, killer, isSummon);
|
||||
}
|
||||
|
||||
@ -133,4 +785,4 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
new Eigis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3574,7 +3574,7 @@
|
||||
<walk ground="60" />
|
||||
<run ground="180" />
|
||||
</speed>
|
||||
<hitTime>600</hitTime>
|
||||
<hitTime>950</hitTime>
|
||||
<attribute>
|
||||
<defence fire="2350" water="2350" wind="2350" earth="2350" holy="2350" dark="2300" />
|
||||
<attack type="HOLY" value="2200" />
|
||||
@ -5546,7 +5546,6 @@
|
||||
</dropLists>
|
||||
</npc>
|
||||
<npc id="29385" level="127" type="RaidBoss" name="Eigis" title="Lady of Despair">
|
||||
<!-- AUTO GENERATED NPC TODO: FIX IT -->
|
||||
<race>UNDEAD</race>
|
||||
<sex>MALE</sex>
|
||||
<acquire exp="4003411775" sp="3603071" />
|
||||
@ -5561,6 +5560,7 @@
|
||||
<hitTime>600</hitTime>
|
||||
</stats>
|
||||
<status attackable="true" />
|
||||
<ai type="MAGE" clanHelpRange="2000" aggroRange="2000" />
|
||||
<collision>
|
||||
<radius normal="36" />
|
||||
<height normal="123" />
|
||||
@ -5572,6 +5572,10 @@
|
||||
<skill id="32909" level="1" /> <!-- Rule of Looting -->
|
||||
<skill id="32675" level="1" /> <!-- Improved Immunity -->
|
||||
<skill id="14804" level="10" /> <!-- Damage Reflection Resistance -->
|
||||
<skill id="34814" level="9" /> <!-- Critical Rate Resistance -->
|
||||
<skill id="34815" level="9" /> <!-- Critical Damage Resistance -->
|
||||
<skill id="34820" level="7" /> <!-- Received Damage Resistance -->
|
||||
<skill id="34108" level="1" /> <!-- Blade of Souls -->
|
||||
</skillList>
|
||||
<dropLists>
|
||||
<drop>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -692,6 +692,34 @@
|
||||
<!-- Level 2: Presentation skill for finishing teleportation. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>2</abnormalTime>
|
||||
<abnormalType>STUN</abnormalType>
|
||||
<abnormalVisualEffect>STUN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>2000</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-5000</effectPoint>
|
||||
<effectRange>400</effectRange>
|
||||
<hitTime>300</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>120</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<trait>PULL</trait>
|
||||
<effects>
|
||||
<effect name="PullBack">
|
||||
<speed>600</speed>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34389" toLevel="1" name="World End">
|
||||
<!-- Mental Attack deals great and even fatal damage to nearby enemies and forces them to dance. -->
|
||||
@ -712,6 +740,20 @@
|
||||
<!-- Presentation Skill. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34393" toLevel="1" name="Tear to Shreds">
|
||||
<!-- Dark energy strikes the target knocking them down. -->
|
||||
|
@ -119,4 +119,24 @@
|
||||
<node X="-4026" Y="185137" />
|
||||
<node X="-7370" Y="186216" />
|
||||
</zone>
|
||||
<zone name="Eigis_Seat_Zone" type="ArenaZone" shape="NPoly" minZ="-4000" maxZ="4900">
|
||||
<node X="-20955" Y="-222973" />
|
||||
<node X="-23272" Y="-223768" />
|
||||
<node X="-24520" Y="-224200" />
|
||||
<node X="-24968" Y="-223352" />
|
||||
<node X="-25512" Y="-222264" />
|
||||
<node X="-25976" Y="-221288" />
|
||||
<node X="-26035" Y="-218613" />
|
||||
<node X="-26020" Y="-218313" />
|
||||
<node X="-25646" Y="-217960" />
|
||||
<node X="-23748" Y="-219030" />
|
||||
<node X="-22744" Y="-219512" />
|
||||
<node X="-22616" Y="-219704" />
|
||||
<node X="-21659" Y="-220231" />
|
||||
<node X="-21414" Y="-220124" />
|
||||
<node X="-21336" Y="-220360" />
|
||||
<node X="-21624" Y="-220984" />
|
||||
<node X="-21208" Y="-221752" />
|
||||
<node X="-20872" Y="-222712" />
|
||||
</zone>
|
||||
</list>
|
@ -1,74 +1,117 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* Copyright (c) 2013 L2jMobius
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package ai.bosses.Eigis;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.l2jmobius.commons.threads.ThreadPool;
|
||||
import org.l2jmobius.commons.util.Rnd;
|
||||
import org.l2jmobius.gameserver.data.SpawnTable;
|
||||
import org.l2jmobius.gameserver.data.xml.NpcData;
|
||||
import org.l2jmobius.gameserver.data.xml.SkillData;
|
||||
import org.l2jmobius.gameserver.enums.SkillFinishType;
|
||||
import org.l2jmobius.gameserver.instancemanager.DBSpawnManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
|
||||
import org.l2jmobius.gameserver.model.Location;
|
||||
import org.l2jmobius.gameserver.model.Spawn;
|
||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||
import org.l2jmobius.gameserver.model.actor.Npc;
|
||||
import org.l2jmobius.gameserver.model.actor.Player;
|
||||
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
||||
import org.l2jmobius.gameserver.model.holders.SkillHolder;
|
||||
import org.l2jmobius.gameserver.model.skill.Skill;
|
||||
import org.l2jmobius.gameserver.model.zone.ZoneType;
|
||||
import org.l2jmobius.gameserver.model.zone.type.ArenaZone;
|
||||
import org.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;
|
||||
|
||||
import ai.AbstractNpcAI;
|
||||
|
||||
/**
|
||||
* @author NasSeKa
|
||||
* @author Notorion
|
||||
*/
|
||||
public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
// NPC
|
||||
// NPCs
|
||||
private static final int EIGIS = 29385;
|
||||
// Misc
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504);
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504, 0);
|
||||
private static final String EIGIS_ALIVE_VAR = "EIGIS_ALIVE";
|
||||
private static final int INVISIBLE_NPC = 18919;
|
||||
private static final int INVISIBLE_NPC_2 = 18920;
|
||||
// Skills
|
||||
private static final SkillHolder COMMON_SKILL = new SkillHolder(34108, 1); // Blade of Souls
|
||||
private static final SkillHolder SPECIAL_AOE_1 = new SkillHolder(34113, 1); // Dark Wave Spray
|
||||
private static final SkillHolder SPECIAL_AOE_VISUAL_2 = new SkillHolder(34114, 1); // Gate of Thousand Flashes
|
||||
private static final SkillHolder JUMP_TARGET_VISUAL_1 = new SkillHolder(34116, 1); // Dark Shadow Clash
|
||||
private static final SkillHolder JUMP_SKILL_DAMAGE_2 = new SkillHolder(34109, 1); // Blade Temptation
|
||||
private static final SkillHolder JUMP_IMPACT_VISUAL_3 = new SkillHolder(34110, 1); // Price for Temptation
|
||||
private static final SkillHolder NPC_AOE_SKILL = new SkillHolder(34115, 1); // Tear to Shreds
|
||||
private static final int NPC_LIFETIME = 6000;
|
||||
// Zone
|
||||
private static final ArenaZone EIGIS_ZONE = ZoneManager.getInstance().getZoneByName("Eigis_Seat_Zone", ArenaZone.class);
|
||||
// Barrier
|
||||
private static final int BARRIER_DURATION_MILLIS = 600000; // 10 minutes
|
||||
private static final int HIT_COUNT = 2000; // 2000 Number of attacks needed to destroy the barrier
|
||||
private static final int HIT_COUNT_RENEW = 500; // 500 hits in 60 seconds to continue without the barrier
|
||||
private static final int RENEW_DURATION_MILLIS = 600000; // 60 seconds of vulnerability
|
||||
private static final SkillHolder LIMIT_BARRIER = new SkillHolder(29515, 1);
|
||||
private boolean _barrierActivated = false;
|
||||
// Misc
|
||||
private boolean _specialActivated = false;
|
||||
private final AtomicBoolean _isUsingSpecialSkill = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill2 = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill3 = new AtomicBoolean(false);
|
||||
private boolean _bossInCombat = false;
|
||||
private final AtomicInteger _targetLossCount = new AtomicInteger(0);
|
||||
private boolean _vulnerablePhase = false;
|
||||
private final Map<Npc, Integer> _eigisHits = new ConcurrentHashMap<>();
|
||||
|
||||
public Eigis()
|
||||
{
|
||||
addAttackId(EIGIS);
|
||||
addSpawnId(EIGIS);
|
||||
addKillId(EIGIS);
|
||||
addAggroRangeEnterId(EIGIS);
|
||||
addExitZoneId(EIGIS_ZONE.getId());
|
||||
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final Calendar calendarEigisStart = Calendar.getInstance();
|
||||
final Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
Calendar calendarEigisStart = Calendar.getInstance();
|
||||
Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, 1); // Sunday
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
calendarEigisStart.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendarEigisStart.set(Calendar.MINUTE, 0);
|
||||
calendarEigisStart.set(Calendar.SECOND, 0);
|
||||
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, 2); // Monday
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
calendarEigisSeal.set(Calendar.HOUR_OF_DAY, 2);
|
||||
calendarEigisSeal.set(Calendar.MINUTE, 0);
|
||||
calendarEigisSeal.set(Calendar.SECOND, 0);
|
||||
|
||||
if (((currentTime > calendarEigisStart.getTimeInMillis()) && (currentTime < calendarEigisSeal.getTimeInMillis())) && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null) && GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, true))
|
||||
{
|
||||
spawnEigis();
|
||||
}
|
||||
else
|
||||
{
|
||||
despawnEigis();
|
||||
}
|
||||
|
||||
if (calendarEigisStart.getTimeInMillis() < currentTime)
|
||||
{
|
||||
calendarEigisStart.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
@ -78,32 +121,36 @@ public class Eigis extends AbstractNpcAI
|
||||
calendarEigisSeal.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
boolean bossAlive = GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false);
|
||||
if (bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
spawnEigis();
|
||||
}, calendarEigisStart.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
}
|
||||
else if (!bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) != null))
|
||||
{
|
||||
despawnEigis();
|
||||
}, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::spawnEigis, calendarEigisStart.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::despawnEigis, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
}
|
||||
|
||||
private void spawnEigis()
|
||||
{
|
||||
try
|
||||
if (!GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false) || (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
final NpcTemplate template = NpcData.getInstance().getTemplate(EIGIS);
|
||||
final Spawn spawn = new Spawn(template);
|
||||
spawn.setXYZ(EIGIS_LOCATION);
|
||||
spawn.setHeading(0);
|
||||
final Npc npc = addSpawn(EIGIS, EIGIS_LOCATION);
|
||||
npc.setRandomWalking(false);
|
||||
npc.setRandomAnimation(false);
|
||||
|
||||
final Spawn spawn = npc.getSpawn();
|
||||
spawn.setRespawnDelay(0);
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, false);
|
||||
spawn.startRespawn();
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, true);
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// LOGGER.info("Eigis spawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,17 +162,622 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
npc.deleteMe();
|
||||
}
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
// LOGGER.info("Eigis has been despawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onSpawn(Npc npc)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkCombatStatus", 1000, npc, null, true);
|
||||
startQuestTimer("checkPosition", 5000, npc, null, true);
|
||||
return super.onSpawn(npc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAggroRangeEnter(Npc npc, Player player, boolean isSummon)
|
||||
{
|
||||
_bossInCombat = true;
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
checkCombatStatus(npc);
|
||||
if (_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
return super.onAggroRangeEnter(npc, player, isSummon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onExitZone(Creature creature, ZoneType zone)
|
||||
{
|
||||
if ((creature instanceof Npc) && (creature.getId() == EIGIS))
|
||||
{
|
||||
final Npc npc = creature.asNpc();
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
cancelSpecialSkills(npc);
|
||||
}
|
||||
return super.onExitZone(creature, zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAttack(Npc npc, Player attacker, int damage, boolean isSummon, Skill skill)
|
||||
{
|
||||
if (!_barrierActivated)
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
|
||||
if (_vulnerablePhase)
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT_RENEW)
|
||||
{
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT)
|
||||
{
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
_vulnerablePhase = true;
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_bossInCombat = true;
|
||||
if (!_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
_specialActivated = true;
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
|
||||
return super.onAttack(npc, attacker, damage, isSummon, skill);
|
||||
}
|
||||
|
||||
private void activateSpecialMechanics(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
startQuestTimer("SkillsBalancer", 2000, npc, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onEvent(String event, Npc npc, Player player)
|
||||
{
|
||||
if ((npc != null) && (npc.getId() == EIGIS))
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case "activate_barrier":
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
_vulnerablePhase = false;
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "remove_barrier":
|
||||
{
|
||||
_barrierActivated = false;
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "EigisSkills1":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills2":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills3":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get())
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "SkillsBalancer":
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
if (!npc.isDead() && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
boolean hasValidTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target instanceof Player) && !target.isDead() && npc.isInsideRadius3D(target, 2000))
|
||||
{
|
||||
hasValidTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidTarget)
|
||||
{
|
||||
final int chance = getRandom(100);
|
||||
if (chance < 33)
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
else if (chance < 66)
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
startQuestTimer("SkillsBalancer", getRandom(10000, 35000), npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
resetSkillFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkTargetRange":
|
||||
{
|
||||
if ((player != null) && !player.isDead())
|
||||
{
|
||||
final double distance = player.calculateDistance3D(npc);
|
||||
if ((distance < 700) || (distance > 2000))
|
||||
{
|
||||
_targetLossCount.incrementAndGet();
|
||||
if (_targetLossCount.get() >= 2)
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomicReference<Player> newTarget = new AtomicReference<>();
|
||||
if (findTargetPlayer(npc, newTarget))
|
||||
{
|
||||
player = newTarget.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkCombatStatus":
|
||||
{
|
||||
checkCombatStatus(npc);
|
||||
break;
|
||||
}
|
||||
case "checkTargetLost":
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
break;
|
||||
}
|
||||
case "checkPosition":
|
||||
{
|
||||
if (!EIGIS_ZONE.isInsideZone(npc))
|
||||
{
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onEvent(event, npc, player);
|
||||
}
|
||||
|
||||
private void useEigisSkills1(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
final boolean useComeToMe = Rnd.get(100) < 48;
|
||||
if (useComeToMe)
|
||||
{
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.doCast(SPECIAL_AOE_1.getSkill());
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat && _isUsingSpecialSkill.get())
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, npc, SPECIAL_AOE_VISUAL_2.getSkillId(), 1, 0, 0));
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Presentation skill Lv.1 Teleport (34388)
|
||||
private void useComeToMeSkill(Npc npc)
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34388, 1));
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private void cancelDebuffs(Npc npc)
|
||||
{
|
||||
if ((npc == null) || npc.isDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.getEffectList().getEffects().forEach(effect ->
|
||||
{
|
||||
if (effect.getSkill().isDebuff())
|
||||
{
|
||||
npc.getEffectList().stopSkillEffects(SkillFinishType.REMOVED, effect.getSkill());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void useEigisSkills2(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill2.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 7000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (Rnd.get(100) < 45)
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Presentation Skill Lv.1 Tear to Shreds (34392)
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34392, 1));
|
||||
}, 3000);
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EIGIS_ZONE == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final Location bossLocation = npc.getLocation();
|
||||
final List<Npc> spawnedNpcs = new ArrayList<>();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
int x = bossLocation.getX() + (int) ((i - 2.5) * 500);
|
||||
int y = bossLocation.getY() + (int) ((j - 2.5) * 500);
|
||||
Location spawnLoc = new Location(x, y, bossLocation.getZ());
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final Npc newNpc = addSpawn(INVISIBLE_NPC, spawnLoc, false, NPC_LIFETIME);
|
||||
newNpc.setHeading(0);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final List<Npc> spawnedNpcsCopy = new ArrayList<>(spawnedNpcs);
|
||||
for (Npc npcYellow : spawnedNpcsCopy)
|
||||
{
|
||||
final Location loc = npcYellow.getLocation();
|
||||
final int npcHeading = getRandom(0, 65535);
|
||||
final Location spawnLoc = calculatePointFromLocation(loc.getX(), loc.getY(), loc.getZ(), 100, npcHeading);
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Npc newNpc = addSpawn(INVISIBLE_NPC_2, spawnLoc, false, NPC_LIFETIME);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.setHeading(npcHeading);
|
||||
newNpc.teleToLocation(newNpc.getX(), newNpc.getY(), newNpc.getZ(), npcHeading);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private Location calculatePointFromLocation(int x, int y, int z, int distance, int heading)
|
||||
{
|
||||
final double angle = Math.toRadians(heading * 0.0054931640625);
|
||||
final int newX = x + (int) (distance * Math.cos(angle));
|
||||
final int newY = y + (int) (distance * Math.sin(angle));
|
||||
return new Location(newX, newY, z);
|
||||
}
|
||||
|
||||
private void useEigisSkills3(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill3.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
final AtomicReference<Player> targetPlayer = new AtomicReference<>();
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!findTargetPlayer(npc, targetPlayer))
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
return;
|
||||
}
|
||||
|
||||
final Player target = targetPlayer.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
npc.setTarget(target);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_TARGET_VISUAL_1.getSkillId(), 1, 3000, 0));
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.doCast(JUMP_SKILL_DAMAGE_2.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_IMPACT_VISUAL_3.getSkillId(), 1, 2000, 0));
|
||||
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}, 6000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private boolean findTargetPlayer(Npc npc, AtomicReference<Player> targetPlayer)
|
||||
{
|
||||
final List<Player> playersInRange = new ArrayList<>();
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && (target instanceof Player))
|
||||
{
|
||||
final Player player = (Player) target;
|
||||
final double distance = npc.calculateDistance3D(player);
|
||||
if ((distance >= 700) && (distance <= 2000))
|
||||
{
|
||||
playersInRange.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!playersInRange.isEmpty())
|
||||
{
|
||||
targetPlayer.set(playersInRange.get(Rnd.get(playersInRange.size())));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resetSkillFlags()
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
|
||||
private void cancelSkill3(Npc npc)
|
||||
{
|
||||
cancelQuestTimer("checkTargetRange", npc, null);
|
||||
resetSkillFlags();
|
||||
}
|
||||
|
||||
private void cancelSpecialSkills(Npc npc)
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
|
||||
private void checkCombatStatus(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
boolean hasTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
hasTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTarget)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkTargetLost", 8000, npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_specialActivated)
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
_specialActivated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onKill(Npc npc, Player killer, boolean isSummon)
|
||||
{
|
||||
if (npc.getId() == EIGIS)
|
||||
{
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
}
|
||||
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
npc.setInvul(false);
|
||||
_eigisHits.clear();
|
||||
_barrierActivated = false;
|
||||
_vulnerablePhase = false;
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
cancelQuestTimer("checkCombatStatus", npc, null);
|
||||
cancelQuestTimer("checkPosition", npc, null);
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
return super.onKill(npc, killer, isSummon);
|
||||
}
|
||||
|
||||
@ -133,4 +785,4 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
new Eigis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3589,7 +3589,7 @@
|
||||
<walk ground="60" />
|
||||
<run ground="180" />
|
||||
</speed>
|
||||
<hitTime>600</hitTime>
|
||||
<hitTime>950</hitTime>
|
||||
<attribute>
|
||||
<defence fire="2350" water="2350" wind="2350" earth="2350" holy="2350" dark="2300" />
|
||||
<attack type="HOLY" value="2200" />
|
||||
@ -5571,7 +5571,6 @@
|
||||
</dropLists>
|
||||
</npc>
|
||||
<npc id="29385" level="127" type="RaidBoss" name="Eigis" title="Lady of Despair">
|
||||
<!-- AUTO GENERATED NPC TODO: FIX IT -->
|
||||
<race>UNDEAD</race>
|
||||
<sex>MALE</sex>
|
||||
<acquire exp="4003411775" sp="3603071" />
|
||||
@ -5586,6 +5585,7 @@
|
||||
<hitTime>600</hitTime>
|
||||
</stats>
|
||||
<status attackable="true" />
|
||||
<ai type="MAGE" clanHelpRange="2000" aggroRange="2000" />
|
||||
<collision>
|
||||
<radius normal="36" />
|
||||
<height normal="123" />
|
||||
@ -5597,6 +5597,10 @@
|
||||
<skill id="32909" level="1" /> <!-- Rule of Looting -->
|
||||
<skill id="32675" level="1" /> <!-- Improved Immunity -->
|
||||
<skill id="14804" level="10" /> <!-- Damage Reflection Resistance -->
|
||||
<skill id="34814" level="9" /> <!-- Critical Rate Resistance -->
|
||||
<skill id="34815" level="9" /> <!-- Critical Damage Resistance -->
|
||||
<skill id="34820" level="7" /> <!-- Received Damage Resistance -->
|
||||
<skill id="34108" level="1" /> <!-- Blade of Souls -->
|
||||
</skillList>
|
||||
<dropLists>
|
||||
<drop>
|
||||
|
@ -111,43 +111,115 @@
|
||||
<icon>icon.skill0000</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>SQUARE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<castRange>400</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<coolTime>600</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<effectRange>400</effectRange>
|
||||
<fanRange>0;0;500;500</fanRange>
|
||||
<hitTime>1300</hitTime>
|
||||
<magicCriticalRate>-5</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<effects>
|
||||
<effect name="MagicalDamage">
|
||||
<power>28000</power>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34109" toLevel="1" name="Blade Temptation">
|
||||
<!-- Pulls enemies around the target stunning them. -->
|
||||
<icon>icon.skill0100</icon>
|
||||
<operateType>A1</operateType>
|
||||
<operateType>DA2</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalTime>3</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<abnormalLevel>4</abnormalLevel>
|
||||
<abnormalTime>6</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>70</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>150</affectRange>
|
||||
<affectRange>500</affectRange>
|
||||
<affectScope>RANGE</affectScope>
|
||||
<basicProperty>PHYSICAL</basicProperty>
|
||||
<castRange>2000</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<coolTime>1200</coolTime>
|
||||
<effectPoint>-711</effectPoint>
|
||||
<effectRange>2000</effectRange>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>5000</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<lvlBonusRate>20</lvlBonusRate>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>127</magicLevel>
|
||||
<nextAction>ATTACK</nextAction>
|
||||
<reuseDelay>10000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>158600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>100</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>150</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34110" toLevel="1" name="Price for Temptation">
|
||||
<!-- Immediately after pulling. -->
|
||||
<icon>icon.skill10262</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>1</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>400</affectRange>
|
||||
<affectScope>RANGE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>800</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>2000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>105</magicLevel>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>5000</reuseDelay>
|
||||
<trait>SHOCK</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>9800000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34111" toLevel="2" name="Teleport">
|
||||
<!-- Level 1: Presentation skill for starting teleportation. -->
|
||||
@ -182,16 +254,53 @@
|
||||
<icon>icon.skill3080</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-1</effectPoint>
|
||||
<hitTime>4000</hitTime>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>7</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>1100</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<castRange>1000</castRange>
|
||||
<coolTime>1100</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5500</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicCriticalRate>-5</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>10000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>158600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>150</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34114" toLevel="1" name="Gate of Thousand Flashes">
|
||||
<!-- Deals great damage to nearby enemies knocking them down. -->
|
||||
@ -214,32 +323,78 @@
|
||||
<icon>icon.skill33915</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>2500</hitTime>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>50</activateRate>
|
||||
<activateRate>70</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>1000</affectRange>
|
||||
<affectScope>SQUARE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>PHYSICAL</basicProperty>
|
||||
<castRange>500</castRange>
|
||||
<coolTime>1200</coolTime>
|
||||
<effectPoint>-716</effectPoint>
|
||||
<effectRange>2600</effectRange>
|
||||
<fanRange>0;0;1400;250</fanRange>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>2600</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<isMagic>0</isMagic>
|
||||
<magicCriticalRate>50</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>5000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>169600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>250</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34116" toLevel="1" name="Dark Shadow Clash">
|
||||
<!-- Deals great damage to nearby enemies knocking them down. The more enemies in the area of attack, the more damage they receive. -->
|
||||
<icon>icon.skill1064</icon>
|
||||
<operateType>DA2</operateType>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>2000</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<hitTime>6000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>8000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34117" toLevel="1" name="Dark Protection">
|
||||
<!-- Dark energy protects you. Strikes attacking enemies with Dark Attack knocking them down. -->
|
||||
|
@ -693,6 +693,34 @@
|
||||
<!-- Level 2: Presentation skill for finishing teleportation. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>2</abnormalTime>
|
||||
<abnormalType>STUN</abnormalType>
|
||||
<abnormalVisualEffect>STUN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>2000</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-5000</effectPoint>
|
||||
<effectRange>400</effectRange>
|
||||
<hitTime>300</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>120</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<trait>PULL</trait>
|
||||
<effects>
|
||||
<effect name="PullBack">
|
||||
<speed>600</speed>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34389" toLevel="1" name="World End">
|
||||
<!-- Mental Attack deals great and even fatal damage to nearby enemies and forces them to dance. -->
|
||||
@ -713,6 +741,20 @@
|
||||
<!-- Presentation Skill. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34393" toLevel="1" name="Tear to Shreds">
|
||||
<!-- Dark energy strikes the target knocking them down. -->
|
||||
|
@ -134,4 +134,24 @@
|
||||
<node X="-4026" Y="185137" />
|
||||
<node X="-7370" Y="186216" />
|
||||
</zone>
|
||||
<zone name="Eigis_Seat_Zone" type="ArenaZone" shape="NPoly" minZ="-4000" maxZ="4900">
|
||||
<node X="-20955" Y="-222973" />
|
||||
<node X="-23272" Y="-223768" />
|
||||
<node X="-24520" Y="-224200" />
|
||||
<node X="-24968" Y="-223352" />
|
||||
<node X="-25512" Y="-222264" />
|
||||
<node X="-25976" Y="-221288" />
|
||||
<node X="-26035" Y="-218613" />
|
||||
<node X="-26020" Y="-218313" />
|
||||
<node X="-25646" Y="-217960" />
|
||||
<node X="-23748" Y="-219030" />
|
||||
<node X="-22744" Y="-219512" />
|
||||
<node X="-22616" Y="-219704" />
|
||||
<node X="-21659" Y="-220231" />
|
||||
<node X="-21414" Y="-220124" />
|
||||
<node X="-21336" Y="-220360" />
|
||||
<node X="-21624" Y="-220984" />
|
||||
<node X="-21208" Y="-221752" />
|
||||
<node X="-20872" Y="-222712" />
|
||||
</zone>
|
||||
</list>
|
@ -1,74 +1,117 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* Copyright (c) 2013 L2jMobius
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package ai.bosses.Eigis;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.l2jmobius.commons.threads.ThreadPool;
|
||||
import org.l2jmobius.commons.util.Rnd;
|
||||
import org.l2jmobius.gameserver.data.SpawnTable;
|
||||
import org.l2jmobius.gameserver.data.xml.NpcData;
|
||||
import org.l2jmobius.gameserver.data.xml.SkillData;
|
||||
import org.l2jmobius.gameserver.enums.SkillFinishType;
|
||||
import org.l2jmobius.gameserver.instancemanager.DBSpawnManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
|
||||
import org.l2jmobius.gameserver.model.Location;
|
||||
import org.l2jmobius.gameserver.model.Spawn;
|
||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||
import org.l2jmobius.gameserver.model.actor.Npc;
|
||||
import org.l2jmobius.gameserver.model.actor.Player;
|
||||
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
||||
import org.l2jmobius.gameserver.model.holders.SkillHolder;
|
||||
import org.l2jmobius.gameserver.model.skill.Skill;
|
||||
import org.l2jmobius.gameserver.model.zone.ZoneType;
|
||||
import org.l2jmobius.gameserver.model.zone.type.ArenaZone;
|
||||
import org.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;
|
||||
|
||||
import ai.AbstractNpcAI;
|
||||
|
||||
/**
|
||||
* @author NasSeKa
|
||||
* @author Notorion
|
||||
*/
|
||||
public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
// NPC
|
||||
// NPCs
|
||||
private static final int EIGIS = 29385;
|
||||
// Misc
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504);
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504, 0);
|
||||
private static final String EIGIS_ALIVE_VAR = "EIGIS_ALIVE";
|
||||
private static final int INVISIBLE_NPC = 18919;
|
||||
private static final int INVISIBLE_NPC_2 = 18920;
|
||||
// Skills
|
||||
private static final SkillHolder COMMON_SKILL = new SkillHolder(34108, 1); // Blade of Souls
|
||||
private static final SkillHolder SPECIAL_AOE_1 = new SkillHolder(34113, 1); // Dark Wave Spray
|
||||
private static final SkillHolder SPECIAL_AOE_VISUAL_2 = new SkillHolder(34114, 1); // Gate of Thousand Flashes
|
||||
private static final SkillHolder JUMP_TARGET_VISUAL_1 = new SkillHolder(34116, 1); // Dark Shadow Clash
|
||||
private static final SkillHolder JUMP_SKILL_DAMAGE_2 = new SkillHolder(34109, 1); // Blade Temptation
|
||||
private static final SkillHolder JUMP_IMPACT_VISUAL_3 = new SkillHolder(34110, 1); // Price for Temptation
|
||||
private static final SkillHolder NPC_AOE_SKILL = new SkillHolder(34115, 1); // Tear to Shreds
|
||||
private static final int NPC_LIFETIME = 6000;
|
||||
// Zone
|
||||
private static final ArenaZone EIGIS_ZONE = ZoneManager.getInstance().getZoneByName("Eigis_Seat_Zone", ArenaZone.class);
|
||||
// Barrier
|
||||
private static final int BARRIER_DURATION_MILLIS = 600000; // 10 minutes
|
||||
private static final int HIT_COUNT = 2000; // 2000 Number of attacks needed to destroy the barrier
|
||||
private static final int HIT_COUNT_RENEW = 500; // 500 hits in 60 seconds to continue without the barrier
|
||||
private static final int RENEW_DURATION_MILLIS = 600000; // 60 seconds of vulnerability
|
||||
private static final SkillHolder LIMIT_BARRIER = new SkillHolder(29515, 1);
|
||||
private boolean _barrierActivated = false;
|
||||
// Misc
|
||||
private boolean _specialActivated = false;
|
||||
private final AtomicBoolean _isUsingSpecialSkill = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill2 = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill3 = new AtomicBoolean(false);
|
||||
private boolean _bossInCombat = false;
|
||||
private final AtomicInteger _targetLossCount = new AtomicInteger(0);
|
||||
private boolean _vulnerablePhase = false;
|
||||
private final Map<Npc, Integer> _eigisHits = new ConcurrentHashMap<>();
|
||||
|
||||
public Eigis()
|
||||
{
|
||||
addAttackId(EIGIS);
|
||||
addSpawnId(EIGIS);
|
||||
addKillId(EIGIS);
|
||||
addAggroRangeEnterId(EIGIS);
|
||||
addExitZoneId(EIGIS_ZONE.getId());
|
||||
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final Calendar calendarEigisStart = Calendar.getInstance();
|
||||
final Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
Calendar calendarEigisStart = Calendar.getInstance();
|
||||
Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, 1); // Sunday
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
calendarEigisStart.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendarEigisStart.set(Calendar.MINUTE, 0);
|
||||
calendarEigisStart.set(Calendar.SECOND, 0);
|
||||
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, 2); // Monday
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
calendarEigisSeal.set(Calendar.HOUR_OF_DAY, 2);
|
||||
calendarEigisSeal.set(Calendar.MINUTE, 0);
|
||||
calendarEigisSeal.set(Calendar.SECOND, 0);
|
||||
|
||||
if (((currentTime > calendarEigisStart.getTimeInMillis()) && (currentTime < calendarEigisSeal.getTimeInMillis())) && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null) && GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, true))
|
||||
{
|
||||
spawnEigis();
|
||||
}
|
||||
else
|
||||
{
|
||||
despawnEigis();
|
||||
}
|
||||
|
||||
if (calendarEigisStart.getTimeInMillis() < currentTime)
|
||||
{
|
||||
calendarEigisStart.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
@ -78,32 +121,36 @@ public class Eigis extends AbstractNpcAI
|
||||
calendarEigisSeal.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
boolean bossAlive = GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false);
|
||||
if (bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
spawnEigis();
|
||||
}, calendarEigisStart.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
}
|
||||
else if (!bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) != null))
|
||||
{
|
||||
despawnEigis();
|
||||
}, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::spawnEigis, calendarEigisStart.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::despawnEigis, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
}
|
||||
|
||||
private void spawnEigis()
|
||||
{
|
||||
try
|
||||
if (!GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false) || (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
final NpcTemplate template = NpcData.getInstance().getTemplate(EIGIS);
|
||||
final Spawn spawn = new Spawn(template);
|
||||
spawn.setXYZ(EIGIS_LOCATION);
|
||||
spawn.setHeading(0);
|
||||
final Npc npc = addSpawn(EIGIS, EIGIS_LOCATION);
|
||||
npc.setRandomWalking(false);
|
||||
npc.setRandomAnimation(false);
|
||||
|
||||
final Spawn spawn = npc.getSpawn();
|
||||
spawn.setRespawnDelay(0);
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, false);
|
||||
spawn.startRespawn();
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, true);
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// LOGGER.info("Eigis spawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,17 +162,622 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
npc.deleteMe();
|
||||
}
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
// LOGGER.info("Eigis has been despawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onSpawn(Npc npc)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkCombatStatus", 1000, npc, null, true);
|
||||
startQuestTimer("checkPosition", 5000, npc, null, true);
|
||||
return super.onSpawn(npc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAggroRangeEnter(Npc npc, Player player, boolean isSummon)
|
||||
{
|
||||
_bossInCombat = true;
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
checkCombatStatus(npc);
|
||||
if (_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
return super.onAggroRangeEnter(npc, player, isSummon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onExitZone(Creature creature, ZoneType zone)
|
||||
{
|
||||
if ((creature instanceof Npc) && (creature.getId() == EIGIS))
|
||||
{
|
||||
final Npc npc = creature.asNpc();
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
cancelSpecialSkills(npc);
|
||||
}
|
||||
return super.onExitZone(creature, zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAttack(Npc npc, Player attacker, int damage, boolean isSummon, Skill skill)
|
||||
{
|
||||
if (!_barrierActivated)
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
|
||||
if (_vulnerablePhase)
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT_RENEW)
|
||||
{
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT)
|
||||
{
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
_vulnerablePhase = true;
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_bossInCombat = true;
|
||||
if (!_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
_specialActivated = true;
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
|
||||
return super.onAttack(npc, attacker, damage, isSummon, skill);
|
||||
}
|
||||
|
||||
private void activateSpecialMechanics(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
startQuestTimer("SkillsBalancer", 2000, npc, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onEvent(String event, Npc npc, Player player)
|
||||
{
|
||||
if ((npc != null) && (npc.getId() == EIGIS))
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case "activate_barrier":
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
_vulnerablePhase = false;
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "remove_barrier":
|
||||
{
|
||||
_barrierActivated = false;
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "EigisSkills1":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills2":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills3":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get())
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "SkillsBalancer":
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
if (!npc.isDead() && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
boolean hasValidTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target instanceof Player) && !target.isDead() && npc.isInsideRadius3D(target, 2000))
|
||||
{
|
||||
hasValidTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidTarget)
|
||||
{
|
||||
final int chance = getRandom(100);
|
||||
if (chance < 33)
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
else if (chance < 66)
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
startQuestTimer("SkillsBalancer", getRandom(10000, 35000), npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
resetSkillFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkTargetRange":
|
||||
{
|
||||
if ((player != null) && !player.isDead())
|
||||
{
|
||||
final double distance = player.calculateDistance3D(npc);
|
||||
if ((distance < 700) || (distance > 2000))
|
||||
{
|
||||
_targetLossCount.incrementAndGet();
|
||||
if (_targetLossCount.get() >= 2)
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomicReference<Player> newTarget = new AtomicReference<>();
|
||||
if (findTargetPlayer(npc, newTarget))
|
||||
{
|
||||
player = newTarget.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkCombatStatus":
|
||||
{
|
||||
checkCombatStatus(npc);
|
||||
break;
|
||||
}
|
||||
case "checkTargetLost":
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
break;
|
||||
}
|
||||
case "checkPosition":
|
||||
{
|
||||
if (!EIGIS_ZONE.isInsideZone(npc))
|
||||
{
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onEvent(event, npc, player);
|
||||
}
|
||||
|
||||
private void useEigisSkills1(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
final boolean useComeToMe = Rnd.get(100) < 48;
|
||||
if (useComeToMe)
|
||||
{
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.doCast(SPECIAL_AOE_1.getSkill());
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat && _isUsingSpecialSkill.get())
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, npc, SPECIAL_AOE_VISUAL_2.getSkillId(), 1, 0, 0));
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Presentation skill Lv.1 Teleport (34388)
|
||||
private void useComeToMeSkill(Npc npc)
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34388, 1));
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private void cancelDebuffs(Npc npc)
|
||||
{
|
||||
if ((npc == null) || npc.isDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.getEffectList().getEffects().forEach(effect ->
|
||||
{
|
||||
if (effect.getSkill().isDebuff())
|
||||
{
|
||||
npc.getEffectList().stopSkillEffects(SkillFinishType.REMOVED, effect.getSkill());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void useEigisSkills2(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill2.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 7000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (Rnd.get(100) < 45)
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Presentation Skill Lv.1 Tear to Shreds (34392)
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34392, 1));
|
||||
}, 3000);
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EIGIS_ZONE == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final Location bossLocation = npc.getLocation();
|
||||
final List<Npc> spawnedNpcs = new ArrayList<>();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
int x = bossLocation.getX() + (int) ((i - 2.5) * 500);
|
||||
int y = bossLocation.getY() + (int) ((j - 2.5) * 500);
|
||||
Location spawnLoc = new Location(x, y, bossLocation.getZ());
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final Npc newNpc = addSpawn(INVISIBLE_NPC, spawnLoc, false, NPC_LIFETIME);
|
||||
newNpc.setHeading(0);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final List<Npc> spawnedNpcsCopy = new ArrayList<>(spawnedNpcs);
|
||||
for (Npc npcYellow : spawnedNpcsCopy)
|
||||
{
|
||||
final Location loc = npcYellow.getLocation();
|
||||
final int npcHeading = getRandom(0, 65535);
|
||||
final Location spawnLoc = calculatePointFromLocation(loc.getX(), loc.getY(), loc.getZ(), 100, npcHeading);
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Npc newNpc = addSpawn(INVISIBLE_NPC_2, spawnLoc, false, NPC_LIFETIME);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.setHeading(npcHeading);
|
||||
newNpc.teleToLocation(newNpc.getX(), newNpc.getY(), newNpc.getZ(), npcHeading);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private Location calculatePointFromLocation(int x, int y, int z, int distance, int heading)
|
||||
{
|
||||
final double angle = Math.toRadians(heading * 0.0054931640625);
|
||||
final int newX = x + (int) (distance * Math.cos(angle));
|
||||
final int newY = y + (int) (distance * Math.sin(angle));
|
||||
return new Location(newX, newY, z);
|
||||
}
|
||||
|
||||
private void useEigisSkills3(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill3.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
final AtomicReference<Player> targetPlayer = new AtomicReference<>();
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!findTargetPlayer(npc, targetPlayer))
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
return;
|
||||
}
|
||||
|
||||
final Player target = targetPlayer.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
npc.setTarget(target);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_TARGET_VISUAL_1.getSkillId(), 1, 3000, 0));
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.doCast(JUMP_SKILL_DAMAGE_2.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_IMPACT_VISUAL_3.getSkillId(), 1, 2000, 0));
|
||||
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}, 6000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private boolean findTargetPlayer(Npc npc, AtomicReference<Player> targetPlayer)
|
||||
{
|
||||
final List<Player> playersInRange = new ArrayList<>();
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && (target instanceof Player))
|
||||
{
|
||||
final Player player = (Player) target;
|
||||
final double distance = npc.calculateDistance3D(player);
|
||||
if ((distance >= 700) && (distance <= 2000))
|
||||
{
|
||||
playersInRange.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!playersInRange.isEmpty())
|
||||
{
|
||||
targetPlayer.set(playersInRange.get(Rnd.get(playersInRange.size())));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resetSkillFlags()
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
|
||||
private void cancelSkill3(Npc npc)
|
||||
{
|
||||
cancelQuestTimer("checkTargetRange", npc, null);
|
||||
resetSkillFlags();
|
||||
}
|
||||
|
||||
private void cancelSpecialSkills(Npc npc)
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
|
||||
private void checkCombatStatus(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
boolean hasTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
hasTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTarget)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkTargetLost", 8000, npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_specialActivated)
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
_specialActivated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onKill(Npc npc, Player killer, boolean isSummon)
|
||||
{
|
||||
if (npc.getId() == EIGIS)
|
||||
{
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
}
|
||||
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
npc.setInvul(false);
|
||||
_eigisHits.clear();
|
||||
_barrierActivated = false;
|
||||
_vulnerablePhase = false;
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
cancelQuestTimer("checkCombatStatus", npc, null);
|
||||
cancelQuestTimer("checkPosition", npc, null);
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
return super.onKill(npc, killer, isSummon);
|
||||
}
|
||||
|
||||
@ -133,4 +785,4 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
new Eigis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5603,7 +5603,6 @@
|
||||
</dropLists>
|
||||
</npc>
|
||||
<npc id="29385" level="127" type="RaidBoss" name="Eigis" title="Lady of Despair">
|
||||
<!-- AUTO GENERATED NPC TODO: FIX IT -->
|
||||
<race>UNDEAD</race>
|
||||
<sex>MALE</sex>
|
||||
<acquire exp="4003411775" sp="3603071" />
|
||||
@ -5615,13 +5614,14 @@
|
||||
<walk ground="60" />
|
||||
<run ground="180" />
|
||||
</speed>
|
||||
<hitTime>600</hitTime>
|
||||
<hitTime>950</hitTime>
|
||||
<attribute>
|
||||
<attack type="DARK" value="2550" />
|
||||
<defence fire="2650" water="2650" wind="2650" earth="2650" holy="2600" dark="2650" />
|
||||
</attribute>
|
||||
</stats>
|
||||
<status attackable="true" />
|
||||
<ai type="MAGE" clanHelpRange="2000" aggroRange="2000" />
|
||||
<collision>
|
||||
<radius normal="36" />
|
||||
<height normal="123" />
|
||||
@ -5633,6 +5633,10 @@
|
||||
<skill id="32909" level="1" /> <!-- Rule of Looting -->
|
||||
<skill id="32675" level="1" /> <!-- Improved Immunity -->
|
||||
<skill id="14804" level="10" /> <!-- Damage Reflection Resistance -->
|
||||
<skill id="34814" level="9" /> <!-- Critical Rate Resistance -->
|
||||
<skill id="34815" level="9" /> <!-- Critical Damage Resistance -->
|
||||
<skill id="34820" level="7" /> <!-- Received Damage Resistance -->
|
||||
<skill id="34108" level="1" /> <!-- Blade of Souls -->
|
||||
</skillList>
|
||||
<dropLists>
|
||||
<drop>
|
||||
|
@ -111,43 +111,115 @@
|
||||
<icon>icon.skill0000</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>SQUARE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<castRange>400</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<coolTime>600</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<effectRange>400</effectRange>
|
||||
<fanRange>0;0;500;500</fanRange>
|
||||
<hitTime>1300</hitTime>
|
||||
<magicCriticalRate>-5</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<effects>
|
||||
<effect name="MagicalDamage">
|
||||
<power>28000</power>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34109" toLevel="1" name="Blade Temptation">
|
||||
<!-- Pulls enemies around the target stunning them. -->
|
||||
<icon>icon.skill0100</icon>
|
||||
<operateType>A1</operateType>
|
||||
<operateType>DA2</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalTime>3</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<abnormalLevel>4</abnormalLevel>
|
||||
<abnormalTime>6</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>70</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>150</affectRange>
|
||||
<affectRange>500</affectRange>
|
||||
<affectScope>RANGE</affectScope>
|
||||
<basicProperty>PHYSICAL</basicProperty>
|
||||
<castRange>2000</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<coolTime>1200</coolTime>
|
||||
<effectPoint>-711</effectPoint>
|
||||
<effectRange>2000</effectRange>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>5000</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<lvlBonusRate>20</lvlBonusRate>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>127</magicLevel>
|
||||
<nextAction>ATTACK</nextAction>
|
||||
<reuseDelay>10000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>158600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>100</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>150</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34110" toLevel="1" name="Price for Temptation">
|
||||
<!-- Immediately after pulling. -->
|
||||
<icon>icon.skill10262</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>1</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>400</affectRange>
|
||||
<affectScope>RANGE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>800</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>2000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>105</magicLevel>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>5000</reuseDelay>
|
||||
<trait>SHOCK</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>9800000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34111" toLevel="2" name="Teleport">
|
||||
<!-- Level 1: Presentation skill for starting teleportation. -->
|
||||
@ -182,16 +254,53 @@
|
||||
<icon>icon.skill3080</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-1</effectPoint>
|
||||
<hitTime>4000</hitTime>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>7</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>1100</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<castRange>1000</castRange>
|
||||
<coolTime>1100</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5500</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicCriticalRate>-5</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>10000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>158600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>150</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34114" toLevel="1" name="Gate of Thousand Flashes">
|
||||
<!-- Deals great damage to nearby enemies knocking them down. -->
|
||||
@ -214,32 +323,78 @@
|
||||
<icon>icon.skill33915</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>2500</hitTime>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>50</activateRate>
|
||||
<activateRate>70</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>1000</affectRange>
|
||||
<affectScope>SQUARE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>PHYSICAL</basicProperty>
|
||||
<castRange>500</castRange>
|
||||
<coolTime>1200</coolTime>
|
||||
<effectPoint>-716</effectPoint>
|
||||
<effectRange>2600</effectRange>
|
||||
<fanRange>0;0;1400;250</fanRange>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>2600</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<isMagic>0</isMagic>
|
||||
<magicCriticalRate>50</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>5000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>169600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>250</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34116" toLevel="1" name="Dark Shadow Clash">
|
||||
<!-- Deals great damage to nearby enemies knocking them down. The more enemies in the area of attack, the more damage they receive. -->
|
||||
<icon>icon.skill1064</icon>
|
||||
<operateType>DA2</operateType>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>2000</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<hitTime>6000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>8000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34117" toLevel="1" name="Dark Protection">
|
||||
<!-- Dark energy protects you. Strikes attacking enemies with Dark Attack knocking them down. -->
|
||||
|
@ -693,6 +693,34 @@
|
||||
<!-- Level 2: Presentation skill for finishing teleportation. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>2</abnormalTime>
|
||||
<abnormalType>STUN</abnormalType>
|
||||
<abnormalVisualEffect>STUN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>2000</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-5000</effectPoint>
|
||||
<effectRange>400</effectRange>
|
||||
<hitTime>300</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>120</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<trait>PULL</trait>
|
||||
<effects>
|
||||
<effect name="PullBack">
|
||||
<speed>600</speed>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34389" toLevel="1" name="World End">
|
||||
<!-- Mental Attack deals great and even fatal damage to nearby enemies and forces them to dance. -->
|
||||
@ -713,6 +741,20 @@
|
||||
<!-- Presentation Skill. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34393" toLevel="1" name="Tear to Shreds">
|
||||
<!-- Dark energy strikes the target knocking them down. -->
|
||||
|
@ -134,4 +134,24 @@
|
||||
<node X="-4026" Y="185137" />
|
||||
<node X="-7370" Y="186216" />
|
||||
</zone>
|
||||
<zone name="Eigis_Seat_Zone" type="ArenaZone" shape="NPoly" minZ="-4000" maxZ="4900">
|
||||
<node X="-20955" Y="-222973" />
|
||||
<node X="-23272" Y="-223768" />
|
||||
<node X="-24520" Y="-224200" />
|
||||
<node X="-24968" Y="-223352" />
|
||||
<node X="-25512" Y="-222264" />
|
||||
<node X="-25976" Y="-221288" />
|
||||
<node X="-26035" Y="-218613" />
|
||||
<node X="-26020" Y="-218313" />
|
||||
<node X="-25646" Y="-217960" />
|
||||
<node X="-23748" Y="-219030" />
|
||||
<node X="-22744" Y="-219512" />
|
||||
<node X="-22616" Y="-219704" />
|
||||
<node X="-21659" Y="-220231" />
|
||||
<node X="-21414" Y="-220124" />
|
||||
<node X="-21336" Y="-220360" />
|
||||
<node X="-21624" Y="-220984" />
|
||||
<node X="-21208" Y="-221752" />
|
||||
<node X="-20872" Y="-222712" />
|
||||
</zone>
|
||||
</list>
|
@ -1,74 +1,117 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* Copyright (c) 2013 L2jMobius
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package ai.bosses.Eigis;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.l2jmobius.commons.threads.ThreadPool;
|
||||
import org.l2jmobius.commons.util.Rnd;
|
||||
import org.l2jmobius.gameserver.data.SpawnTable;
|
||||
import org.l2jmobius.gameserver.data.xml.NpcData;
|
||||
import org.l2jmobius.gameserver.data.xml.SkillData;
|
||||
import org.l2jmobius.gameserver.enums.SkillFinishType;
|
||||
import org.l2jmobius.gameserver.instancemanager.DBSpawnManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
|
||||
import org.l2jmobius.gameserver.model.Location;
|
||||
import org.l2jmobius.gameserver.model.Spawn;
|
||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||
import org.l2jmobius.gameserver.model.actor.Npc;
|
||||
import org.l2jmobius.gameserver.model.actor.Player;
|
||||
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
||||
import org.l2jmobius.gameserver.model.holders.SkillHolder;
|
||||
import org.l2jmobius.gameserver.model.skill.Skill;
|
||||
import org.l2jmobius.gameserver.model.zone.ZoneType;
|
||||
import org.l2jmobius.gameserver.model.zone.type.ArenaZone;
|
||||
import org.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;
|
||||
|
||||
import ai.AbstractNpcAI;
|
||||
|
||||
/**
|
||||
* @author NasSeKa
|
||||
* @author Notorion
|
||||
*/
|
||||
public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
// NPC
|
||||
// NPCs
|
||||
private static final int EIGIS = 29385;
|
||||
// Misc
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504);
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504, 0);
|
||||
private static final String EIGIS_ALIVE_VAR = "EIGIS_ALIVE";
|
||||
private static final int INVISIBLE_NPC = 18919;
|
||||
private static final int INVISIBLE_NPC_2 = 18920;
|
||||
// Skills
|
||||
private static final SkillHolder COMMON_SKILL = new SkillHolder(34108, 1); // Blade of Souls
|
||||
private static final SkillHolder SPECIAL_AOE_1 = new SkillHolder(34113, 1); // Dark Wave Spray
|
||||
private static final SkillHolder SPECIAL_AOE_VISUAL_2 = new SkillHolder(34114, 1); // Gate of Thousand Flashes
|
||||
private static final SkillHolder JUMP_TARGET_VISUAL_1 = new SkillHolder(34116, 1); // Dark Shadow Clash
|
||||
private static final SkillHolder JUMP_SKILL_DAMAGE_2 = new SkillHolder(34109, 1); // Blade Temptation
|
||||
private static final SkillHolder JUMP_IMPACT_VISUAL_3 = new SkillHolder(34110, 1); // Price for Temptation
|
||||
private static final SkillHolder NPC_AOE_SKILL = new SkillHolder(34115, 1); // Tear to Shreds
|
||||
private static final int NPC_LIFETIME = 6000;
|
||||
// Zone
|
||||
private static final ArenaZone EIGIS_ZONE = ZoneManager.getInstance().getZoneByName("Eigis_Seat_Zone", ArenaZone.class);
|
||||
// Barrier
|
||||
private static final int BARRIER_DURATION_MILLIS = 600000; // 10 minutes
|
||||
private static final int HIT_COUNT = 2000; // 2000 Number of attacks needed to destroy the barrier
|
||||
private static final int HIT_COUNT_RENEW = 500; // 500 hits in 60 seconds to continue without the barrier
|
||||
private static final int RENEW_DURATION_MILLIS = 600000; // 60 seconds of vulnerability
|
||||
private static final SkillHolder LIMIT_BARRIER = new SkillHolder(29515, 1);
|
||||
private boolean _barrierActivated = false;
|
||||
// Misc
|
||||
private boolean _specialActivated = false;
|
||||
private final AtomicBoolean _isUsingSpecialSkill = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill2 = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill3 = new AtomicBoolean(false);
|
||||
private boolean _bossInCombat = false;
|
||||
private final AtomicInteger _targetLossCount = new AtomicInteger(0);
|
||||
private boolean _vulnerablePhase = false;
|
||||
private final Map<Npc, Integer> _eigisHits = new ConcurrentHashMap<>();
|
||||
|
||||
public Eigis()
|
||||
{
|
||||
addAttackId(EIGIS);
|
||||
addSpawnId(EIGIS);
|
||||
addKillId(EIGIS);
|
||||
addAggroRangeEnterId(EIGIS);
|
||||
addExitZoneId(EIGIS_ZONE.getId());
|
||||
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final Calendar calendarEigisStart = Calendar.getInstance();
|
||||
final Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
Calendar calendarEigisStart = Calendar.getInstance();
|
||||
Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, 1); // Sunday
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
calendarEigisStart.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendarEigisStart.set(Calendar.MINUTE, 0);
|
||||
calendarEigisStart.set(Calendar.SECOND, 0);
|
||||
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, 2); // Monday
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
calendarEigisSeal.set(Calendar.HOUR_OF_DAY, 2);
|
||||
calendarEigisSeal.set(Calendar.MINUTE, 0);
|
||||
calendarEigisSeal.set(Calendar.SECOND, 0);
|
||||
|
||||
if (((currentTime > calendarEigisStart.getTimeInMillis()) && (currentTime < calendarEigisSeal.getTimeInMillis())) && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null) && GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, true))
|
||||
{
|
||||
spawnEigis();
|
||||
}
|
||||
else
|
||||
{
|
||||
despawnEigis();
|
||||
}
|
||||
|
||||
if (calendarEigisStart.getTimeInMillis() < currentTime)
|
||||
{
|
||||
calendarEigisStart.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
@ -78,32 +121,36 @@ public class Eigis extends AbstractNpcAI
|
||||
calendarEigisSeal.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
boolean bossAlive = GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false);
|
||||
if (bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
spawnEigis();
|
||||
}, calendarEigisStart.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
}
|
||||
else if (!bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) != null))
|
||||
{
|
||||
despawnEigis();
|
||||
}, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::spawnEigis, calendarEigisStart.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::despawnEigis, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
}
|
||||
|
||||
private void spawnEigis()
|
||||
{
|
||||
try
|
||||
if (!GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false) || (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
final NpcTemplate template = NpcData.getInstance().getTemplate(EIGIS);
|
||||
final Spawn spawn = new Spawn(template);
|
||||
spawn.setXYZ(EIGIS_LOCATION);
|
||||
spawn.setHeading(0);
|
||||
final Npc npc = addSpawn(EIGIS, EIGIS_LOCATION);
|
||||
npc.setRandomWalking(false);
|
||||
npc.setRandomAnimation(false);
|
||||
|
||||
final Spawn spawn = npc.getSpawn();
|
||||
spawn.setRespawnDelay(0);
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, false);
|
||||
spawn.startRespawn();
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, true);
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// LOGGER.info("Eigis spawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,17 +162,622 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
npc.deleteMe();
|
||||
}
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
// LOGGER.info("Eigis has been despawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onSpawn(Npc npc)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkCombatStatus", 1000, npc, null, true);
|
||||
startQuestTimer("checkPosition", 5000, npc, null, true);
|
||||
return super.onSpawn(npc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAggroRangeEnter(Npc npc, Player player, boolean isSummon)
|
||||
{
|
||||
_bossInCombat = true;
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
checkCombatStatus(npc);
|
||||
if (_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
return super.onAggroRangeEnter(npc, player, isSummon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onExitZone(Creature creature, ZoneType zone)
|
||||
{
|
||||
if ((creature instanceof Npc) && (creature.getId() == EIGIS))
|
||||
{
|
||||
final Npc npc = creature.asNpc();
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
cancelSpecialSkills(npc);
|
||||
}
|
||||
return super.onExitZone(creature, zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAttack(Npc npc, Player attacker, int damage, boolean isSummon, Skill skill)
|
||||
{
|
||||
if (!_barrierActivated)
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
|
||||
if (_vulnerablePhase)
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT_RENEW)
|
||||
{
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT)
|
||||
{
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
_vulnerablePhase = true;
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_bossInCombat = true;
|
||||
if (!_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
_specialActivated = true;
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
|
||||
return super.onAttack(npc, attacker, damage, isSummon, skill);
|
||||
}
|
||||
|
||||
private void activateSpecialMechanics(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
startQuestTimer("SkillsBalancer", 2000, npc, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onEvent(String event, Npc npc, Player player)
|
||||
{
|
||||
if ((npc != null) && (npc.getId() == EIGIS))
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case "activate_barrier":
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
_vulnerablePhase = false;
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "remove_barrier":
|
||||
{
|
||||
_barrierActivated = false;
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "EigisSkills1":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills2":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills3":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get())
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "SkillsBalancer":
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
if (!npc.isDead() && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
boolean hasValidTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target instanceof Player) && !target.isDead() && npc.isInsideRadius3D(target, 2000))
|
||||
{
|
||||
hasValidTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidTarget)
|
||||
{
|
||||
final int chance = getRandom(100);
|
||||
if (chance < 33)
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
else if (chance < 66)
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
startQuestTimer("SkillsBalancer", getRandom(10000, 35000), npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
resetSkillFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkTargetRange":
|
||||
{
|
||||
if ((player != null) && !player.isDead())
|
||||
{
|
||||
final double distance = player.calculateDistance3D(npc);
|
||||
if ((distance < 700) || (distance > 2000))
|
||||
{
|
||||
_targetLossCount.incrementAndGet();
|
||||
if (_targetLossCount.get() >= 2)
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomicReference<Player> newTarget = new AtomicReference<>();
|
||||
if (findTargetPlayer(npc, newTarget))
|
||||
{
|
||||
player = newTarget.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkCombatStatus":
|
||||
{
|
||||
checkCombatStatus(npc);
|
||||
break;
|
||||
}
|
||||
case "checkTargetLost":
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
break;
|
||||
}
|
||||
case "checkPosition":
|
||||
{
|
||||
if (!EIGIS_ZONE.isInsideZone(npc))
|
||||
{
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onEvent(event, npc, player);
|
||||
}
|
||||
|
||||
private void useEigisSkills1(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
final boolean useComeToMe = Rnd.get(100) < 48;
|
||||
if (useComeToMe)
|
||||
{
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.doCast(SPECIAL_AOE_1.getSkill());
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat && _isUsingSpecialSkill.get())
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, npc, SPECIAL_AOE_VISUAL_2.getSkillId(), 1, 0, 0));
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Presentation skill Lv.1 Teleport (34388)
|
||||
private void useComeToMeSkill(Npc npc)
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34388, 1));
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private void cancelDebuffs(Npc npc)
|
||||
{
|
||||
if ((npc == null) || npc.isDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.getEffectList().getEffects().forEach(effect ->
|
||||
{
|
||||
if (effect.getSkill().isDebuff())
|
||||
{
|
||||
npc.getEffectList().stopSkillEffects(SkillFinishType.REMOVED, effect.getSkill());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void useEigisSkills2(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill2.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 7000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (Rnd.get(100) < 45)
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Presentation Skill Lv.1 Tear to Shreds (34392)
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34392, 1));
|
||||
}, 3000);
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EIGIS_ZONE == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final Location bossLocation = npc.getLocation();
|
||||
final List<Npc> spawnedNpcs = new ArrayList<>();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
int x = bossLocation.getX() + (int) ((i - 2.5) * 500);
|
||||
int y = bossLocation.getY() + (int) ((j - 2.5) * 500);
|
||||
Location spawnLoc = new Location(x, y, bossLocation.getZ());
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final Npc newNpc = addSpawn(INVISIBLE_NPC, spawnLoc, false, NPC_LIFETIME);
|
||||
newNpc.setHeading(0);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final List<Npc> spawnedNpcsCopy = new ArrayList<>(spawnedNpcs);
|
||||
for (Npc npcYellow : spawnedNpcsCopy)
|
||||
{
|
||||
final Location loc = npcYellow.getLocation();
|
||||
final int npcHeading = getRandom(0, 65535);
|
||||
final Location spawnLoc = calculatePointFromLocation(loc.getX(), loc.getY(), loc.getZ(), 100, npcHeading);
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Npc newNpc = addSpawn(INVISIBLE_NPC_2, spawnLoc, false, NPC_LIFETIME);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.setHeading(npcHeading);
|
||||
newNpc.teleToLocation(newNpc.getX(), newNpc.getY(), newNpc.getZ(), npcHeading);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private Location calculatePointFromLocation(int x, int y, int z, int distance, int heading)
|
||||
{
|
||||
final double angle = Math.toRadians(heading * 0.0054931640625);
|
||||
final int newX = x + (int) (distance * Math.cos(angle));
|
||||
final int newY = y + (int) (distance * Math.sin(angle));
|
||||
return new Location(newX, newY, z);
|
||||
}
|
||||
|
||||
private void useEigisSkills3(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill3.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
final AtomicReference<Player> targetPlayer = new AtomicReference<>();
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!findTargetPlayer(npc, targetPlayer))
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
return;
|
||||
}
|
||||
|
||||
final Player target = targetPlayer.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
npc.setTarget(target);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_TARGET_VISUAL_1.getSkillId(), 1, 3000, 0));
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.doCast(JUMP_SKILL_DAMAGE_2.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_IMPACT_VISUAL_3.getSkillId(), 1, 2000, 0));
|
||||
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}, 6000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private boolean findTargetPlayer(Npc npc, AtomicReference<Player> targetPlayer)
|
||||
{
|
||||
final List<Player> playersInRange = new ArrayList<>();
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && (target instanceof Player))
|
||||
{
|
||||
final Player player = (Player) target;
|
||||
final double distance = npc.calculateDistance3D(player);
|
||||
if ((distance >= 700) && (distance <= 2000))
|
||||
{
|
||||
playersInRange.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!playersInRange.isEmpty())
|
||||
{
|
||||
targetPlayer.set(playersInRange.get(Rnd.get(playersInRange.size())));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resetSkillFlags()
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
|
||||
private void cancelSkill3(Npc npc)
|
||||
{
|
||||
cancelQuestTimer("checkTargetRange", npc, null);
|
||||
resetSkillFlags();
|
||||
}
|
||||
|
||||
private void cancelSpecialSkills(Npc npc)
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
|
||||
private void checkCombatStatus(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
boolean hasTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
hasTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTarget)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkTargetLost", 8000, npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_specialActivated)
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
_specialActivated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onKill(Npc npc, Player killer, boolean isSummon)
|
||||
{
|
||||
if (npc.getId() == EIGIS)
|
||||
{
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
}
|
||||
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
npc.setInvul(false);
|
||||
_eigisHits.clear();
|
||||
_barrierActivated = false;
|
||||
_vulnerablePhase = false;
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
cancelQuestTimer("checkCombatStatus", npc, null);
|
||||
cancelQuestTimer("checkPosition", npc, null);
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
return super.onKill(npc, killer, isSummon);
|
||||
}
|
||||
|
||||
@ -133,4 +785,4 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
new Eigis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5603,25 +5603,25 @@
|
||||
</dropLists>
|
||||
</npc>
|
||||
<npc id="29385" level="127" type="RaidBoss" name="Eigis" title="Lady of Despair">
|
||||
<!-- AUTO GENERATED NPC TODO: FIX IT -->
|
||||
<race>UNDEAD</race>
|
||||
<sex>MALE</sex>
|
||||
<acquire exp="4003411775" sp="3603071" />
|
||||
<stats str="164" int="188" dex="55" wit="78" con="111" men="149">
|
||||
<vitals hp="800175000" hpRegen="13.4" mp="68076" mpRegen="30" />
|
||||
<attack physical="639686" magical="583012" random="10" critical="4" accuracy="5" attackSpeed="253" type="SWORD" range="40" distance="80" width="120" />
|
||||
<vitals hp="800175000" hpRegen="150075.4" mp="68076" mpRegen="30" />
|
||||
<attack physical="639686" magical="583012" random="10" critical="4" accuracy="5" attackSpeed="253" type="SWORD" range="150" distance="150" width="120" />
|
||||
<defence physical="301296" magical="1458092" />
|
||||
<speed>
|
||||
<walk ground="60" />
|
||||
<run ground="180" />
|
||||
</speed>
|
||||
<hitTime>600</hitTime>
|
||||
<hitTime>950</hitTime>
|
||||
<attribute>
|
||||
<attack type="DARK" value="2550" />
|
||||
<defence fire="2650" water="2650" wind="2650" earth="2650" holy="2600" dark="2650" />
|
||||
</attribute>
|
||||
</stats>
|
||||
<status attackable="true" />
|
||||
<ai type="MAGE" clanHelpRange="2000" aggroRange="2000" />
|
||||
<collision>
|
||||
<radius normal="36" />
|
||||
<height normal="123" />
|
||||
@ -5633,6 +5633,10 @@
|
||||
<skill id="32909" level="1" /> <!-- Rule of Looting -->
|
||||
<skill id="32675" level="1" /> <!-- Improved Immunity -->
|
||||
<skill id="14804" level="10" /> <!-- Damage Reflection Resistance -->
|
||||
<skill id="34814" level="9" /> <!-- Critical Rate Resistance -->
|
||||
<skill id="34815" level="9" /> <!-- Critical Damage Resistance -->
|
||||
<skill id="34820" level="7" /> <!-- Received Damage Resistance -->
|
||||
<skill id="34108" level="1" /> <!-- Blade of Souls -->
|
||||
</skillList>
|
||||
<dropLists>
|
||||
<drop>
|
||||
|
@ -111,43 +111,115 @@
|
||||
<icon>icon.skill0000</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>SQUARE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<castRange>400</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<coolTime>600</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<effectRange>400</effectRange>
|
||||
<fanRange>0;0;500;500</fanRange>
|
||||
<hitTime>1300</hitTime>
|
||||
<magicCriticalRate>-5</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<effects>
|
||||
<effect name="MagicalDamage">
|
||||
<power>28000</power>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34109" toLevel="1" name="Blade Temptation">
|
||||
<!-- Pulls enemies around the target stunning them. -->
|
||||
<icon>icon.skill0100</icon>
|
||||
<operateType>A1</operateType>
|
||||
<operateType>DA2</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalTime>3</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<abnormalLevel>4</abnormalLevel>
|
||||
<abnormalTime>6</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>70</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>150</affectRange>
|
||||
<affectRange>500</affectRange>
|
||||
<affectScope>RANGE</affectScope>
|
||||
<basicProperty>PHYSICAL</basicProperty>
|
||||
<castRange>2000</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<coolTime>1200</coolTime>
|
||||
<effectPoint>-711</effectPoint>
|
||||
<effectRange>2000</effectRange>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>5000</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<lvlBonusRate>20</lvlBonusRate>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>127</magicLevel>
|
||||
<nextAction>ATTACK</nextAction>
|
||||
<reuseDelay>10000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>158600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>100</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>150</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34110" toLevel="1" name="Price for Temptation">
|
||||
<!-- Immediately after pulling. -->
|
||||
<icon>icon.skill10262</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>1</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>400</affectRange>
|
||||
<affectScope>RANGE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>800</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>2000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>105</magicLevel>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>5000</reuseDelay>
|
||||
<trait>SHOCK</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>9800000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34111" toLevel="2" name="Teleport">
|
||||
<!-- Level 1: Presentation skill for starting teleportation. -->
|
||||
@ -182,16 +254,53 @@
|
||||
<icon>icon.skill3080</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-1</effectPoint>
|
||||
<hitTime>4000</hitTime>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>7</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>1100</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<castRange>1000</castRange>
|
||||
<coolTime>1100</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5500</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicCriticalRate>-5</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>10000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>158600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>150</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34114" toLevel="1" name="Gate of Thousand Flashes">
|
||||
<!-- Deals great damage to nearby enemies knocking them down. -->
|
||||
@ -214,32 +323,78 @@
|
||||
<icon>icon.skill33915</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>2500</hitTime>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>50</activateRate>
|
||||
<activateRate>70</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>1000</affectRange>
|
||||
<affectScope>SQUARE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>PHYSICAL</basicProperty>
|
||||
<castRange>500</castRange>
|
||||
<coolTime>1200</coolTime>
|
||||
<effectPoint>-716</effectPoint>
|
||||
<effectRange>2600</effectRange>
|
||||
<fanRange>0;0;1400;250</fanRange>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>2600</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<isMagic>0</isMagic>
|
||||
<magicCriticalRate>50</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>5000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>169600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>250</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34116" toLevel="1" name="Dark Shadow Clash">
|
||||
<!-- Deals great damage to nearby enemies knocking them down. The more enemies in the area of attack, the more damage they receive. -->
|
||||
<icon>icon.skill1064</icon>
|
||||
<operateType>DA2</operateType>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>2000</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<hitTime>6000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>8000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34117" toLevel="1" name="Dark Protection">
|
||||
<!-- Dark energy protects you. Strikes attacking enemies with Dark Attack knocking them down. -->
|
||||
|
@ -693,6 +693,34 @@
|
||||
<!-- Level 2: Presentation skill for finishing teleportation. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>2</abnormalTime>
|
||||
<abnormalType>STUN</abnormalType>
|
||||
<abnormalVisualEffect>STUN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>2000</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-5000</effectPoint>
|
||||
<effectRange>400</effectRange>
|
||||
<hitTime>300</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>120</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<trait>PULL</trait>
|
||||
<effects>
|
||||
<effect name="PullBack">
|
||||
<speed>600</speed>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34389" toLevel="1" name="World End">
|
||||
<!-- Mental Attack deals great and even fatal damage to nearby enemies and forces them to dance. -->
|
||||
@ -713,6 +741,20 @@
|
||||
<!-- Presentation Skill. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34393" toLevel="1" name="Tear to Shreds">
|
||||
<!-- Dark energy strikes the target knocking them down. -->
|
||||
|
@ -140,4 +140,24 @@
|
||||
<node X="-87121" Y="211950" />
|
||||
<node X="-82180" Y="211846" />
|
||||
</zone>
|
||||
<zone name="Eigis_Seat_Zone" type="ArenaZone" shape="NPoly" minZ="-4000" maxZ="4900">
|
||||
<node X="-20955" Y="-222973" />
|
||||
<node X="-23272" Y="-223768" />
|
||||
<node X="-24520" Y="-224200" />
|
||||
<node X="-24968" Y="-223352" />
|
||||
<node X="-25512" Y="-222264" />
|
||||
<node X="-25976" Y="-221288" />
|
||||
<node X="-26035" Y="-218613" />
|
||||
<node X="-26020" Y="-218313" />
|
||||
<node X="-25646" Y="-217960" />
|
||||
<node X="-23748" Y="-219030" />
|
||||
<node X="-22744" Y="-219512" />
|
||||
<node X="-22616" Y="-219704" />
|
||||
<node X="-21659" Y="-220231" />
|
||||
<node X="-21414" Y="-220124" />
|
||||
<node X="-21336" Y="-220360" />
|
||||
<node X="-21624" Y="-220984" />
|
||||
<node X="-21208" Y="-221752" />
|
||||
<node X="-20872" Y="-222712" />
|
||||
</zone>
|
||||
</list>
|
@ -20,59 +20,98 @@
|
||||
*/
|
||||
package ai.bosses.Eigis;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.l2jmobius.commons.threads.ThreadPool;
|
||||
import org.l2jmobius.commons.util.Rnd;
|
||||
import org.l2jmobius.gameserver.data.SpawnTable;
|
||||
import org.l2jmobius.gameserver.data.xml.NpcData;
|
||||
import org.l2jmobius.gameserver.data.xml.SkillData;
|
||||
import org.l2jmobius.gameserver.enums.SkillFinishType;
|
||||
import org.l2jmobius.gameserver.instancemanager.DBSpawnManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
|
||||
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
|
||||
import org.l2jmobius.gameserver.model.Location;
|
||||
import org.l2jmobius.gameserver.model.Spawn;
|
||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||
import org.l2jmobius.gameserver.model.actor.Npc;
|
||||
import org.l2jmobius.gameserver.model.actor.Player;
|
||||
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
||||
import org.l2jmobius.gameserver.model.holders.SkillHolder;
|
||||
import org.l2jmobius.gameserver.model.skill.Skill;
|
||||
import org.l2jmobius.gameserver.model.zone.ZoneType;
|
||||
import org.l2jmobius.gameserver.model.zone.type.ArenaZone;
|
||||
import org.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;
|
||||
|
||||
import ai.AbstractNpcAI;
|
||||
|
||||
/**
|
||||
* @author NasSeKa
|
||||
* @author Notorion
|
||||
*/
|
||||
public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
// NPC
|
||||
// NPCs
|
||||
private static final int EIGIS = 29385;
|
||||
// Misc
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504);
|
||||
private static final Location EIGIS_LOCATION = new Location(-23172, -222237, -3504, 0);
|
||||
private static final String EIGIS_ALIVE_VAR = "EIGIS_ALIVE";
|
||||
private static final int INVISIBLE_NPC = 18919;
|
||||
private static final int INVISIBLE_NPC_2 = 18920;
|
||||
// Skills
|
||||
private static final SkillHolder COMMON_SKILL = new SkillHolder(34108, 1); // Blade of Souls
|
||||
private static final SkillHolder SPECIAL_AOE_1 = new SkillHolder(34113, 1); // Dark Wave Spray
|
||||
private static final SkillHolder SPECIAL_AOE_VISUAL_2 = new SkillHolder(34114, 1); // Gate of Thousand Flashes
|
||||
private static final SkillHolder JUMP_TARGET_VISUAL_1 = new SkillHolder(34116, 1); // Dark Shadow Clash
|
||||
private static final SkillHolder JUMP_SKILL_DAMAGE_2 = new SkillHolder(34109, 1); // Blade Temptation
|
||||
private static final SkillHolder JUMP_IMPACT_VISUAL_3 = new SkillHolder(34110, 1); // Price for Temptation
|
||||
private static final SkillHolder NPC_AOE_SKILL = new SkillHolder(34115, 1); // Tear to Shreds
|
||||
private static final int NPC_LIFETIME = 6000;
|
||||
// Zone
|
||||
private static final ArenaZone EIGIS_ZONE = ZoneManager.getInstance().getZoneByName("Eigis_Seat_Zone", ArenaZone.class);
|
||||
// Barrier
|
||||
private static final int BARRIER_DURATION_MILLIS = 600000; // 10 minutes
|
||||
private static final int HIT_COUNT = 2000; // 2000 Number of attacks needed to destroy the barrier
|
||||
private static final int HIT_COUNT_RENEW = 500; // 500 hits in 60 seconds to continue without the barrier
|
||||
private static final int RENEW_DURATION_MILLIS = 600000; // 60 seconds of vulnerability
|
||||
private static final SkillHolder LIMIT_BARRIER = new SkillHolder(29515, 1);
|
||||
private boolean _barrierActivated = false;
|
||||
// Misc
|
||||
private boolean _specialActivated = false;
|
||||
private final AtomicBoolean _isUsingSpecialSkill = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill2 = new AtomicBoolean(false);
|
||||
private final AtomicBoolean _isUsingSpecialSkill3 = new AtomicBoolean(false);
|
||||
private boolean _bossInCombat = false;
|
||||
private final AtomicInteger _targetLossCount = new AtomicInteger(0);
|
||||
private boolean _vulnerablePhase = false;
|
||||
private final Map<Npc, Integer> _eigisHits = new ConcurrentHashMap<>();
|
||||
|
||||
public Eigis()
|
||||
{
|
||||
addAttackId(EIGIS);
|
||||
addSpawnId(EIGIS);
|
||||
addKillId(EIGIS);
|
||||
addAggroRangeEnterId(EIGIS);
|
||||
addExitZoneId(EIGIS_ZONE.getId());
|
||||
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final Calendar calendarEigisStart = Calendar.getInstance();
|
||||
final Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
Calendar calendarEigisStart = Calendar.getInstance();
|
||||
Calendar calendarEigisSeal = Calendar.getInstance();
|
||||
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, 1); // Sunday
|
||||
calendarEigisStart.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
calendarEigisStart.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendarEigisStart.set(Calendar.MINUTE, 0);
|
||||
calendarEigisStart.set(Calendar.SECOND, 0);
|
||||
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, 2); // Monday
|
||||
calendarEigisSeal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
calendarEigisSeal.set(Calendar.HOUR_OF_DAY, 2);
|
||||
calendarEigisSeal.set(Calendar.MINUTE, 0);
|
||||
calendarEigisSeal.set(Calendar.SECOND, 0);
|
||||
|
||||
if (((currentTime > calendarEigisStart.getTimeInMillis()) && (currentTime < calendarEigisSeal.getTimeInMillis())) && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null) && GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, true))
|
||||
{
|
||||
spawnEigis();
|
||||
}
|
||||
else
|
||||
{
|
||||
despawnEigis();
|
||||
}
|
||||
|
||||
if (calendarEigisStart.getTimeInMillis() < currentTime)
|
||||
{
|
||||
calendarEigisStart.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
@ -82,32 +121,36 @@ public class Eigis extends AbstractNpcAI
|
||||
calendarEigisSeal.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
boolean bossAlive = GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false);
|
||||
if (bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
spawnEigis();
|
||||
}, calendarEigisStart.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(() ->
|
||||
}
|
||||
else if (!bossAlive && (SpawnTable.getInstance().getAnySpawn(EIGIS) != null))
|
||||
{
|
||||
despawnEigis();
|
||||
}, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000); // 7 days
|
||||
}
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::spawnEigis, calendarEigisStart.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
|
||||
ThreadPool.scheduleAtFixedRate(this::despawnEigis, calendarEigisSeal.getTimeInMillis() - currentTime, 604800000L); // 7 days
|
||||
}
|
||||
|
||||
private void spawnEigis()
|
||||
{
|
||||
try
|
||||
if (!GlobalVariablesManager.getInstance().getBoolean(EIGIS_ALIVE_VAR, false) || (SpawnTable.getInstance().getAnySpawn(EIGIS) == null))
|
||||
{
|
||||
final NpcTemplate template = NpcData.getInstance().getTemplate(EIGIS);
|
||||
final Spawn spawn = new Spawn(template);
|
||||
spawn.setXYZ(EIGIS_LOCATION);
|
||||
spawn.setHeading(0);
|
||||
final Npc npc = addSpawn(EIGIS, EIGIS_LOCATION);
|
||||
npc.setRandomWalking(false);
|
||||
npc.setRandomAnimation(false);
|
||||
|
||||
final Spawn spawn = npc.getSpawn();
|
||||
spawn.setRespawnDelay(0);
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, false);
|
||||
spawn.startRespawn();
|
||||
DBSpawnManager.getInstance().addNewSpawn(spawn, true);
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// LOGGER.info("Eigis spawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,17 +162,622 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
npc.deleteMe();
|
||||
}
|
||||
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
// LOGGER.info("Eigis has been despawned.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onSpawn(Npc npc)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkCombatStatus", 1000, npc, null, true);
|
||||
startQuestTimer("checkPosition", 5000, npc, null, true);
|
||||
return super.onSpawn(npc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAggroRangeEnter(Npc npc, Player player, boolean isSummon)
|
||||
{
|
||||
_bossInCombat = true;
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
checkCombatStatus(npc);
|
||||
if (_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
return super.onAggroRangeEnter(npc, player, isSummon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onExitZone(Creature creature, ZoneType zone)
|
||||
{
|
||||
if ((creature instanceof Npc) && (creature.getId() == EIGIS))
|
||||
{
|
||||
final Npc npc = creature.asNpc();
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
cancelSpecialSkills(npc);
|
||||
}
|
||||
return super.onExitZone(creature, zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onAttack(Npc npc, Player attacker, int damage, boolean isSummon, Skill skill)
|
||||
{
|
||||
if (!_barrierActivated)
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
|
||||
if (_vulnerablePhase)
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT_RENEW)
|
||||
{
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int hits = _eigisHits.getOrDefault(npc, 0) + 1;
|
||||
_eigisHits.put(npc, hits);
|
||||
|
||||
if (hits >= HIT_COUNT)
|
||||
{
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
_vulnerablePhase = true;
|
||||
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_bossInCombat = true;
|
||||
if (!_specialActivated && (npc.getCurrentHp() < (npc.getMaxHp() * 0.99)))
|
||||
{
|
||||
_specialActivated = true;
|
||||
activateSpecialMechanics(npc);
|
||||
}
|
||||
|
||||
return super.onAttack(npc, attacker, damage, isSummon, skill);
|
||||
}
|
||||
|
||||
private void activateSpecialMechanics(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
startQuestTimer("SkillsBalancer", 2000, npc, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onEvent(String event, Npc npc, Player player)
|
||||
{
|
||||
if ((npc != null) && (npc.getId() == EIGIS))
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case "activate_barrier":
|
||||
{
|
||||
_barrierActivated = true;
|
||||
LIMIT_BARRIER.getSkill().applyEffects(npc, npc);
|
||||
npc.setInvul(true);
|
||||
_vulnerablePhase = false;
|
||||
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "remove_barrier":
|
||||
{
|
||||
_barrierActivated = false;
|
||||
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
|
||||
npc.setInvul(false);
|
||||
_eigisHits.put(npc, 0);
|
||||
break;
|
||||
}
|
||||
case "EigisSkills1":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills2":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EigisSkills3":
|
||||
{
|
||||
if (_bossInCombat && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get())
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "SkillsBalancer":
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
if (!npc.isDead() && !_isUsingSpecialSkill.get() && !_isUsingSpecialSkill2.get() && !_isUsingSpecialSkill3.get())
|
||||
{
|
||||
boolean hasValidTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target instanceof Player) && !target.isDead() && npc.isInsideRadius3D(target, 2000))
|
||||
{
|
||||
hasValidTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidTarget)
|
||||
{
|
||||
final int chance = getRandom(100);
|
||||
if (chance < 33)
|
||||
{
|
||||
useEigisSkills1(npc);
|
||||
}
|
||||
else if (chance < 66)
|
||||
{
|
||||
useEigisSkills2(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
useEigisSkills3(npc);
|
||||
}
|
||||
startQuestTimer("SkillsBalancer", getRandom(10000, 35000), npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
resetSkillFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkTargetRange":
|
||||
{
|
||||
if ((player != null) && !player.isDead())
|
||||
{
|
||||
final double distance = player.calculateDistance3D(npc);
|
||||
if ((distance < 700) || (distance > 2000))
|
||||
{
|
||||
_targetLossCount.incrementAndGet();
|
||||
if (_targetLossCount.get() >= 2)
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomicReference<Player> newTarget = new AtomicReference<>();
|
||||
if (findTargetPlayer(npc, newTarget))
|
||||
{
|
||||
player = newTarget.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "checkCombatStatus":
|
||||
{
|
||||
checkCombatStatus(npc);
|
||||
break;
|
||||
}
|
||||
case "checkTargetLost":
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
break;
|
||||
}
|
||||
case "checkPosition":
|
||||
{
|
||||
if (!EIGIS_ZONE.isInsideZone(npc))
|
||||
{
|
||||
npc.teleToLocation(EIGIS_LOCATION);
|
||||
npc.setTarget(null);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onEvent(event, npc, player);
|
||||
}
|
||||
|
||||
private void useEigisSkills1(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
final boolean useComeToMe = Rnd.get(100) < 48;
|
||||
if (useComeToMe)
|
||||
{
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.doCast(SPECIAL_AOE_1.getSkill());
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat && _isUsingSpecialSkill.get())
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, npc, SPECIAL_AOE_VISUAL_2.getSkillId(), 1, 0, 0));
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Presentation skill Lv.1 Teleport (34388)
|
||||
private void useComeToMeSkill(Npc npc)
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34388, 1));
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private void cancelDebuffs(Npc npc)
|
||||
{
|
||||
if ((npc == null) || npc.isDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.getEffectList().getEffects().forEach(effect ->
|
||||
{
|
||||
if (effect.getSkill().isDebuff())
|
||||
{
|
||||
npc.getEffectList().stopSkillEffects(SkillFinishType.REMOVED, effect.getSkill());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void useEigisSkills2(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill2.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 7000);
|
||||
cancelDebuffs(npc);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (Rnd.get(100) < 45)
|
||||
{
|
||||
useComeToMeSkill(npc);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Presentation Skill Lv.1 Tear to Shreds (34392)
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
npc.doCast(SkillData.getInstance().getSkill(34392, 1));
|
||||
}, 3000);
|
||||
|
||||
if (npc.getAttackByList().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EIGIS_ZONE == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final Location bossLocation = npc.getLocation();
|
||||
final List<Npc> spawnedNpcs = new ArrayList<>();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
int x = bossLocation.getX() + (int) ((i - 2.5) * 500);
|
||||
int y = bossLocation.getY() + (int) ((j - 2.5) * 500);
|
||||
Location spawnLoc = new Location(x, y, bossLocation.getZ());
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final Npc newNpc = addSpawn(INVISIBLE_NPC, spawnLoc, false, NPC_LIFETIME);
|
||||
newNpc.setHeading(0);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
final List<Npc> spawnedNpcsCopy = new ArrayList<>(spawnedNpcs);
|
||||
for (Npc npcYellow : spawnedNpcsCopy)
|
||||
{
|
||||
final Location loc = npcYellow.getLocation();
|
||||
final int npcHeading = getRandom(0, 65535);
|
||||
final Location spawnLoc = calculatePointFromLocation(loc.getX(), loc.getY(), loc.getZ(), 100, npcHeading);
|
||||
|
||||
if (!EIGIS_ZONE.isInsideZone(spawnLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Npc newNpc = addSpawn(INVISIBLE_NPC_2, spawnLoc, false, NPC_LIFETIME);
|
||||
// newNpc.setName("Swords Eigis");
|
||||
spawnedNpcs.add(newNpc);
|
||||
newNpc.setInvul(true);
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.setHeading(npcHeading);
|
||||
newNpc.teleToLocation(newNpc.getX(), newNpc.getY(), newNpc.getZ(), npcHeading);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!newNpc.isDead())
|
||||
{
|
||||
newNpc.doCast(NPC_AOE_SKILL.getSkill());
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
resetSkillFlags();
|
||||
}, 4000);
|
||||
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private Location calculatePointFromLocation(int x, int y, int z, int distance, int heading)
|
||||
{
|
||||
final double angle = Math.toRadians(heading * 0.0054931640625);
|
||||
final int newX = x + (int) (distance * Math.cos(angle));
|
||||
final int newY = y + (int) (distance * Math.sin(angle));
|
||||
return new Location(newX, newY, z);
|
||||
}
|
||||
|
||||
private void useEigisSkills3(Npc npc)
|
||||
{
|
||||
if (!_bossInCombat || !_isUsingSpecialSkill3.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
npc.disableSkill(COMMON_SKILL.getSkill(), 8000);
|
||||
final AtomicReference<Player> targetPlayer = new AtomicReference<>();
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!findTargetPlayer(npc, targetPlayer))
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
return;
|
||||
}
|
||||
|
||||
final Player target = targetPlayer.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
npc.setTarget(target);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_TARGET_VISUAL_1.getSkillId(), 1, 3000, 0));
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.setTarget(target);
|
||||
npc.doCast(JUMP_SKILL_DAMAGE_2.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!npc.isDead() && _bossInCombat)
|
||||
{
|
||||
npc.broadcastPacket(new MagicSkillUse(npc, target, JUMP_IMPACT_VISUAL_3.getSkillId(), 1, 2000, 0));
|
||||
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}, 6000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelSkill3(npc);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private boolean findTargetPlayer(Npc npc, AtomicReference<Player> targetPlayer)
|
||||
{
|
||||
final List<Player> playersInRange = new ArrayList<>();
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && (target instanceof Player))
|
||||
{
|
||||
final Player player = (Player) target;
|
||||
final double distance = npc.calculateDistance3D(player);
|
||||
if ((distance >= 700) && (distance <= 2000))
|
||||
{
|
||||
playersInRange.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!playersInRange.isEmpty())
|
||||
{
|
||||
targetPlayer.set(playersInRange.get(Rnd.get(playersInRange.size())));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resetSkillFlags()
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
}
|
||||
|
||||
private void cancelSkill3(Npc npc)
|
||||
{
|
||||
cancelQuestTimer("checkTargetRange", npc, null);
|
||||
resetSkillFlags();
|
||||
}
|
||||
|
||||
private void cancelSpecialSkills(Npc npc)
|
||||
{
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
npc.enableSkill(COMMON_SKILL.getSkill());
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
}
|
||||
|
||||
private void checkCombatStatus(Npc npc)
|
||||
{
|
||||
if (_bossInCombat)
|
||||
{
|
||||
boolean hasTarget = false;
|
||||
for (WeakReference<Creature> targetRef : npc.getAttackByList())
|
||||
{
|
||||
final Creature target = targetRef.get();
|
||||
if ((target != null) && !target.isDead())
|
||||
{
|
||||
hasTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTarget)
|
||||
{
|
||||
_bossInCombat = false;
|
||||
startQuestTimer("checkTargetLost", 8000, npc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_specialActivated)
|
||||
{
|
||||
cancelSpecialSkills(npc);
|
||||
_specialActivated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onKill(Npc npc, Player killer, boolean isSummon)
|
||||
{
|
||||
if (npc.getId() == EIGIS)
|
||||
{
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
}
|
||||
|
||||
_bossInCombat = false;
|
||||
_specialActivated = false;
|
||||
_isUsingSpecialSkill.set(false);
|
||||
_isUsingSpecialSkill2.set(false);
|
||||
_isUsingSpecialSkill3.set(false);
|
||||
GlobalVariablesManager.getInstance().set(EIGIS_ALIVE_VAR, false);
|
||||
npc.setInvul(false);
|
||||
_eigisHits.clear();
|
||||
_barrierActivated = false;
|
||||
_vulnerablePhase = false;
|
||||
cancelQuestTimer("SkillsBalancer", npc, null);
|
||||
cancelQuestTimer("checkTargetLost", npc, null);
|
||||
cancelQuestTimer("checkCombatStatus", npc, null);
|
||||
cancelQuestTimer("checkPosition", npc, null);
|
||||
cancelQuestTimer("activate_barrier", npc, null);
|
||||
cancelQuestTimer("remove_barrier", npc, null);
|
||||
return super.onKill(npc, killer, isSummon);
|
||||
}
|
||||
|
||||
@ -137,4 +785,4 @@ public class Eigis extends AbstractNpcAI
|
||||
{
|
||||
new Eigis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5596,7 +5596,6 @@
|
||||
</dropLists>
|
||||
</npc>
|
||||
<npc id="29385" level="127" type="RaidBoss" name="Eigis" title="Lady of Despair">
|
||||
<!-- AUTO GENERATED NPC TODO: FIX IT -->
|
||||
<race>UNDEAD</race>
|
||||
<sex>MALE</sex>
|
||||
<acquire exp="5337882368" sp="4804094" />
|
||||
@ -5608,13 +5607,14 @@
|
||||
<walk ground="60" />
|
||||
<run ground="180" />
|
||||
</speed>
|
||||
<hitTime>600</hitTime>
|
||||
<hitTime>950</hitTime>
|
||||
<attribute>
|
||||
<attack type="DARK" value="2550" />
|
||||
<defence fire="2650" water="2650" wind="2650" earth="2650" holy="2600" dark="2650" />
|
||||
</attribute>
|
||||
</stats>
|
||||
<status attackable="true" />
|
||||
<ai type="MAGE" clanHelpRange="2000" aggroRange="2000" />
|
||||
<collision>
|
||||
<radius normal="36" />
|
||||
<height normal="123" />
|
||||
@ -5626,6 +5626,10 @@
|
||||
<skill id="32909" level="1" /> <!-- Rule of Looting -->
|
||||
<skill id="32675" level="1" /> <!-- Improved Immunity -->
|
||||
<skill id="14804" level="10" /> <!-- Damage Reflection Resistance -->
|
||||
<skill id="34814" level="9" /> <!-- Critical Rate Resistance -->
|
||||
<skill id="34815" level="9" /> <!-- Critical Damage Resistance -->
|
||||
<skill id="34820" level="7" /> <!-- Received Damage Resistance -->
|
||||
<skill id="34108" level="1" /> <!-- Blade of Souls -->
|
||||
</skillList>
|
||||
<dropLists>
|
||||
<drop>
|
||||
|
@ -111,43 +111,115 @@
|
||||
<icon>icon.skill0000</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>SQUARE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<castRange>400</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<coolTime>600</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<effectRange>400</effectRange>
|
||||
<fanRange>0;0;500;500</fanRange>
|
||||
<hitTime>1300</hitTime>
|
||||
<magicCriticalRate>-5</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<effects>
|
||||
<effect name="MagicalDamage">
|
||||
<power>28000</power>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34109" toLevel="1" name="Blade Temptation">
|
||||
<!-- Pulls enemies around the target stunning them. -->
|
||||
<icon>icon.skill0100</icon>
|
||||
<operateType>A1</operateType>
|
||||
<operateType>DA2</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalTime>3</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<abnormalLevel>4</abnormalLevel>
|
||||
<abnormalTime>6</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>70</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>150</affectRange>
|
||||
<affectRange>500</affectRange>
|
||||
<affectScope>RANGE</affectScope>
|
||||
<basicProperty>PHYSICAL</basicProperty>
|
||||
<castRange>2000</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<coolTime>1200</coolTime>
|
||||
<effectPoint>-711</effectPoint>
|
||||
<effectRange>2000</effectRange>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>5000</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<lvlBonusRate>20</lvlBonusRate>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>127</magicLevel>
|
||||
<nextAction>ATTACK</nextAction>
|
||||
<reuseDelay>10000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>158600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>100</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>150</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34110" toLevel="1" name="Price for Temptation">
|
||||
<!-- Immediately after pulling. -->
|
||||
<icon>icon.skill10262</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>1</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>400</affectRange>
|
||||
<affectScope>RANGE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>800</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>1500</hitTime>
|
||||
<isMagic>1</isMagic>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>2000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>105</magicLevel>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>5000</reuseDelay>
|
||||
<trait>SHOCK</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>9800000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34111" toLevel="2" name="Teleport">
|
||||
<!-- Level 1: Presentation skill for starting teleportation. -->
|
||||
@ -182,16 +254,53 @@
|
||||
<icon>icon.skill3080</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-1</effectPoint>
|
||||
<hitTime>4000</hitTime>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>7</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>1100</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<castRange>1000</castRange>
|
||||
<coolTime>1100</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5500</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicCriticalRate>-5</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>10000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>158600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>150</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34114" toLevel="1" name="Gate of Thousand Flashes">
|
||||
<!-- Deals great damage to nearby enemies knocking them down. The effect cannot be removed or resisted. -->
|
||||
@ -214,32 +323,78 @@
|
||||
<icon>icon.skill33915</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>-1</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>2500</hitTime>
|
||||
<abnormalLevel>1</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<abnormalType>KNOCKDOWN</abnormalType>
|
||||
<abnormalVisualEffect>KNOCKDOWN</abnormalVisualEffect>
|
||||
<activateRate>50</activateRate>
|
||||
<activateRate>70</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>1000</affectRange>
|
||||
<affectScope>SQUARE</affectScope>
|
||||
<attributeType>DARK</attributeType>
|
||||
<attributeValue>2000</attributeValue>
|
||||
<basicProperty>PHYSICAL</basicProperty>
|
||||
<castRange>500</castRange>
|
||||
<coolTime>1200</coolTime>
|
||||
<effectPoint>-716</effectPoint>
|
||||
<effectRange>2600</effectRange>
|
||||
<fanRange>0;0;1400;250</fanRange>
|
||||
<hitCancelTime>1</hitCancelTime>
|
||||
<hitTime>2600</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<isMagic>0</isMagic>
|
||||
<magicCriticalRate>50</magicCriticalRate>
|
||||
<magicLevel>127</magicLevel>
|
||||
<reuseDelay>5000</reuseDelay>
|
||||
<trait>KNOCKDOWN</trait>
|
||||
<effects>
|
||||
<effect name="PhysicalDamage">
|
||||
<power>169600000</power>
|
||||
<overHit>true</overHit>
|
||||
<criticalChance>
|
||||
<value fromLevel="1" toLevel="9">9</value>
|
||||
<value fromLevel="1" toLevel="9" fromSubLevel="2001" toSubLevel="2020">{base + (base / 100 * subIndex)}</value>
|
||||
</criticalChance>
|
||||
</effect>
|
||||
<effect name="KnockBack">
|
||||
<speed>400</speed>
|
||||
<distance>250</distance>
|
||||
<knockDown>true</knockDown>
|
||||
</effect>
|
||||
<effect name="DefenceTrait">
|
||||
<HOLD>100</HOLD>
|
||||
<KNOCKDOWN>100</KNOCKDOWN>
|
||||
<SHOCK>100</SHOCK>
|
||||
<AIRBIND>100</AIRBIND>
|
||||
<SLEEP>100</SLEEP>
|
||||
<KNOCKBACK>100</KNOCKBACK>
|
||||
<CHANGEBODY>100</CHANGEBODY>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34116" toLevel="1" name="Dark Shadow Clash">
|
||||
<!-- Deals great damage to nearby enemies knocking them down. The more enemies in the area of attack, the more damage they receive. -->
|
||||
<icon>icon.skill1064</icon>
|
||||
<operateType>DA2</operateType>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalTime>10</abnormalTime>
|
||||
<affectScope>SINGLE</affectScope>
|
||||
<castRange>2000</castRange>
|
||||
<coolTime>1000</coolTime>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<isMagic>1</isMagic>
|
||||
<hitTime>6000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<reuseDelay>10</reuseDelay>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>8000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34117" toLevel="1" name="Dark Protection">
|
||||
<!-- Dark energy protects you. Strikes attacking enemies with Dark Attack knocking them down. -->
|
||||
|
@ -693,6 +693,34 @@
|
||||
<!-- Level 2: Presentation skill for finishing teleportation. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>SELF</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>2</abnormalTime>
|
||||
<abnormalType>STUN</abnormalType>
|
||||
<abnormalVisualEffect>STUN</abnormalVisualEffect>
|
||||
<activateRate>60</activateRate>
|
||||
<activateRate>900</activateRate>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectRange>2000</affectRange>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<basicProperty>NONE</basicProperty>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-5000</effectPoint>
|
||||
<effectRange>400</effectRange>
|
||||
<hitTime>300</hitTime>
|
||||
<isDebuff>true</isDebuff>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>120</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
<trait>PULL</trait>
|
||||
<effects>
|
||||
<effect name="PullBack">
|
||||
<speed>600</speed>
|
||||
</effect>
|
||||
<effect name="BlockActions">
|
||||
<allowedSkills>10279;10517;10025;10776;11770;1904;11264;11093;13314;1912;7002;18721;18722;28203;30516;35190</allowedSkills>
|
||||
</effect>
|
||||
</effects>
|
||||
</skill>
|
||||
<skill id="34389" toLevel="1" name="World End">
|
||||
<!-- Mental Attack deals great and even fatal damage to nearby enemies and forces them to dance. -->
|
||||
@ -713,6 +741,20 @@
|
||||
<!-- Presentation Skill. -->
|
||||
<icon>icon.skill10516</icon>
|
||||
<operateType>A1</operateType>
|
||||
<targetType>ENEMY</targetType>
|
||||
<abnormalLevel>2</abnormalLevel>
|
||||
<abnormalTime>5</abnormalTime>
|
||||
<activateRate>900</activateRate>
|
||||
<affectLimit>5-12</affectLimit>
|
||||
<affectObject>NOT_FRIEND</affectObject>
|
||||
<affectScope>POINT_BLANK</affectScope>
|
||||
<coolTime>500</coolTime>
|
||||
<effectPoint>-100</effectPoint>
|
||||
<hitTime>5000</hitTime>
|
||||
<isMagic>7</isMagic>
|
||||
<magicCriticalRate>5</magicCriticalRate>
|
||||
<magicLevel>105</magicLevel>
|
||||
<reuseDelay>1000</reuseDelay>
|
||||
</skill>
|
||||
<skill id="34393" toLevel="1" name="Tear to Shreds">
|
||||
<!-- Dark energy strikes the target knocking them down. -->
|
||||
|
@ -140,4 +140,24 @@
|
||||
<node X="-87121" Y="211950" />
|
||||
<node X="-82180" Y="211846" />
|
||||
</zone>
|
||||
<zone name="Eigis_Seat_Zone" type="ArenaZone" shape="NPoly" minZ="-4000" maxZ="4900">
|
||||
<node X="-20955" Y="-222973" />
|
||||
<node X="-23272" Y="-223768" />
|
||||
<node X="-24520" Y="-224200" />
|
||||
<node X="-24968" Y="-223352" />
|
||||
<node X="-25512" Y="-222264" />
|
||||
<node X="-25976" Y="-221288" />
|
||||
<node X="-26035" Y="-218613" />
|
||||
<node X="-26020" Y="-218313" />
|
||||
<node X="-25646" Y="-217960" />
|
||||
<node X="-23748" Y="-219030" />
|
||||
<node X="-22744" Y="-219512" />
|
||||
<node X="-22616" Y="-219704" />
|
||||
<node X="-21659" Y="-220231" />
|
||||
<node X="-21414" Y="-220124" />
|
||||
<node X="-21336" Y="-220360" />
|
||||
<node X="-21624" Y="-220984" />
|
||||
<node X="-21208" Y="-221752" />
|
||||
<node X="-20872" Y="-222712" />
|
||||
</zone>
|
||||
</list>
|
Loading…
Reference in New Issue
Block a user