From 7d865f41ac4e9988615e3ef73a75c98f3acc7327 Mon Sep 17 00:00:00 2001
From: Maneco2 <maneco_1@hotmail.com>
Date: Wed, 3 Nov 2021 11:41:46 -0300
Subject: [PATCH] Rework Orfen AI

Minion help Orfen bellow 1/2 HP, retail like.
RandomTarget for minions is missing at:
https://www.l2jserver.com/forum/viewtopic.php?f=86&t=33133
Fixed issue #226
---
 .../datapack/ai/individual/Orfen.java         | 400 +++++++++---------
 1 file changed, 202 insertions(+), 198 deletions(-)

diff --git a/src/main/java/com/l2jserver/datapack/ai/individual/Orfen.java b/src/main/java/com/l2jserver/datapack/ai/individual/Orfen.java
index d1abc36fea..9b012ef260 100644
--- a/src/main/java/com/l2jserver/datapack/ai/individual/Orfen.java
+++ b/src/main/java/com/l2jserver/datapack/ai/individual/Orfen.java
@@ -21,273 +21,277 @@ package com.l2jserver.datapack.ai.individual;
 import static com.l2jserver.gameserver.config.Configuration.grandBoss;
 
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
 
 import com.l2jserver.datapack.ai.npc.AbstractNpcAI;
-import com.l2jserver.gameserver.ai.CtrlIntention;
 import com.l2jserver.gameserver.enums.audio.Music;
 import com.l2jserver.gameserver.instancemanager.GrandBossManager;
 import com.l2jserver.gameserver.model.L2Object;
-import com.l2jserver.gameserver.model.L2Spawn;
 import com.l2jserver.gameserver.model.Location;
 import com.l2jserver.gameserver.model.StatsSet;
 import com.l2jserver.gameserver.model.actor.L2Attackable;
 import com.l2jserver.gameserver.model.actor.L2Character;
 import com.l2jserver.gameserver.model.actor.L2Npc;
 import com.l2jserver.gameserver.model.actor.instance.L2GrandBossInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.model.holders.SkillHolder;
 import com.l2jserver.gameserver.model.skills.Skill;
+import com.l2jserver.gameserver.model.zone.L2ZoneType;
 import com.l2jserver.gameserver.model.zone.type.L2BossZone;
 import com.l2jserver.gameserver.network.NpcStringId;
 import com.l2jserver.gameserver.network.clientpackets.Say2;
-import com.l2jserver.gameserver.network.serverpackets.NpcSay;
 
 /**
- * Orfen's AI
+ * Orfen AI
  * @author Emperorc
+ * @author Maneco2
  */
 public final class Orfen extends AbstractNpcAI {
-	private static final Location[] POS = {
-		new Location(43728, 17220, -4342),
-		new Location(55024, 17368, -5412),
-		new Location(53504, 21248, -5486),
-		new Location(53248, 24576, -5262)
-	};
-	
-	private static final NpcStringId[] TEXT = {
-		NpcStringId.S1_STOP_KIDDING_YOURSELF_ABOUT_YOUR_OWN_POWERLESSNESS,
-		NpcStringId.S1_ILL_MAKE_YOU_FEEL_WHAT_TRUE_FEAR_IS,
-		NpcStringId.YOURE_REALLY_STUPID_TO_HAVE_CHALLENGED_ME_S1_GET_READY,
-		NpcStringId.S1_DO_YOU_THINK_THATS_GOING_TO_WORK
-	};
-	
+	// NPCs
 	private static final int ORFEN = 29014;
-	// private static final int RAIKEL = 29015;
+	private static final int RAIKEL = 29015;
 	private static final int RAIKEL_LEOS = 29016;
-	// private static final int RIBA = 29017;
 	private static final int RIBA_IREN = 29018;
-	
-	private static boolean _IsTeleported;
-	private static final List<L2Attackable> MINIONS = new CopyOnWriteArrayList<>();
-	private static L2BossZone ZONE;
-	
-	private static final byte ALIVE = 0;
-	private static final byte DEAD = 1;
-	
+	// Orfen Location
+	private static final Location ORFEN_LOC_1 = new Location(43728, 17220, -4342, 0);
+	private static final Location ORFEN_LOC_2 = new Location(55024, 17368, -5412, 0);
+	private static final Location ORFEN_LOC_3 = new Location(53504, 21248, -5486, 0);
+	private static final Location ORFEN_LOC_4 = new Location(53248, 24576, -5262, 0);
+	// Zone
+	private static final int ZONE = 12013;
 	// Skills
-	private static final SkillHolder PARALYSIS = new SkillHolder(4064);
-	private static final SkillHolder NPC_MORTAL_BLOW = new SkillHolder(4067, 4);
-	private static final SkillHolder ORFEN_HEAL = new SkillHolder(4516);
+	private static final SkillHolder ORFEN_STRIKE = new SkillHolder(4063);
+	private static final SkillHolder ORFEN_DISPEL = new SkillHolder(4064);
+	private static final SkillHolder S_NPC_BLOW4 = new SkillHolder(4067, 4);
+	private static final SkillHolder S_ORFEN_HEAL = new SkillHolder(4516);
+	// Variables
+	private boolean isAttacked = false;
+	private boolean isDefensive = false;
+	private static L2BossZone _zone;
+	private L2GrandBossInstance _orfen = null;
+	private L2Npc _riba = null;
+	// Status
+	private static final int ALIVE = 0;
+	private static final int DEAD = 1;
 	
 	public Orfen() {
 		super(Orfen.class.getSimpleName(), "ai/individual");
-		int[] mobs = {
-			ORFEN,
-			RAIKEL_LEOS,
-			RIBA_IREN
-		};
-		registerMobs(mobs);
-		_IsTeleported = false;
-		ZONE = GrandBossManager.getInstance().getZone(POS[0]);
+		addKillId(ORFEN);
+		addSpawnId(ORFEN, RIBA_IREN);
+		addExitZoneId(ZONE);
+		addSkillSeeId(ORFEN);
+		addTeleportId(ORFEN);
+		addAttackId(ORFEN, RAIKEL_LEOS, RAIKEL, RIBA_IREN);
+		_zone = GrandBossManager.getInstance().getZone(43728, 17220, -4342);
 		StatsSet info = GrandBossManager.getInstance().getStatsSet(ORFEN);
-		int status = GrandBossManager.getInstance().getBossStatus(ORFEN);
-		if (status == DEAD) {
-			// load the unlock date and time for Orfen from DB
-			long temp = info.getLong("respawn_time") - System.currentTimeMillis();
-			// if Orfen is locked until a certain time, mark it so and start the unlock timer
-			// the unlock time has not yet expired.
-			if (temp > 0) {
-				startQuestTimer("orfen_unlock", temp, null, null);
+		if (GrandBossManager.getInstance().getBossStatus(ORFEN) == DEAD) {
+			final long remain = info.getLong("respawn_time") - System.currentTimeMillis();
+			if (remain > 0) {
+				startQuestTimer("ORFEN_UNLOCK", remain, null, null);
 			} else {
-				// the time has already expired while the server was offline. Immediately spawn Orfen.
-				int i = getRandom(10);
-				Location loc;
-				if (i < 4) {
-					loc = POS[1];
-				} else if (i < 7) {
-					loc = POS[2];
-				} else {
-					loc = POS[3];
-				}
-				L2GrandBossInstance orfen = (L2GrandBossInstance) addSpawn(ORFEN, loc, false, 0);
-				GrandBossManager.getInstance().setBossStatus(ORFEN, ALIVE);
-				spawnBoss(orfen);
+				notifyEvent("ORFEN_UNLOCK", null, null);
 			}
 		} else {
-			int loc_x = info.getInt("loc_x");
-			int loc_y = info.getInt("loc_y");
-			int loc_z = info.getInt("loc_z");
-			int heading = info.getInt("heading");
-			int hp = info.getInt("currentHP");
-			int mp = info.getInt("currentMP");
-			L2GrandBossInstance orfen = (L2GrandBossInstance) addSpawn(ORFEN, loc_x, loc_y, loc_z, heading, false, 0);
-			orfen.setCurrentHpMp(hp, mp);
-			spawnBoss(orfen);
+			notifyEvent("ORFEN_UNLOCK", null, null);
 		}
 	}
 	
-	public void setSpawnPoint(L2Npc npc, int index) {
-		((L2Attackable) npc).clearAggroList();
-		npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null, null);
-		L2Spawn spawn = npc.getSpawn();
-		spawn.setLocation(POS[index]);
-		npc.teleToLocation(POS[index], false);
-	}
-	
-	public void spawnBoss(L2GrandBossInstance npc) {
-		GrandBossManager.getInstance().addBoss(npc);
-		npc.broadcastPacket(Music.BS01_A_7000.getPacket());
-		startQuestTimer("check_orfen_pos", 10000, npc, null, true);
-		// Spawn minions
-		int x = npc.getX();
-		int y = npc.getY();
-		L2Attackable mob;
-		mob = (L2Attackable) addSpawn(RAIKEL_LEOS, x + 100, y + 100, npc.getZ(), 0, false, 0);
-		mob.setIsRaidMinion(true);
-		MINIONS.add(mob);
-		mob = (L2Attackable) addSpawn(RAIKEL_LEOS, x + 100, y - 100, npc.getZ(), 0, false, 0);
-		mob.setIsRaidMinion(true);
-		MINIONS.add(mob);
-		mob = (L2Attackable) addSpawn(RAIKEL_LEOS, x - 100, y + 100, npc.getZ(), 0, false, 0);
-		mob.setIsRaidMinion(true);
-		MINIONS.add(mob);
-		mob = (L2Attackable) addSpawn(RAIKEL_LEOS, x - 100, y - 100, npc.getZ(), 0, false, 0);
-		mob.setIsRaidMinion(true);
-		MINIONS.add(mob);
-		startQuestTimer("check_minion_loc", 10000, npc, null, true);
-	}
-	
 	@Override
 	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player) {
-		if (event.equalsIgnoreCase("orfen_unlock")) {
-			int i = getRandom(10);
-			Location loc;
-			if (i < 4) {
-				loc = POS[1];
-			} else if (i < 7) {
-				loc = POS[2];
-			} else {
-				loc = POS[3];
-			}
-			L2GrandBossInstance orfen = (L2GrandBossInstance) addSpawn(ORFEN, loc, false, 0);
-			GrandBossManager.getInstance().setBossStatus(ORFEN, ALIVE);
-			spawnBoss(orfen);
-		} else if (event.equalsIgnoreCase("check_orfen_pos")) {
-			if ((_IsTeleported && (npc.getCurrentHp() > (npc.getMaxHp() * 0.95))) || (!ZONE.isInsideZone(npc) && !_IsTeleported)) {
-				setSpawnPoint(npc, getRandom(3) + 1);
-				_IsTeleported = false;
-			} else if (_IsTeleported && !ZONE.isInsideZone(npc)) {
-				setSpawnPoint(npc, 0);
-			}
-		} else if (event.equalsIgnoreCase("check_minion_loc")) {
-			for (L2Attackable mob : MINIONS) {
-				if (!npc.isInsideRadius(mob, 3000, false, false)) {
-					mob.teleToLocation(npc.getLocation());
+		switch (event) {
+			case "CORE_AI": {
+				if (npc != null) {
 					((L2Attackable) npc).clearAggroList();
-					npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null, null);
+					npc.disableCoreAI(false);
 				}
+				break;
 			}
-		} else if (event.equalsIgnoreCase("despawn_minions")) {
-			for (L2Attackable mob : MINIONS) {
-				if (mob != null) {
-					mob.decayMe();
+			case "ORFEN_UNLOCK": {
+				StatsSet info = GrandBossManager.getInstance().getStatsSet(ORFEN);
+				int hp = info.getInt("currentHP");
+				int mp = info.getInt("currentMP");
+				if (getRandom(100) < 40) {
+					_orfen = (L2GrandBossInstance) addSpawn(ORFEN, 55024, 17368, -5412, 0, false, 0);
+				} else if (getRandom(100) < 30) {
+					_orfen = (L2GrandBossInstance) addSpawn(ORFEN, 53504, 21248, -5486, 0, false, 0);
+				} else {
+					_orfen = (L2GrandBossInstance) addSpawn(ORFEN, 53248, 24576, -5262, 0, false, 0);
+				}
+				_orfen.setCurrentHpMp(hp, mp);
+				
+				if (GrandBossManager.getInstance().getBossStatus(ORFEN) == DEAD) {
+					GrandBossManager.getInstance().setBossStatus(ORFEN, ALIVE);
 				}
+				spawnBoss(_orfen);
+				break;
+			}
+			case "CHECK_ZONE": {
+				if (npc != null) {
+					if (npc.getCurrentHp() > (npc.getMaxHp() * 0.95)) {
+						if ((isDefensive) && (!isAttacked)) {
+							isAttacked = true;
+							npc.disableCoreAI(true);
+							if (getRandom(100) < 33) {
+								npc.teleToLocation(ORFEN_LOC_2);
+								npc.getSpawn().setLocation(ORFEN_LOC_2);
+							} else if (getRandom(100) < 66) {
+								npc.teleToLocation(ORFEN_LOC_3);
+								npc.getSpawn().setLocation(ORFEN_LOC_3);
+							} else {
+								npc.teleToLocation(ORFEN_LOC_4);
+								npc.getSpawn().setLocation(ORFEN_LOC_4);
+							}
+							
+							if (isDefensive) {
+								isDefensive = false;
+							}
+						}
+					} else {
+						if ((npc.getCurrentHp()) < (npc.getMaxHp() / 2)) {
+							if (!isDefensive) {
+								isDefensive = true;
+								npc.disableCoreAI(true);
+								npc.teleToLocation(ORFEN_LOC_1);
+								npc.getSpawn().setLocation(ORFEN_LOC_1);
+								if (isAttacked) {
+									isAttacked = false;
+								}
+							}
+						}
+					}
+				}
+				break;
 			}
-			MINIONS.clear();
-		} else if (event.equalsIgnoreCase("spawn_minion")) {
-			L2Attackable mob = (L2Attackable) addSpawn(RAIKEL_LEOS, npc.getX(), npc.getY(), npc.getZ(), 0, false, 0);
-			mob.setIsRaidMinion(true);
-			MINIONS.add(mob);
 		}
 		return super.onAdvEvent(event, npc, player);
 	}
 	
 	@Override
-	public String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, List<L2Object> targets, boolean isSummon) {
-		if (npc.getId() == ORFEN) {
-			L2Character originalCaster = isSummon ? caster.getSummon() : caster;
-			if ((skill.getEffectPoint() > 0) && (getRandom(5) == 0) && npc.isInsideRadius(originalCaster, 1000, false, false)) {
-				NpcSay packet = new NpcSay(npc.getObjectId(), Say2.NPC_ALL, npc.getId(), TEXT[getRandom(4)]);
-				packet.addStringParameter(caster.getName().toString());
-				npc.broadcastPacket(packet);
-				originalCaster.teleToLocation(npc.getLocation());
-				npc.setTarget(originalCaster);
-				npc.doCast(PARALYSIS);
+	public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon) {
+		if ((npc != null) && (!npc.isCastingNow()) && (!npc.isCoreAIDisabled())) {
+			switch (npc.getId()) {
+				case ORFEN: {
+					if ((npc.getCurrentHp() - damage) < (npc.getMaxHp() / 2)) {
+						if (!isDefensive) {
+							isDefensive = true;
+							npc.disableCoreAI(true);
+							npc.teleToLocation(ORFEN_LOC_1);
+							npc.getSpawn().setLocation(ORFEN_LOC_1);
+							if (isAttacked) {
+								isAttacked = false;
+							}
+						}
+						if (getRandom(100) < 90) {
+							_riba.setTarget(attacker);
+							_riba.doCast(S_ORFEN_HEAL);
+						} else if (getRandom(100) < 10) {
+							_riba.setTarget(attacker);
+							_riba.doCast(S_ORFEN_HEAL);
+						}
+					}
+					if ((npc.calculateDistance(attacker, false, false) > 300) && (npc.calculateDistance(attacker, false, false) < 1000) && (getRandom(100) < 10)) {
+						if (getRandom(100) < 33) {
+							broadcastNpcSay(npc, Say2.ALL, NpcStringId.S1_STOP_KIDDING_YOURSELF_ABOUT_YOUR_OWN_POWERLESSNESS);
+						} else if (getRandom(100) < 66) {
+							broadcastNpcSay(npc, Say2.ALL, NpcStringId.S1_ILL_MAKE_YOU_FEEL_WHAT_TRUE_FEAR_IS);
+						} else {
+							broadcastNpcSay(npc, Say2.ALL, NpcStringId.YOURE_REALLY_STUPID_TO_HAVE_CHALLENGED_ME_S1_GET_READY);
+						}
+						
+						attacker.teleToLocation(npc.getLocation());
+						addSkillCastDesire(npc, attacker, ORFEN_STRIKE, 1000000L);
+					} else if (getRandom(100) < 20) {
+						addSkillCastDesire(npc, attacker, ORFEN_DISPEL, 1000000L);
+					}
+				}
+				case RAIKEL_LEOS: {
+					if (getRandom(100) < 5) {
+						addSkillCastDesire(npc, attacker, S_NPC_BLOW4, 1000000L);
+					}
+				}
+				case RAIKEL:
+				case RIBA_IREN: {
+					if ((npc.getCurrentHp() - damage) < (npc.getMaxHp() / 2)) {
+						npc.setTarget(npc);
+						npc.doCast(S_ORFEN_HEAL);
+					}
+				}
 			}
 		}
-		return super.onSkillSee(npc, caster, skill, targets, isSummon);
+		return super.onAttack(npc, attacker, damage, isSummon);
 	}
 	
 	@Override
-	public String onFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isSummon) {
-		if ((caller == null) || (npc == null) || npc.isCastingNow()) {
-			return super.onFactionCall(npc, caller, attacker, isSummon);
-		}
-		int npcId = npc.getId();
-		int callerId = caller.getId();
-		if ((npcId == RAIKEL_LEOS) && (getRandom(20) == 0)) {
-			npc.setTarget(attacker);
-			npc.doCast(NPC_MORTAL_BLOW);
-		} else if (npcId == RIBA_IREN) {
-			int chance = 1;
-			if (callerId == ORFEN) {
-				chance = 9;
-			}
-			if ((callerId != RIBA_IREN) && (caller.getCurrentHp() < (caller.getMaxHp() / 2.0)) && (getRandom(10) < chance)) {
-				npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null, null);
-				npc.setTarget(caller);
-				npc.doCast(ORFEN_HEAL);
+	public String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, List<L2Object> targets, boolean isSummon) {
+		if (npc.getId() == ORFEN) {
+			final L2Character originalCaster = isSummon ? caster.getSummon() : caster;
+			if ((skill.getEffectPoint() > 0) && (getRandom(100) < 20) && (npc.calculateDistance(originalCaster, false, false) < 1000)) {
+				if (getRandom(100) < 25) {
+					broadcastNpcSay(npc, Say2.ALL, NpcStringId.S1_STOP_KIDDING_YOURSELF_ABOUT_YOUR_OWN_POWERLESSNESS);
+				} else if (getRandom(100) < 50) {
+					broadcastNpcSay(npc, Say2.ALL, NpcStringId.S1_ILL_MAKE_YOU_FEEL_WHAT_TRUE_FEAR_IS);
+				} else if (getRandom(100) < 75) {
+					broadcastNpcSay(npc, Say2.ALL, NpcStringId.YOURE_REALLY_STUPID_TO_HAVE_CHALLENGED_ME_S1_GET_READY);
+				} else {
+					broadcastNpcSay(npc, Say2.ALL, NpcStringId.S1_DO_YOU_THINK_THATS_GOING_TO_WORK);
+				}
+				
+				originalCaster.teleToLocation(npc.getLocation());
+				addSkillCastDesire(npc, caster, ORFEN_DISPEL, 1000000L);
 			}
 		}
-		return super.onFactionCall(npc, caller, attacker, isSummon);
+		return super.onSkillSee(npc, caster, skill, targets, isSummon);
 	}
 	
 	@Override
-	public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon) {
-		int npcId = npc.getId();
-		if (npcId == ORFEN) {
-			if (!_IsTeleported && ((npc.getCurrentHp() - damage) < (npc.getMaxHp() / 2))) {
-				_IsTeleported = true;
-				setSpawnPoint(npc, 0);
-			} else if (npc.isInsideRadius(attacker, 1000, false, false) && !npc.isInsideRadius(attacker, 300, false, false) && (getRandom(10) == 0)) {
-				NpcSay packet = new NpcSay(npc.getObjectId(), Say2.NPC_ALL, npcId, TEXT[getRandom(3)]);
-				packet.addStringParameter(attacker.getName().toString());
-				npc.broadcastPacket(packet);
-				attacker.teleToLocation(npc.getLocation());
-				npc.setTarget(attacker);
-				npc.doCast(PARALYSIS);
+	public String onSpawn(L2Npc npc) {
+		switch (npc.getId()) {
+			case ORFEN: {
+				notifyEvent("CHECK_ZONE", npc, null);
+				startQuestTimer("CHECK_ZONE", 10000, npc, null, true);
 			}
-		} else if (npcId == RIBA_IREN) {
-			if (!npc.isCastingNow() && ((npc.getCurrentHp() - damage) < (npc.getMaxHp() / 2.0))) {
-				npc.setTarget(attacker);
-				npc.doCast(ORFEN_HEAL);
+			case RIBA_IREN: {
+				_riba = npc;
+				break;
 			}
 		}
-		return super.onAttack(npc, attacker, damage, isSummon);
+		
+		return super.onSpawn(npc);
 	}
 	
 	@Override
 	public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon) {
 		if (npc.getId() == ORFEN) {
+			cancelQuestTimers("CHECK_ZONE");
 			npc.broadcastPacket(Music.BS02_D_7000.getPacket());
 			GrandBossManager.getInstance().setBossStatus(ORFEN, DEAD);
-			// Calculate Min and Max respawn times randomly.
-			long respawnTime = grandBoss().getIntervalOfOrfenSpawn() + getRandom(-grandBoss().getRandomOfOrfenSpawn(), grandBoss().getRandomOfOrfenSpawn());
-			respawnTime *= 3600000;
-			startQuestTimer("orfen_unlock", respawnTime, null, null);
-			// also save the respawn time so that the info is maintained past reboots
-			StatsSet info = GrandBossManager.getInstance().getStatsSet(ORFEN);
-			info.set("respawn_time", System.currentTimeMillis() + respawnTime);
-			GrandBossManager.getInstance().setStatsSet(ORFEN, info);
-			cancelQuestTimer("check_minion_loc", npc, null);
-			cancelQuestTimer("check_orfen_pos", npc, null);
-			startQuestTimer("despawn_minions", 20000, null, null);
-			cancelQuestTimers("spawn_minion");
-		} else if ((GrandBossManager.getInstance().getBossStatus(ORFEN) == ALIVE) && (npc.getId() == RAIKEL_LEOS)) {
-			MINIONS.remove(npc);
-			startQuestTimer("spawn_minion", 360000, npc, null);
+			final long respawnTime = (grandBoss().getIntervalOfOrfenSpawn() + getRandom(-grandBoss().getRandomOfOrfenSpawn(), grandBoss().getRandomOfOrfenSpawn())) * 3600000;
+			GrandBossManager.getInstance().getStatsSet(ORFEN).set("respawn_time", (System.currentTimeMillis() + respawnTime));
+			startQuestTimer("ORFEN_UNLOCK", respawnTime, null, null);
+			_orfen = null;
+			_riba = null;
 		}
 		return super.onKill(npc, killer, isSummon);
 	}
+	
+	@Override
+	public String onExitZone(L2Character character, L2ZoneType zone) {
+		if ((character.isAttackable()) && (character.isRaid()) && (!_zone.isInsideZone(_orfen)) && (_orfen != null)) {
+			_orfen.disableCoreAI(true);
+			_orfen.teleToLocation(_orfen.getSpawn().getLocation());
+			startQuestTimer("CORE_AI", 100, _orfen, null);
+		}
+		return super.onExitZone(character, zone);
+	}
+	
+	@Override
+	protected void onTeleport(L2Npc npc) {
+		startQuestTimer("CORE_AI", 100, npc, null);
+	}
+	
+	public void spawnBoss(L2GrandBossInstance npc) {
+		GrandBossManager.getInstance().addBoss(npc);
+		npc.broadcastPacket(Music.BS01_A_7000.getPacket());
+		_orfen = npc;
+	}
 }
-- 
GitLab