Updated Eigis AI.
Contributed by notorionn.
This commit is contained in:
		| @@ -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> | ||||
		Reference in New Issue
	
	Block a user
	 MobiusDevelopment
					MobiusDevelopment