Jump to content

Question

Posted (edited)

Hey all! I'm working on my own server(HIGH FIVE) (files not mine ofc) and I was testing them, to be sure everything is OK. But when I checked Frintezza, saw a BIG bug or don't know what it must be. 

 

1) Entered with GM hero(I got low p atk so i didnt 1 hit, was visible, was hittable) by the npc;

2) Went through first room - killed all mobs -> Gate opened - it;s ok for now;

3) Went in the 2nd room - killed all mobs again -> Gate opened - ok again;

4) Went in Frintezza's room and waited for the "Earthquake", then pictures, evils and Scarlet spawned;

5) I killed pictures and evils and start hitting Scarlet - but Scarlet never goes to his 2nd phase or Avatar whatever it is - the one with Wings on his back - not the transformation - it must be after that.

 

So my quest is: Is this instance bugged or I need to do it with normal characters, not GMs (btw Freya worked with GM Char). Also I checked like 3 or 4 files for that instance (java file - FinalEmperialTomb.java) And they were exactly the same. If you want I can put the source here or the file. 

/*
 * Copyright (C) 2004-2013 L2J DataPack
 * 
 * This file is part of L2J DataPack.
 * 
 * L2J DataPack 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.
 * 
 * L2J DataPack 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/>.
 */
package l2r.gameserver.scripts.instances;

import java.io.File;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;

import javax.xml.parsers.DocumentBuilderFactory;

import javolution.util.FastList;
import javolution.util.FastMap;
import l2r.Config;
import l2r.gameserver.GeoData;
import l2r.gameserver.ThreadPoolManager;
import l2r.gameserver.enums.CtrlIntention;
import l2r.gameserver.enums.InstanceType;
import l2r.gameserver.enums.PcCondOverride;
import l2r.gameserver.instancemanager.InstanceManager;
import l2r.gameserver.model.L2CharPosition;
import l2r.gameserver.model.L2CommandChannel;
import l2r.gameserver.model.L2Party;
import l2r.gameserver.model.L2Territory;
import l2r.gameserver.model.L2World;
import l2r.gameserver.model.Location;
import l2r.gameserver.model.actor.L2Attackable;
import l2r.gameserver.model.actor.L2Character;
import l2r.gameserver.model.actor.L2Npc;
import l2r.gameserver.model.actor.instance.L2GrandBossInstance;
import l2r.gameserver.model.actor.instance.L2MonsterInstance;
import l2r.gameserver.model.actor.instance.L2PcInstance;
import l2r.gameserver.model.holders.SkillHolder;
import l2r.gameserver.model.instancezone.InstanceWorld;
import l2r.gameserver.model.quest.Quest;
import l2r.gameserver.model.quest.QuestState;
import l2r.gameserver.model.skills.L2Skill;
import l2r.gameserver.model.skills.L2SkillType;
import l2r.gameserver.network.NpcStringId;
import l2r.gameserver.network.SystemMessageId;
import l2r.gameserver.network.serverpackets.AbstractNpcInfo.NpcInfo;
import l2r.gameserver.network.serverpackets.Earthquake;
import l2r.gameserver.network.serverpackets.ExShowScreenMessage;
import l2r.gameserver.network.serverpackets.L2GameServerPacket;
import l2r.gameserver.network.serverpackets.MagicSkillCanceld;
import l2r.gameserver.network.serverpackets.MagicSkillUse;
import l2r.gameserver.network.serverpackets.SocialAction;
import l2r.gameserver.network.serverpackets.SpecialCamera;
import l2r.gameserver.network.serverpackets.SystemMessage;
import l2r.gameserver.util.Util;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 * Final Emperial Tomb instance zone. TODO:<br>
 * Test when Frintezza song use 5008 effect skill.<br>
 * Test deeply Scarlet van Halisha's AI.<br>
 * Use proper zone spawn system.
 * @author Gigiikun
 */
public class FinalEmperialTomb extends Quest
{
	private class FETWorld extends InstanceWorld
	{
		public Lock lock = new ReentrantLock();
		public FastList<L2Npc> npcList = new FastList<>();
		public int darkChoirPlayerCount = 0;
		public FrintezzaSong OnSong = null;
		public ScheduledFuture<?> songTask = null;
		public ScheduledFuture<?> songEffectTask = null;
		public boolean isVideo = false;
		public L2Npc frintezzaDummy = null;
		public L2Npc overheadDummy = null;
		public L2Npc portraitDummy1 = null;
		public L2Npc portraitDummy3 = null;
		public L2Npc scarletDummy = null;
		public L2GrandBossInstance frintezza = null;
		public L2GrandBossInstance activeScarlet = null;
		public List<L2MonsterInstance> demons = new FastList<>();
		public Map<L2MonsterInstance, Integer> portraits = new FastMap<>();
		public int scarlet_x = 0;
		public int scarlet_y = 0;
		public int scarlet_z = 0;
		public int scarlet_h = 0;
		public int scarlet_a = 0;
		
		protected FETWorld()
		{
			npcList.shared();
		}
	}
	
	protected static class FETSpawn
	{
		public boolean isZone = false;
		public boolean isNeededNextFlag = false;
		public int npcId;
		public int x = 0;
		public int y = 0;
		public int z = 0;
		public int h = 0;
		public int zone = 0;
		public int count = 0;
	}
	
	private static class FrintezzaSong
	{
		public SkillHolder skill;
		public SkillHolder effectSkill;
		public NpcStringId songName;
		public int chance;
		
		public FrintezzaSong(SkillHolder sk, SkillHolder esk, NpcStringId sn, int ch)
		{
			skill = sk;
			effectSkill = esk;
			songName = sn;
			chance = ch;
		}
	}
	
	private static final int INSTANCEID = 136; // this is the client number
	private static final int MIN_PLAYERS = Config.MIN_PLAYER_TO_FE;
	private static final int MAX_PLAYERS = Config.MAX_PLAYER_TO_FE;
	private static final boolean debug = false;
	
	private final Map<Integer, L2Territory> _spawnZoneList = new HashMap<>();
	private final Map<Integer, List<FETSpawn>> _spawnList = new HashMap<>();
	private final List<Integer> _mustKillMobsId = new FastList<>();
	
	// Teleports
	private static final Location ENTER_TELEPORT = new Location(-88015, -141153, -9168);
	
	// NPCs
	private static final int GUIDE = 32011;
	private static final int CUBE = 29061;
	// Item
	private static final int DEWDROP_OF_DESTRUCTION_ITEM_ID = 8556;
	// mobs
	private static final int SCARLET1 = 29046;
	private static final int SCARLET2 = 29047;
	private static final int FRINTEZZA = 29045;
	private static final int[] PORTRAITS =
	{
		29048,
		29049
	};
	private static final int[] DEMONS =
	{
		29050,
		29051
	};
	private static final int HALL_ALARM = 18328;
	private static final int HALL_KEEPER_CAPTAIN = 18329;
	private static final int HALL_KEEPER_SUICIDAL_SOLDIER = 18333;
	private static final int DARK_CHOIR_PLAYER = 18339;
	private static final int[] AI_DISABLED_MOBS =
	{
		18328
	};
	
	private static final int FIRST_SCARLET_WEAPON = 8204;
	private static final int SECOND_SCARLET_WEAPON = 7903;
	protected static final SkillHolder INTRO_SKILL = new SkillHolder(5004, 1);
	private static final SkillHolder FIRST_MORPH_SKILL = new SkillHolder(5017, 1);
	
	protected static final FrintezzaSong[] FRINTEZZASONGLIST =
	{
		new FrintezzaSong(new SkillHolder(5007, 1), new SkillHolder(5008, 1), NpcStringId.REQUIEM_OF_HATRED, 5),
		new FrintezzaSong(new SkillHolder(5007, 2), new SkillHolder(5008, 2), NpcStringId.RONDO_OF_SOLITUDE, 50),
		new FrintezzaSong(new SkillHolder(5007, 3), new SkillHolder(5008, 3), NpcStringId.FRENETIC_TOCCATA, 70),
		new FrintezzaSong(new SkillHolder(5007, 4), new SkillHolder(5008, 4), NpcStringId.FUGUE_OF_JUBILATION, 90),
		new FrintezzaSong(new SkillHolder(5007, 5), new SkillHolder(5008, 5), NpcStringId.HYPNOTIC_MAZURKA, 100),
	};
	// Skills
	private static final int DEWDROP_OF_DESTRUCTION_SKILL_ID = 2276;
	private static final int SOUL_BREAKING_ARROW_SKILL_ID = 2234;
	// Doors/Walls/Zones
	protected static final int[] FIRST_ROOM_DOORS =
	{
		17130051,
		17130052,
		17130053,
		17130054,
		17130055,
		17130056,
		17130057,
		17130058
	};
	protected static final int[] SECOND_ROOM_DOORS =
	{
		17130061,
		17130062,
		17130063,
		17130064,
		17130065,
		17130066,
		17130067,
		17130068,
		17130069,
		17130070
	};
	
	protected static final int[] FIRST_ROUTE_DOORS =
	{
		17130042,
		17130043
	};
	protected static final int[] SECOND_ROUTE_DOORS =
	{
		17130045,
		17130046
	};
	protected static final L2CharPosition MOVE_TO_CENTER = new L2CharPosition(-87904, -141296, -9168, 0);
	
	// spawns
	private static final int TIME_BETWEEN_DEMON_SPAWNS = 20000;
	private static final int MAX_DEMONS = 24;
	protected static final int[][] PORTRAIT_SPAWNS =
	{
		{
			29048,
			-89381,
			-153981,
			-9168,
			3368,
			-89378,
			-153968,
			-9168,
			3368
		},
		{
			29048,
			-86234,
			-152467,
			-9168,
			37656,
			-86261,
			-152492,
			-9168,
			37656
		},
		{
			29049,
			-89342,
			-152479,
			-9168,
			-5152,
			-89311,
			-152491,
			-9168,
			-5152
		},
		{
			29049,
			-86189,
			-153968,
			-9168,
			29456,
			-86217,
			-153956,
			-9168,
			29456
		}
	};
	
	// Initialization at 6:30 am on Wednesday and Saturday
	private static final int RESET_HOUR = 6;
	private static final int RESET_MIN = 30;
	private static final int RESET_DAY_1 = 4;
	private static final int RESET_DAY_2 = 7;
	
	@SuppressWarnings("unused")
	private void load()
	{
		int spawnCount = 0;
		try
		{
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setValidating(false);
			factory.setIgnoringComments(true);
			
			File file = new File(Config.DATAPACK_ROOT + "/data/spawnZones/final_emperial_tomb.xml");
			if (!file.exists())
			{
				_log.severe("[Final Emperial Tomb] Missing final_emperial_tomb.xml. The quest wont work without it!");
				return;
			}
			
			Document doc = factory.newDocumentBuilder().parse(file);
			Node first = doc.getFirstChild();
			if ((first != null) && "list".equalsIgnoreCase(first.getNodeName()))
			{
				for (Node n = first.getFirstChild(); n != null; n = n.getNextSibling())
				{
					if ("npc".equalsIgnoreCase(n.getNodeName()))
					{
						for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
						{
							if ("spawn".equalsIgnoreCase(d.getNodeName()))
							{
								NamedNodeMap attrs = d.getAttributes();
								Node att = attrs.getNamedItem("npcId");
								if (att == null)
								{
									_log.severe("[Final Emperial Tomb] Missing npcId in npc List, skipping");
									continue;
								}
								int npcId = Integer.parseInt(attrs.getNamedItem("npcId").getNodeValue());
								
								att = attrs.getNamedItem("flag");
								if (att == null)
								{
									_log.severe("[Final Emperial Tomb] Missing flag in npc List npcId: " + npcId + ", skipping");
									continue;
								}
								int flag = Integer.parseInt(attrs.getNamedItem("flag").getNodeValue());
								if (!_spawnList.containsKey(flag))
								{
									_spawnList.put(flag, new FastList<FETSpawn>());
								}
								
								for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
								{
									if ("loc".equalsIgnoreCase(cd.getNodeName()))
									{
										attrs = cd.getAttributes();
										FETSpawn spw = new FETSpawn();
										spw.npcId = npcId;
										
										att = attrs.getNamedItem("x");
										if (att != null)
										{
											spw.x = Integer.parseInt(att.getNodeValue());
										}
										else
										{
											continue;
										}
										att = attrs.getNamedItem("y");
										if (att != null)
										{
											spw.y = Integer.parseInt(att.getNodeValue());
										}
										else
										{
											continue;
										}
										att = attrs.getNamedItem("z");
										if (att != null)
										{
											spw.z = Integer.parseInt(att.getNodeValue());
										}
										else
										{
											continue;
										}
										att = attrs.getNamedItem("heading");
										if (att != null)
										{
											spw.h = Integer.parseInt(att.getNodeValue());
										}
										else
										{
											continue;
										}
										att = attrs.getNamedItem("mustKill");
										if (att != null)
										{
											spw.isNeededNextFlag = Boolean.parseBoolean(att.getNodeValue());
										}
										if (spw.isNeededNextFlag)
										{
											_mustKillMobsId.add(npcId);
										}
										_spawnList.get(flag).add(spw);
										spawnCount++;
									}
									else if ("zone".equalsIgnoreCase(cd.getNodeName()))
									{
										attrs = cd.getAttributes();
										FETSpawn spw = new FETSpawn();
										spw.npcId = npcId;
										spw.isZone = true;
										
										att = attrs.getNamedItem("id");
										if (att != null)
										{
											spw.zone = Integer.parseInt(att.getNodeValue());
										}
										else
										{
											continue;
										}
										att = attrs.getNamedItem("count");
										if (att != null)
										{
											spw.count = Integer.parseInt(att.getNodeValue());
										}
										else
										{
											continue;
										}
										att = attrs.getNamedItem("mustKill");
										if (att != null)
										{
											spw.isNeededNextFlag = Boolean.parseBoolean(att.getNodeValue());
										}
										if (spw.isNeededNextFlag)
										{
											_mustKillMobsId.add(npcId);
										}
										_spawnList.get(flag).add(spw);
										spawnCount++;
									}
								}
							}
						}
					}
					else if ("spawnZones".equalsIgnoreCase(n.getNodeName()))
					{
						for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
						{
							if ("zone".equalsIgnoreCase(d.getNodeName()))
							{
								NamedNodeMap attrs = d.getAttributes();
								Node att = attrs.getNamedItem("id");
								if (att == null)
								{
									_log.severe("[Final Emperial Tomb] Missing id in spawnZones List, skipping");
									continue;
								}
								int id = Integer.parseInt(att.getNodeValue());
								att = attrs.getNamedItem("minZ");
								if (att == null)
								{
									_log.severe("[Final Emperial Tomb] Missing minZ in spawnZones List id: " + id + ", skipping");
									continue;
								}
								int minz = Integer.parseInt(att.getNodeValue());
								att = attrs.getNamedItem("maxZ");
								if (att == null)
								{
									_log.severe("[Final Emperial Tomb] Missing maxZ in spawnZones List id: " + id + ", skipping");
									continue;
								}
								int maxz = Integer.parseInt(att.getNodeValue());
								L2Territory ter = new L2Territory(id);
								
								for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
								{
									if ("point".equalsIgnoreCase(cd.getNodeName()))
									{
										attrs = cd.getAttributes();
										int x, y;
										att = attrs.getNamedItem("x");
										if (att != null)
										{
											x = Integer.parseInt(att.getNodeValue());
										}
										else
										{
											continue;
										}
										att = attrs.getNamedItem("y");
										if (att != null)
										{
											y = Integer.parseInt(att.getNodeValue());
										}
										else
										{
											continue;
										}
										
										ter.add(x, y, minz, maxz, 0);
									}
								}
								
								_spawnZoneList.put(id, ter);
							}
						}
					}
				}
			}
		}
		catch (Exception e)
		{
			_log.log(Level.WARNING, "[Final Emperial Tomb] Could not parse final_emperial_tomb.xml file: " + e.getMessage(), e);
		}
		if (debug)
		{
			_log.info("[Final Emperial Tomb] Loaded " + _spawnZoneList.size() + " spawn zones data.");
			_log.info("[Final Emperial Tomb] Loaded " + spawnCount + " spawns data.");
		}
	}
	
	private boolean checkConditions(L2PcInstance player)
	{
		if (debug || player.canOverrideCond(PcCondOverride.INSTANCE_CONDITIONS))
		{
			return true;
		}
		L2Party party = player.getParty();
		if (party == null)
		{
			player.sendPacket(SystemMessageId.NOT_IN_PARTY_CANT_ENTER);
			return false;
		}
		L2CommandChannel channel = player.getParty().getCommandChannel();
		if (channel == null)
		{
			player.sendPacket(SystemMessageId.NOT_IN_COMMAND_CHANNEL_CANT_ENTER);
			return false;
		}
		else if (channel.getLeader() != player)
		{
			player.sendPacket(SystemMessageId.ONLY_PARTY_LEADER_CAN_ENTER);
			return false;
		}
		else if (player.getInventory().getItemByItemId(8073) == null)
		{
			SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_ITEM_REQUIREMENT_NOT_SUFFICIENT);
			sm.addPcName(player);
			player.sendPacket(sm);
			return false;
		}
		else if ((channel.getMemberCount() < MIN_PLAYERS) || (channel.getMemberCount() > MAX_PLAYERS))
		{
			player.sendPacket(SystemMessageId.PARTY_EXCEEDED_THE_LIMIT_CANT_ENTER);
			return false;
		}
		for (L2PcInstance channelMember : channel.getMembers())
		{
			if (channelMember.getLevel() < Config.MIN_LEVEL_TO_FE)
			{
				SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_LEVEL_REQUIREMENT_NOT_SUFFICIENT);
				sm.addPcName(channelMember);
				party.broadcastPacket(sm);
				return false;
			}
			if (!Util.checkIfInRange(1000, player, channelMember, true))
			{
				SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_IS_IN_LOCATION_THAT_CANNOT_BE_ENTERED);
				sm.addPcName(channelMember);
				party.broadcastPacket(sm);
				return false;
			}
			Long reentertime = InstanceManager.getInstance().getInstanceTime(channelMember.getObjectId(), INSTANCEID);
			if (System.currentTimeMillis() < reentertime)
			{
				SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_MAY_NOT_REENTER_YET);
				sm.addPcName(channelMember);
				party.broadcastPacket(sm);
				return false;
			}
		}
		return true;
	}
	
	protected int enterInstance(L2PcInstance player, String template, Location loc)
	{
		int instanceId = 0;
		// check for existing instances for this player
		InstanceWorld world = InstanceManager.getInstance().getPlayerWorld(player);
		// existing instance
		if (world != null)
		{
			if (!(world instanceof FETWorld))
			{
				player.sendPacket(SystemMessageId.ALREADY_ENTERED_ANOTHER_INSTANCE_CANT_ENTER);
				return 0;
			}
			teleportPlayer(player, loc, world.getInstanceId(), false);
			return world.getInstanceId();
		}
		
		// New instance
		if (!checkConditions(player))
		{
			return 0;
		}
		if (!player.canOverrideCond(PcCondOverride.INSTANCE_CONDITIONS) && !player.destroyItemByItemId("QUEST", 8073, 1, player, true))
		{
			return 0;
		}
		instanceId = InstanceManager.getInstance().createDynamicInstance(template);
		// Instance ins = InstanceManager.getInstance().getInstance(instanceId);
		// ins.setSpawnLoc(new int[]{player.getX(),player.getY(),player.getZ()});
		world = new FETWorld();
		world.setTemplateId(INSTANCEID);
		world.setInstanceId(instanceId);
		world.setStatus(0);
		InstanceManager.getInstance().addWorld(world);
		controlStatus((FETWorld) world);
		_log.info("Final Emperial Tomb started " + template + " Instance: " + instanceId + " created by player: " + player.getName());
		// teleport players
		if ((player.getParty() == null) || (player.getParty().getCommandChannel() == null))
		{
			player.destroyItemByItemId(getName(), DEWDROP_OF_DESTRUCTION_ITEM_ID, player.getInventory().getInventoryItemCount(DEWDROP_OF_DESTRUCTION_ITEM_ID, -1), null, true);
			world.addAllowed(player.getObjectId());
			teleportPlayer(player, loc, instanceId, false);
		}
		else
		{
			for (L2PcInstance channelMember : player.getParty().getCommandChannel().getMembers())
			{
				channelMember.destroyItemByItemId(getName(), DEWDROP_OF_DESTRUCTION_ITEM_ID, channelMember.getInventory().getInventoryItemCount(DEWDROP_OF_DESTRUCTION_ITEM_ID, -1), null, true);
				world.addAllowed(channelMember.getObjectId());
				teleportPlayer(channelMember, loc, instanceId, false);
			}
		}
		return instanceId;
	}
	
	protected boolean checkKillProgress(L2Npc mob, FETWorld world)
	{
		if (world.npcList.contains(mob))
		{
			world.npcList.remove(mob);
		}
		return world.npcList.isEmpty();
	}
	
	private void spawnFlaggedNPCs(FETWorld world, int flag)
	{
		if (world.lock.tryLock())
		{
			try
			{
				for (FETSpawn spw : _spawnList.get(flag))
				{
					if (spw.isZone)
					{
						for (int i = 0; i < spw.count; i++)
						{
							if (_spawnZoneList.containsKey(spw.zone))
							{
								int[] point = _spawnZoneList.get(spw.zone).getRandomPoint();
								spawn(world, spw.npcId, point[0], point[1], GeoData.getInstance().getSpawnHeight(point[0], point[1], point[2], point[3], null), getRandom(65535), spw.isNeededNextFlag);
							}
							else
							{
								_log.info("[Final Emperial Tomb] Missing zone: " + spw.zone);
							}
						}
					}
					else
					{
						spawn(world, spw.npcId, spw.x, spw.y, spw.z, spw.h, spw.isNeededNextFlag);
					}
				}
			}
			finally
			{
				world.lock.unlock();
			}
		}
	}
	
	protected boolean controlStatus(FETWorld world)
	{
		if (world.lock.tryLock())
		{
			try
			{
				if (debug)
				{
					_log.info("[Final Emperial Tomb] Starting " + world.getStatus() + ". status.");
				}
				world.npcList.clear();
				switch (world.getStatus())
				{
					case 0:
						spawnFlaggedNPCs(world, 0);
						break;
					case 1:
						for (int doorId : FIRST_ROUTE_DOORS)
						{
							openDoor(doorId, world.getInstanceId());
						}
						spawnFlaggedNPCs(world, world.getStatus());
						break;
					case 2:
						for (int doorId : SECOND_ROUTE_DOORS)
						{
							openDoor(doorId, world.getInstanceId());
						}
						ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(world, 0), 600000);
						break;
					case 3: // first morph
						if (world.songEffectTask != null)
						{
							world.songEffectTask.cancel(false);
						}
						world.songEffectTask = null;
						world.activeScarlet.setIsInvul(true);
						if (world.activeScarlet.isCastingNow())
						{
							world.activeScarlet.abortCast();
						}
						setInstanceTimeRestrictions(world);
						world.activeScarlet.doCast(FIRST_MORPH_SKILL.getSkill());
						ThreadPoolManager.getInstance().scheduleGeneral(new SongTask(world, 2), 1500);
						break;
					case 4: // second morph
						world.isVideo = true;
						broadCastPacket(world, new MagicSkillCanceld(world.frintezza.getObjectId()));
						if (world.songEffectTask != null)
						{
							world.songEffectTask.cancel(false);
						}
						world.songEffectTask = null;
						ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(world, 23), 2000);
						ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(world, 24), 2100);
						break;
					case 5: // raid success
						world.isVideo = true;
						broadCastPacket(world, new MagicSkillCanceld(world.frintezza.getObjectId()));
						if (world.songTask != null)
						{
							world.songTask.cancel(true);
						}
						if (world.songEffectTask != null)
						{
							world.songEffectTask.cancel(false);
						}
						world.songTask = null;
						world.songEffectTask = null;
						ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(world, 33), 500);
						break;
					case 6: // open doors
						InstanceManager.getInstance().getInstance(world.getInstanceId()).setDuration(300000);
						for (int doorId : FIRST_ROOM_DOORS)
						{
							openDoor(doorId, world.getInstanceId());
						}
						for (int doorId : FIRST_ROUTE_DOORS)
						{
							openDoor(doorId, world.getInstanceId());
						}
						for (int doorId : SECOND_ROUTE_DOORS)
						{
							openDoor(doorId, world.getInstanceId());
						}
						for (int doorId : SECOND_ROOM_DOORS)
						{
							closeDoor(doorId, world.getInstanceId());
						}
						break;
				}
				world.incStatus();
				return true;
			}
			finally
			{
				world.lock.unlock();
			}
		}
		return false;
	}
	
	protected void spawn(FETWorld world, int npcId, int x, int y, int z, int h, boolean addToKillTable)
	{
		L2Npc npc = addSpawn(npcId, x, y, z, h, false, 0, false, world.getInstanceId());
		if (addToKillTable)
		{
			world.npcList.add(npc);
		}
		npc.setIsNoRndWalk(true);
		if (npc.isInstanceType(InstanceType.L2Attackable))
		{
			((L2Attackable) npc).setSeeThroughSilentMove(true);
		}
		if (Util.contains(AI_DISABLED_MOBS, npcId))
		{
			npc.disableCoreAI(true);
		}
		if (npcId == DARK_CHOIR_PLAYER)
		{
			world.darkChoirPlayerCount++;
		}
	}
	
	private class DemonSpawnTask implements Runnable
	{
		private final FETWorld _world;
		
		DemonSpawnTask(FETWorld world)
		{
			_world = world;
		}
		
		@Override
		public void run()
		{
			if ((InstanceManager.getInstance().getWorld(_world.getInstanceId()) != _world) || _world.portraits.isEmpty())
			{
				if (debug)
				{
					_log.info("[Final Emperial Tomb] Instance is deleted or all Portraits is killed.");
				}
				return;
			}
			for (int i : _world.portraits.values())
			{
				if (_world.demons.size() > MAX_DEMONS)
				{
					break;
				}
				L2MonsterInstance demon = (L2MonsterInstance) addSpawn(PORTRAIT_SPAWNS[i][0] + 2, PORTRAIT_SPAWNS[i][5], PORTRAIT_SPAWNS[i][6], PORTRAIT_SPAWNS[i][7], PORTRAIT_SPAWNS[i][8], false, 0, false, _world.getInstanceId());
				updateKnownList(_world, demon);
				_world.demons.add(demon);
			}
			ThreadPoolManager.getInstance().scheduleGeneral(new DemonSpawnTask(_world), TIME_BETWEEN_DEMON_SPAWNS);
		}
	}
	
	private class SoulBreakingArrow implements Runnable
	{
		private final L2Npc _npc;
		
		protected SoulBreakingArrow(L2Npc npc)
		{
			_npc = npc;
		}
		
		@Override
		public void run()
		{
			_npc.setScriptValue(0);
		}
	}
	
	private class SongTask implements Runnable
	{
		private final FETWorld _world;
		private final int _status;
		
		SongTask(FETWorld world, int status)
		{
			_world = world;
			_status = status;
		}
		
		@Override
		public void run()
		{
			if (InstanceManager.getInstance().getWorld(_world.getInstanceId()) != _world)
			{
				return;
			}
			switch (_status)
			{
				case 0: // new song play
					if (_world.isVideo)
					{
						_world.songTask = ThreadPoolManager.getInstance().scheduleGeneral(new SongTask(_world, 0), 1000);
					}
					else if ((_world.frintezza != null) && !_world.frintezza.isDead())
					{
						if (_world.frintezza.getScriptValue() != 1)
						{
							int rnd = getRandom(100);
							for (FrintezzaSong element : FRINTEZZASONGLIST)
							{
								if (rnd < element.chance)
								{
									_world.OnSong = element;
									broadCastPacket(_world, new ExShowScreenMessage(2, -1, 2, 0, 0, 0, 0, true, 4000, false, null, element.songName, null));
									broadCastPacket(_world, new MagicSkillUse(_world.frintezza, _world.frintezza, element.skill.getSkillId(), element.skill.getSkillLvl(), element.skill.getSkill().getHitTime(), 0));
									_world.songEffectTask = ThreadPoolManager.getInstance().scheduleGeneral(new SongTask(_world, 1), element.skill.getSkill().getHitTime() - 10000);
									_world.songTask = ThreadPoolManager.getInstance().scheduleGeneral(new SongTask(_world, 0), element.skill.getSkill().getHitTime());
									break;
								}
							}
						}
						else
						{
							ThreadPoolManager.getInstance().scheduleGeneral(new SoulBreakingArrow(_world.frintezza), 35000);
						}
					}
					break;
				case 1: // Frintezza song effect
					_world.songEffectTask = null;
					L2Skill skill = _world.OnSong.effectSkill.getSkill();
					if (skill == null)
					{
						return;
					}
					
					if ((_world.frintezza != null) && !_world.frintezza.isDead() && (_world.activeScarlet != null) && !_world.activeScarlet.isDead())
					{
						List<L2Character> targetList = new FastList<>();
						if ((skill.getSkillType() == L2SkillType.STUN) || (skill.getSkillType() == L2SkillType.DEBUFF))
						{
							for (int objId : _world.getAllowed())
							{
								L2PcInstance player = L2World.getInstance().getPlayer(objId);
								if ((player != null) && player.isOnline() && (player.getInstanceId() == _world.getInstanceId()))
								{
									if (!player.isDead())
									{
										targetList.add(player);
									}
									if (player.hasSummon() && !player.getSummon().isDead())
									{
										targetList.add(player.getSummon());
									}
								}
							}
						}
						else
						{
							targetList.add(_world.activeScarlet);
						}
						if (targetList.size() > 0)
						{
							_world.frintezza.doCast(skill, targetList.get(0), targetList.toArray(new L2Character[targetList.size()]));
						}
					}
					break;
				case 2: // finish morph
					_world.activeScarlet.setRHandId(SECOND_SCARLET_WEAPON);
					_world.activeScarlet.setIsInvul(false);
					break;
			}
		}
	}
	
	private class IntroTask implements Runnable
	{
		private final FETWorld _world;
		private final int _status;
		
		IntroTask(FETWorld world, int status)
		{
			_world = world;
			_status = status;
		}
		
		@Override
		public void run()
		{
			switch (_status)
			{
				case 0:
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 1), 27000);
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 2), 30000);
					broadCastPacket(_world, new Earthquake(-87784, -155083, -9087, 45, 27));
					break;
				case 1:
					for (int doorId : FIRST_ROOM_DOORS)
					{
						closeDoor(doorId, _world.getInstanceId());
					}
					for (int doorId : FIRST_ROUTE_DOORS)
					{
						closeDoor(doorId, _world.getInstanceId());
					}
					for (int doorId : SECOND_ROOM_DOORS)
					{
						closeDoor(doorId, _world.getInstanceId());
					}
					for (int doorId : SECOND_ROUTE_DOORS)
					{
						closeDoor(doorId, _world.getInstanceId());
					}
					addSpawn(29061, -87904, -141296, -9168, 0, false, 0, false, _world.getInstanceId());
					break;
				case 2:
					_world.frintezzaDummy = addSpawn(29052, -87784, -155083, -9087, 16048, false, 0, false, _world.getInstanceId());
					_world.frintezzaDummy.setIsInvul(true);
					_world.frintezzaDummy.setIsImmobilized(true);
					
					_world.overheadDummy = addSpawn(29052, -87784, -153298, -9175, 16384, false, 0, false, _world.getInstanceId());
					_world.overheadDummy.setIsInvul(true);
					_world.overheadDummy.setIsImmobilized(true);
					_world.overheadDummy.setCollisionHeight(600);
					broadCastPacket(_world, new NpcInfo(_world.overheadDummy, null));
					
					_world.portraitDummy1 = addSpawn(29052, -89566, -153168, -9165, 16048, false, 0, false, _world.getInstanceId());
					_world.portraitDummy1.setIsImmobilized(true);
					_world.portraitDummy1.setIsInvul(true);
					
					_world.portraitDummy3 = addSpawn(29052, -86004, -153168, -9165, 16048, false, 0, false, _world.getInstanceId());
					_world.portraitDummy3.setIsImmobilized(true);
					_world.portraitDummy3.setIsInvul(true);
					
					_world.scarletDummy = addSpawn(29053, -87784, -153298, -9175, 16384, false, 0, false, _world.getInstanceId());
					_world.scarletDummy.setIsInvul(true);
					_world.scarletDummy.setIsImmobilized(true);
					
					stopPc();
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 3), 1000);
					break;
				case 3:
					broadCastPacket(_world, new SpecialCamera(_world.overheadDummy.getObjectId(), 0, 75, -89, 0, 100, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.overheadDummy.getObjectId(), 0, 75, -89, 0, 100, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.overheadDummy.getObjectId(), 300, 90, -10, 6500, 7000, 0, 0, 1, 0));
					
					_world.frintezza = (L2GrandBossInstance) addSpawn(FRINTEZZA, -87780, -155086, -9080, 16384, false, 0, false, _world.getInstanceId());
					_world.frintezza.setIsImmobilized(true);
					_world.frintezza.setIsInvul(true);
					_world.frintezza.disableAllSkills();
					updateKnownList(_world, _world.frintezza);
					
					for (int[] element : PORTRAIT_SPAWNS)
					{
						L2MonsterInstance demon = (L2MonsterInstance) addSpawn(element[0] + 2, element[5], element[6], element[7], element[8], false, 0, false, _world.getInstanceId());
						demon.setIsImmobilized(true);
						demon.disableAllSkills();
						updateKnownList(_world, demon);
						_world.demons.add(demon);
					}
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 4), 6500);
					break;
				case 4:
					broadCastPacket(_world, new SpecialCamera(_world.frintezzaDummy.getObjectId(), 1800, 90, 8, 6500, 7000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 5), 900);
					break;
				case 5:
					broadCastPacket(_world, new SpecialCamera(_world.frintezzaDummy.getObjectId(), 140, 90, 10, 2500, 4500, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 6), 4000);
					break;
				case 6:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 40, 75, -10, 0, 1000, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 40, 75, -10, 0, 12000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 7), 1350);
					break;
				case 7:
					broadCastPacket(_world, new SocialAction(_world.frintezza.getObjectId(), 2));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 8), 7000);
					break;
				case 8:
					_world.frintezzaDummy.deleteMe();
					_world.frintezzaDummy = null;
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 9), 1000);
					break;
				case 9:
					broadCastPacket(_world, new SocialAction(_world.demons.get(1).getObjectId(), 1));
					broadCastPacket(_world, new SocialAction(_world.demons.get(2).getObjectId(), 1));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 10), 400);
					break;
				case 10:
					broadCastPacket(_world, new SocialAction(_world.demons.get(0).getObjectId(), 1));
					broadCastPacket(_world, new SocialAction(_world.demons.get(3).getObjectId(), 1));
					sendPacketX(new SpecialCamera(_world.portraitDummy1.getObjectId(), 1000, 118, 0, 0, 1000, 0, 0, 1, 0), new SpecialCamera(_world.portraitDummy3.getObjectId(), 1000, 62, 0, 0, 1000, 0, 0, 1, 0), -87784);
					sendPacketX(new SpecialCamera(_world.portraitDummy1.getObjectId(), 1000, 118, 0, 0, 10000, 0, 0, 1, 0), new SpecialCamera(_world.portraitDummy3.getObjectId(), 1000, 62, 0, 0, 10000, 0, 0, 1, 0), -87784);
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 11), 2000);
					break;
				case 11:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 240, 90, 0, 0, 1000, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 240, 90, 25, 5500, 10000, 0, 0, 1, 0));
					broadCastPacket(_world, new SocialAction(_world.frintezza.getObjectId(), 3));
					_world.portraitDummy1.deleteMe();
					_world.portraitDummy3.deleteMe();
					_world.portraitDummy1 = null;
					_world.portraitDummy3 = null;
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 12), 4500);
					break;
				case 12:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 100, 195, 35, 0, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 13), 700);
					break;
				case 13:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 100, 195, 35, 0, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 14), 1300);
					break;
				case 14:
					broadCastPacket(_world, new ExShowScreenMessage(NpcStringId.MOURNFUL_CHORALE_PRELUDE, 2, 5000));
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 120, 180, 45, 1500, 10000, 0, 0, 1, 0));
					broadCastPacket(_world, new MagicSkillUse(_world.frintezza, _world.frintezza, 5006, 1, 34000, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 15), 1500);
					break;
				case 15:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 520, 135, 45, 8000, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 16), 7500);
					break;
				case 16:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 1500, 110, 25, 10000, 13000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 17), 9500);
					break;
				case 17:
					broadCastPacket(_world, new SpecialCamera(_world.overheadDummy.getObjectId(), 930, 160, -20, 0, 1000, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.overheadDummy.getObjectId(), 600, 180, -25, 0, 10000, 0, 0, 1, 0));
					broadCastPacket(_world, new MagicSkillUse(_world.scarletDummy, _world.overheadDummy, 5004, 1, 5800, 0));
					
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 18), 5000);
					break;
				case 18:
					_world.activeScarlet = (L2GrandBossInstance) addSpawn(29046, -87789, -153295, -9176, 16384, false, 0, false, _world.getInstanceId());
					_world.activeScarlet.setRHandId(FIRST_SCARLET_WEAPON);
					_world.activeScarlet.setIsInvul(true);
					_world.activeScarlet.setIsImmobilized(true);
					_world.activeScarlet.disableAllSkills();
					updateKnownList(_world, _world.activeScarlet);
					broadCastPacket(_world, new SocialAction(_world.activeScarlet.getObjectId(), 3));
					broadCastPacket(_world, new SpecialCamera(_world.scarletDummy.getObjectId(), 800, 180, 10, 1000, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 19), 2100);
					break;
				case 19:
					broadCastPacket(_world, new SpecialCamera(_world.activeScarlet.getObjectId(), 300, 60, 8, 0, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 20), 2000);
					break;
				case 20:
					broadCastPacket(_world, new SpecialCamera(_world.activeScarlet.getObjectId(), 500, 90, 10, 3000, 5000, 0, 0, 1, 0));
					_world.songTask = ThreadPoolManager.getInstance().scheduleGeneral(new SongTask(_world, 0), 100);
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 21), 3000);
					break;
				case 21:
					for (int i = 0; i < PORTRAIT_SPAWNS.length; i++)
					{
						L2MonsterInstance portrait = (L2MonsterInstance) addSpawn(PORTRAIT_SPAWNS[i][0], PORTRAIT_SPAWNS[i][1], PORTRAIT_SPAWNS[i][2], PORTRAIT_SPAWNS[i][3], PORTRAIT_SPAWNS[i][4], false, 0, false, _world.getInstanceId());
						updateKnownList(_world, portrait);
						_world.portraits.put(portrait, i);
					}
					
					_world.overheadDummy.deleteMe();
					_world.scarletDummy.deleteMe();
					_world.overheadDummy = null;
					_world.scarletDummy = null;
					
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 22), 2000);
					break;
				case 22:
					for (L2MonsterInstance demon : _world.demons)
					{
						demon.setIsImmobilized(false);
						demon.enableAllSkills();
					}
					_world.activeScarlet.setIsInvul(false);
					_world.activeScarlet.setIsImmobilized(false);
					_world.activeScarlet.enableAllSkills();
					_world.activeScarlet.setRunning();
					_world.activeScarlet.doCast(INTRO_SKILL.getSkill());
					_world.frintezza.enableAllSkills();
					_world.frintezza.disableCoreAI(true);
					_world.frintezza.setIsMortal(false);
					startPc();
					
					ThreadPoolManager.getInstance().scheduleGeneral(new DemonSpawnTask(_world), TIME_BETWEEN_DEMON_SPAWNS);
					break;
				case 23:
					broadCastPacket(_world, new SocialAction(_world.frintezza.getObjectId(), 4));
					break;
				case 24:
					stopPc();
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 250, 120, 15, 0, 1000, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 250, 120, 15, 0, 10000, 0, 0, 1, 0));
					_world.activeScarlet.abortAttack();
					_world.activeScarlet.abortCast();
					_world.activeScarlet.setIsInvul(true);
					_world.activeScarlet.setIsImmobilized(true);
					_world.activeScarlet.disableAllSkills();
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 25), 7000);
					break;
				case 25:
					broadCastPacket(_world, new MagicSkillUse(_world.frintezza, _world.frintezza, 5006, 1, 34000, 0));
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 500, 70, 15, 3000, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 26), 3000);
					break;
				case 26:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 2500, 90, 12, 6000, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 27), 3000);
					break;
				case 27:
					_world.scarlet_x = _world.activeScarlet.getX();
					_world.scarlet_y = _world.activeScarlet.getY();
					_world.scarlet_z = _world.activeScarlet.getZ();
					_world.scarlet_h = _world.activeScarlet.getHeading();
					if (_world.scarlet_h < 32768)
					{
						_world.scarlet_a = Math.abs(180 - (int) (_world.scarlet_h / 182.044444444));
					}
					else
					{
						_world.scarlet_a = Math.abs(540 - (int) (_world.scarlet_h / 182.044444444));
					}
					broadCastPacket(_world, new SpecialCamera(_world.activeScarlet.getObjectId(), 250, _world.scarlet_a, 12, 0, 1000, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.activeScarlet.getObjectId(), 250, _world.scarlet_a, 12, 0, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 28), 500);
					break;
				case 28:
					_world.activeScarlet.doDie(_world.activeScarlet);
					broadCastPacket(_world, new SpecialCamera(_world.activeScarlet.getObjectId(), 450, _world.scarlet_a, 14, 8000, 8000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 29), 6250);
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 30), 7200);
					break;
				case 29:
					_world.activeScarlet.deleteMe();
					_world.activeScarlet = null;
					break;
				case 30:
					_world.activeScarlet = (L2GrandBossInstance) addSpawn(SCARLET2, _world.scarlet_x, _world.scarlet_y, _world.scarlet_z, _world.scarlet_h, false, 0, false, _world.getInstanceId());
					_world.activeScarlet.setIsInvul(true);
					_world.activeScarlet.setIsImmobilized(true);
					_world.activeScarlet.disableAllSkills();
					updateKnownList(_world, _world.activeScarlet);
					
					broadCastPacket(_world, new SpecialCamera(_world.activeScarlet.getObjectId(), 450, _world.scarlet_a, 12, 500, 14000, 0, 0, 1, 0));
					
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 31), 8100);
					break;
				case 31:
					broadCastPacket(_world, new SocialAction(_world.activeScarlet.getObjectId(), 2));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 32), 9000);
					break;
				case 32:
					startPc();
					_world.activeScarlet.setIsInvul(false);
					_world.activeScarlet.setIsImmobilized(false);
					_world.activeScarlet.enableAllSkills();
					_world.isVideo = false;
					break;
				case 33:
					broadCastPacket(_world, new SpecialCamera(_world.activeScarlet.getObjectId(), 300, _world.scarlet_a - 180, 5, 0, 7000, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.activeScarlet.getObjectId(), 200, _world.scarlet_a, 85, 4000, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 34), 7400);
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 35), 7500);
					break;
				case 34:
					_world.frintezza.doDie(_world.frintezza);
					break;
				case 35:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 100, 120, 5, 0, 7000, 0, 0, 1, 0));
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 100, 90, 5, 5000, 15000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 36), 7000);
					break;
				case 36:
					broadCastPacket(_world, new SpecialCamera(_world.frintezza.getObjectId(), 900, 90, 25, 7000, 10000, 0, 0, 1, 0));
					ThreadPoolManager.getInstance().scheduleGeneral(new IntroTask(_world, 37), 9000);
					break;
				case 37:
					controlStatus(_world);
					_world.isVideo = false;
					startPc();
					break;
			}
		}
		
		private void stopPc()
		{
			for (int objId : _world.getAllowed())
			{
				L2PcInstance player = L2World.getInstance().getPlayer(objId);
				if ((player != null) && player.isOnline() && (player.getInstanceId() == _world.getInstanceId()))
				{
					player.abortAttack();
					player.abortCast();
					player.disableAllSkills();
					player.setTarget(null);
					player.stopMove(null);
					player.setIsImmobilized(true);
					player.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
				}
			}
		}
		
		private void startPc()
		{
			for (int objId : _world.getAllowed())
			{
				L2PcInstance player = L2World.getInstance().getPlayer(objId);
				if ((player != null) && player.isOnline() && (player.getInstanceId() == _world.getInstanceId()))
				{
					player.enableAllSkills();
					player.setIsImmobilized(false);
				}
			}
		}
		
		private void sendPacketX(L2GameServerPacket packet1, L2GameServerPacket packet2, int x)
		{
			for (int objId : _world.getAllowed())
			{
				L2PcInstance player = L2World.getInstance().getPlayer(objId);
				if ((player != null) && player.isOnline() && (player.getInstanceId() == _world.getInstanceId()))
				{
					if (player.getX() < x)
					{
						player.sendPacket(packet1);
					}
					else
					{
						player.sendPacket(packet2);
					}
				}
			}
		}
	}
	
	private class StatusTask implements Runnable
	{
		private final FETWorld _world;
		private final int _status;
		
		StatusTask(FETWorld world, int status)
		{
			_world = world;
			_status = status;
		}
		
		@Override
		public void run()
		{
			if (InstanceManager.getInstance().getWorld(_world.getInstanceId()) != _world)
			{
				return;
			}
			switch (_status)
			{
				case 0:
					ThreadPoolManager.getInstance().scheduleGeneral(new StatusTask(_world, 1), 2000);
					for (int doorId : FIRST_ROOM_DOORS)
					{
						openDoor(doorId, _world.getInstanceId());
					}
					break;
				case 1:
					addAggroToMobs();
					break;
				case 2:
					ThreadPoolManager.getInstance().scheduleGeneral(new StatusTask(_world, 3), 100);
					for (int doorId : SECOND_ROOM_DOORS)
					{
						openDoor(doorId, _world.getInstanceId());
					}
					break;
				case 3:
					addAggroToMobs();
					break;
				case 4:
					controlStatus(_world);
					break;
			}
		}
		
		private void addAggroToMobs()
		{
			L2PcInstance target = L2World.getInstance().getPlayer(_world.getAllowed().get(getRandom(_world.getAllowed().size())));
			if ((target == null) || (target.getInstanceId() != _world.getInstanceId()) || target.isDead() || target.isFakeDeath())
			{
				for (int objId : _world.getAllowed())
				{
					target = L2World.getInstance().getPlayer(objId);
					if ((target != null) && (target.getInstanceId() == _world.getInstanceId()) && !target.isDead() && !target.isFakeDeath())
					{
						break;
					}
					target = null;
				}
			}
			for (L2Npc mob : _world.npcList)
			{
				mob.setRunning();
				if (target != null)
				{
					((L2MonsterInstance) mob).addDamageHate(target, 0, 500);
					mob.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
				}
				else
				{
					mob.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, MOVE_TO_CENTER);
				}
			}
		}
	}
	
	protected void setInstanceTimeRestrictions(FETWorld world)
	{
		Calendar reenter = Calendar.getInstance();
		reenter.set(Calendar.MINUTE, RESET_MIN);
		reenter.set(Calendar.HOUR_OF_DAY, RESET_HOUR);
		// if time is >= RESET_HOUR - roll to the next day
		if (reenter.getTimeInMillis() <= System.currentTimeMillis())
		{
			reenter.add(Calendar.DAY_OF_MONTH, 1);
		}
		if (reenter.get(Calendar.DAY_OF_WEEK) <= RESET_DAY_1)
		{
			while (reenter.get(Calendar.DAY_OF_WEEK) != RESET_DAY_1)
			{
				reenter.add(Calendar.DAY_OF_MONTH, 1);
			}
		}
		else
		{
			while (reenter.get(Calendar.DAY_OF_WEEK) != RESET_DAY_2)
			{
				reenter.add(Calendar.DAY_OF_MONTH, 1);
			}
		}
		
		SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.INSTANT_ZONE_S1_RESTRICTED);
		sm.addInstanceName(INSTANCEID);
		
		// set instance reenter time for all allowed players
		for (int objectId : world.getAllowed())
		{
			L2PcInstance player = L2World.getInstance().getPlayer(objectId);
			InstanceManager.getInstance().setInstanceTime(objectId, INSTANCEID, reenter.getTimeInMillis());
			if ((player != null) && player.isOnline())
			{
				player.sendPacket(sm);
			}
		}
	}
	
	protected void broadCastPacket(FETWorld world, L2GameServerPacket packet)
	{
		for (int objId : world.getAllowed())
		{
			L2PcInstance player = L2World.getInstance().getPlayer(objId);
			if ((player != null) && player.isOnline() && (player.getInstanceId() == world.getInstanceId()))
			{
				player.sendPacket(packet);
			}
		}
	}
	
	protected void updateKnownList(FETWorld world, L2Npc npc)
	{
		Map<Integer, L2PcInstance> npcKnownPlayers = npc.getKnownList().getKnownPlayers();
		for (int objId : world.getAllowed())
		{
			L2PcInstance player = L2World.getInstance().getPlayer(objId);
			if ((player != null) && player.isOnline() && (player.getInstanceId() == world.getInstanceId()))
			{
				npcKnownPlayers.put(player.getObjectId(), player);
			}
		}
	}
	
	@Override
	public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, L2Skill skill)
	{
		final InstanceWorld tmpworld = InstanceManager.getInstance().getWorld(npc.getInstanceId());
		if (tmpworld instanceof FETWorld)
		{
			final FETWorld world = (FETWorld) tmpworld;
			if ((npc.getNpcId() == SCARLET1) && (world.getStatus() == 3) && (npc.getCurrentHp() < (npc.getMaxHp() * 0.80)))
			{
				controlStatus(world);
			}
			else if ((npc.getNpcId() == SCARLET1) && (world.getStatus() == 4) && (npc.getCurrentHp() < (npc.getMaxHp() * 0.20)))
			{
				controlStatus(world);
			}
			if (skill != null)
			{
				// When Dewdrop of Destruction is used on Portraits they suicide.
				if (Util.contains(PORTRAITS, npc.getNpcId()) && (skill.getId() == DEWDROP_OF_DESTRUCTION_SKILL_ID))
				{
					npc.doDie(attacker);
				}
				else if ((npc.getNpcId() == FRINTEZZA) && (skill.getId() == SOUL_BREAKING_ARROW_SKILL_ID))
				{
					npc.setScriptValue(1);
					npc.setTarget(null);
					npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
				}
			}
		}
		return null;
	}
	
	@Override
	public String onSpellFinished(L2Npc npc, L2PcInstance player, L2Skill skill)
	{
		if (skill.isSuicideAttack())
		{
			return onKill(npc, null, false);
		}
		return super.onSpellFinished(npc, player, skill);
	}
	
	@Override
	public String onKill(L2Npc npc, L2PcInstance player, boolean isSummon)
	{
		InstanceWorld tmpworld = InstanceManager.getInstance().getWorld(npc.getInstanceId());
		if (tmpworld instanceof FETWorld)
		{
			FETWorld world = (FETWorld) tmpworld;
			if (npc.getNpcId() == HALL_ALARM)
			{
				ThreadPoolManager.getInstance().scheduleGeneral(new StatusTask(world, 0), 2000);
				if (debug)
				{
					_log.info("[Final Emperial Tomb] Hall alarm is disabled, doors will open!");
				}
			}
			else if (npc.getNpcId() == DARK_CHOIR_PLAYER)
			{
				world.darkChoirPlayerCount--;
				if (world.darkChoirPlayerCount < 1)
				{
					ThreadPoolManager.getInstance().scheduleGeneral(new StatusTask(world, 2), 2000);
					if (debug)
					{
						_log.info("[Final Emperial Tomb] All Dark Choir Players are killed, doors will open!");
					}
				}
			}
			else if (npc.getNpcId() == SCARLET2)
			{
				controlStatus(world);
			}
			else if (world.getStatus() <= 2)
			{
				if (npc.getNpcId() == HALL_KEEPER_CAPTAIN)
				{
					if (getRandom(100) < 5)
					{
						((L2MonsterInstance) npc).dropItem(player, DEWDROP_OF_DESTRUCTION_ITEM_ID, 1);
					}
				}
				
				if (checkKillProgress(npc, world))
				{
					controlStatus(world);
				}
			}
			else if (world.demons.contains(npc))
			{
				world.demons.remove(npc);
			}
			else if (world.portraits.containsKey(npc))
			{
				world.portraits.remove(npc);
			}
		}
		return "";
	}
	
	@Override
	public String onTalk(L2Npc npc, L2PcInstance player)
	{
		int npcId = npc.getNpcId();
		QuestState st = player.getQuestState(getName());
		if (st == null)
		{
			st = newQuestState(player);
		}
		if (npcId == GUIDE)
		{
			enterInstance(player, "FinalEmperialTomb.xml", ENTER_TELEPORT);
		}
		else if (npc.getNpcId() == CUBE)
		{
			int x = -87534 + getRandom(500);
			int y = -153048 + getRandom(500);
			player.teleToLocation(x, y, -9165);
			return null;
		}
		return "";
	}
	
	public FinalEmperialTomb(int questId, String name, String descr)
	{
		super(questId, name, descr);
		
		load();
		addAttackId(SCARLET1, FRINTEZZA);
		addAttackId(PORTRAITS);
		addStartNpc(GUIDE, CUBE);
		addTalkId(GUIDE, CUBE);
		addKillId(HALL_ALARM, HALL_KEEPER_CAPTAIN, DARK_CHOIR_PLAYER, SCARLET2);
		addKillId(PORTRAITS);
		addKillId(DEMONS);
		addKillId(_mustKillMobsId);
		addSpellFinishedId(HALL_KEEPER_SUICIDAL_SOLDIER);
	}
	
	public static void main(String[] args)
	{
		new FinalEmperialTomb(-1, FinalEmperialTomb.class.getSimpleName(), "instances");
	}
}

Put the source, because I cannot attach .java files.

Thanks in advance!!!

Edited by helpdesk

4 answers to this question

Recommended Posts

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Posts

    • Five More Days Left for the 50% Discount in all our video services! 
    • Good afternoon, gentlemen. I’d like to ask a question to those who know their stuff — and just get some advice. Let me start with the main point. I want to try learning Java. Just for myself — I simply like programming, and it’s a small hobby of mine. Though, perhaps it’s too early to say I really like Java, since I don’t actually know it well yet. I’ll get straight to the point. Back in my youth, like many others, I played Lineage 2. Later, I became interested in creating servers and even made my own — we had around 70 players, all schoolkids like me. I learned how to install builds, add NPCs and various add-ons “from the internet” back then. But my attempts to create my own Lineage 2 additions in Java almost always failed. At best, I could slightly modify ready-made code, but not write anything from scratch. My interest in Lineage 2 and Java has stayed with me to this day. For me, it’s a creative process — a favorite game where you can build your own “sandbox.” I’d really like to hear from the community about where you started. In your opinion, what’s the best way to learn Java using Lineage 2 as an example? I don’t have a clear learning plan — and that’s probably the problem. I’ve tried reading Schildt’s book, and I’ve also tried studying Java source codes of various server builds by poking around to understand how the server works and how programs are written. I can’t say I understand nothing — I do get some things, and I believe I have a knack for it. But it’s not systematic. Maybe I really do need advice from more experienced people, to find a strategy that’s both effective for learning Java and interesting because it’s connected to Lineage 2 — not just writing abstract programs “for nothing.” In Lineage 2, you can see the result right away — and I get genuinely happy even from small successes. So here’s the main point: I’d like to know where you started. Is it even worth combining Java and Lineage 2? Recently, ChatGPT has been a huge help — thanks to it, I’ve made some progress and started to understand the server structure at least a little. But there’s one issue: if I just copy and paste, the result works — but without ChatGPT, I can’t reproduce it on my own. I’ve been working with PwSoft source files — I think many people know them. Why exactly them? I can’t really say — probably because there are lots of ready-made add-ons for this build online, and I originally focused more on modifying existing code than writing my own. I worked with PwSoft for a long time. Then, on ChatGPT’s advice, I switched to aCis — it’s cleaner, more minimalistic, without unnecessary “clutter.” It’s easier to understand where everything is located. I installed the aCis sources, everything compiles fine, but… if in PwSoft I could at least navigate the code and write simple methods, in aCis I couldn’t even make a basic NPC that gives noblesse for adena. That’s why I decided to write this post — just to talk about learning Java through Lineage 2, to hear your opinions, your stories, your approaches. Maybe someone will say: “Don’t bother, it’s a waste of time.” I’ll accept those answers too. But I’d still like to hear different perspectives. If you can, please recommend good video courses on Java that would help me learn faster and in a more structured way. To repeat: this is something from my childhood that I never finished. My job is stable now, and I have time — at least sometimes. I can work on this calmly, as long as my learning goes in the right direction. I think you get it: this is something “for the soul,” and I really want to grow in it. Thanks to everyone who read to the end. I really appreciate any advice. And if someone is open to chatting privately or by voice — I’d gladly talk, and I’m happy to “buy a beer” for a constructive conversation. Peace to everyone!       Добрый день, господа. Я хотел бы задать вопрос тем, кто знает свое дело — и просто получить совет. Начну с главного. Я хочу попробовать изучить Java. Только для себя — мне просто нравится программирование, и это мое маленькое хобби. Хотя, возможно, еще слишком рано говорить, что мне действительно нравится Java, так как я еще не очень хорошо ее знаю. Я сразу перейду к делу. В юности, как и многие другие, я играл в Lineage 2. Позже я увлекся созданием серверов и даже сделал свой — у нас было около 70 игроков, все школьники, как и я. Я научился устанавливать сборки, добавлять NPC и различные дополнения "из интернета" еще тогда. Но мои попытки создать свои собственные дополнения Lineage 2 на Java почти всегда терпели неудачу. В лучшем случае я мог бы немного доработать готовый код, но не писать ничего с нуля. Мой интерес к Lineage 2 и Java не покидает меня и по сей день. Для меня это творческий процесс — любимая игра, в которой можно построить свою «песочницу». Мне очень хотелось бы услышать от сообщества о том, с чего вы начали. На ваш взгляд, как лучше всего изучать Java на примере Lineage 2? У меня нет четкого плана обучения — и, вероятно, в этом проблема. Я пробовал читать книгу Герберта Шилдта, а также пытался изучать исходные коды Java различных серверных сборок, копаясь вокруг, чтобы понять, как работает сервер и как пишутся программы. Не могу сказать, что я ничего не понимаю — я кое-что понимаю, и я верю, что у меня есть к этому способности. Но это не систематично. Возможно, мне действительно нужен совет от более опытных людей, чтобы найти стратегию, которая была бы одновременно эффективной для изучения Java и интересной, потому что она связана с Lineage 2, а не просто писать абстрактные программы «впустую». В Lineage 2 результат виден сразу — и я искренне радуюсь даже от маленьких успехов. Итак, вот главное: я хотел бы знать, с чего вы начали. Стоит ли вообще совмещать Java и Lineage 2? В последнее время ChatGPT очень помог — благодаря ему я добился некоторого прогресса и начал хотя бы немного разбираться в структуре сервера. Но есть одна проблема: если я просто скопирую и вставлю, результат работает, но без ChatGPT я не могу воспроизвести его самостоятельно. Я работал с исходными файлами PwSoft — думаю, многие их знают. Почему именно они? Я не могу точно сказать — вероятно, потому что в сети есть много готовых дополнений для этой сборки, и я изначально больше сосредоточился на модификации существующего кода, чем на написании своего. Я долгое время работал с PwSoft. Потом, по совету ChatGPT, я перешел на aCis — он чище, минималистичнее, без лишнего «беспорядка». Так проще понять, где что находится. Я установил исходники aCis, все нормально компилируется, но... Если в PwSoft я мог хотя бы ориентироваться в коде и писать простые методы, то в aCis я не смог сделать даже базового NPC, который дает благородство для adena. Вот почему я решил написать этот пост — просто чтобы поговорить об изучении Java через Lineage 2, услышать ваши мнения, ваши истории, ваши подходы. Может быть, кто-то скажет: «Не заморачивайтесь, это пустая трата времени». Я тоже приму эти ответы. Но я все же хотел бы услышать разные точки зрения. Если можете, пожалуйста, порекомендуйте хорошие видеокурсы по Java, которые помогут мне учиться быстрее и более структурированно. Повторюсь: это что-то из моего детства, которое я так и не закончил. Моя работа сейчас стабильна, и у меня есть время — хотя бы иногда. Я могу спокойно работать над этим, пока мое обучение идет в правильном направлении. Думаю, вы поняли: это что-то «для души», и я очень хочу в этом расти. Спасибо всем, кто дочитал до конца. Я очень ценю любой совет. А если кто-то открыт для личного общения или голосом — я с удовольствием поговорю, и с удовольствием «куплю пиво» для конструктивного разговора. Всем мира!😀🤝
    • 亲爱的朋友们,我们很高兴地宣布一个好消息—— 我们正式推出我们的新机器人,用于购买和租用虚拟号码以接收来自任何服务的短信!您是否已经厌倦了其他平台上重复使用或转售的号码? 试试我们的解决方案! 我们的服务允许您接收来自任何主流热门服务的短信。 服务列表包含超过 200 个项目! 短信接收国家:美国 (+1)。我们使用真实的美国实体号码——从未在其他平台上使用过! 目前仅提供短期租用。每个电话号码的租用时长会在购买时显示。 每个号码的价格也会在购买前显示在对应的服务旁。 您还可以为所选服务的号码接收额外短信(接收额外短信无需额外费用)。 若要快速找到所需服务,可使用便捷的搜索功能——只需输入您所需服务的名称即可获得激活号码。 可用支付方式:加密货币、银行卡,以及从我们其他机器人转移余额。 感谢您的信任! 有效链接: 虚拟号码服务:前往 数字商品商店(网站):前往 商店 Telegram 机器人:前往 – 通过 Telegram 信使方便地访问商店。 Telegram 星星购买机器人:前往 – 在 Telegram 中快速且优惠地购买星星。 SMM 面板:前往 – 推广您的社交媒体账户。 我们向您展示当前的促销与特别优惠,用于购买我们平台的商品和服务: 1. 使用促销码 OCTOBER2025(8% 折扣)在十月份于我们的商店(网站、机器人)购物!您也可以使用首次购买促销码:SOCNET(15% 折扣) 2. 获得 $1 商店余额或 10–20% 折扣 —— 注册后在我们网站按以下格式发布用户名:"SEND ME BONUS, MY USERNAME IS..." —— 并将其发布在我们的论坛主题中! 3. 获取 SMM 面板首个试用奖励 $1 —— 只需在我们网站(支持)提交标题为 “Get Trial Bonus” 的工单。 4. 每周在我们的 Telegram 频道和星星购买机器人中赠送 Telegram 星星! 新闻: ➡ Telegram 频道:https://t.me/accsforyou_shop ➡ WhatsApp 频道:https://chat.whatsapp.com/K8rBy500nA73z27PxgaJUw?mode=ems_copy_t ➡ Discord 服务器:https://discord.gg/y9AStFFsrh 联系方式与支持: ➡ Telegram:https://t.me/socnet_support ➡ WhatsApp:https://wa.me/79051904467 ➡ Discord:socnet_support ➡ ✉ 邮箱:solomonbog@socnet.store
    • 亲爱的朋友们,我们很高兴地宣布一个好消息—— 我们正式推出我们的新机器人,用于购买和租用虚拟号码以接收来自任何服务的短信!您是否已经厌倦了其他平台上重复使用或转售的号码? 试试我们的解决方案! 我们的服务允许您接收来自任何主流热门服务的短信。 服务列表包含超过 200 个项目! 短信接收国家:美国 (+1)。我们使用真实的美国实体号码——从未在其他平台上使用过! 目前仅提供短期租用。每个电话号码的租用时长会在购买时显示。 每个号码的价格也会在购买前显示在对应的服务旁。 您还可以为所选服务的号码接收额外短信(接收额外短信无需额外费用)。 若要快速找到所需服务,可使用便捷的搜索功能——只需输入您所需服务的名称即可获得激活号码。 可用支付方式:加密货币、银行卡,以及从我们其他机器人转移余额。 感谢您的信任! 有效链接: 虚拟号码服务:前往 数字商品商店(网站):前往 商店 Telegram 机器人:前往 – 通过 Telegram 信使方便地访问商店。 Telegram 星星购买机器人:前往 – 在 Telegram 中快速且优惠地购买星星。 SMM 面板:前往 – 推广您的社交媒体账户。 我们向您展示当前的促销与特别优惠,用于购买我们平台的商品和服务: 1. 使用促销码 OCTOBER2025(8% 折扣)在十月份于我们的商店(网站、机器人)购物!您也可以使用首次购买促销码:SOCNET(15% 折扣) 2. 获得 $1 商店余额或 10–20% 折扣 —— 注册后在我们网站按以下格式发布用户名:"SEND ME BONUS, MY USERNAME IS..." —— 并将其发布在我们的论坛主题中! 3. 获取 SMM 面板首个试用奖励 $1 —— 只需在我们网站(支持)提交标题为 “Get Trial Bonus” 的工单。 4. 每周在我们的 Telegram 频道和星星购买机器人中赠送 Telegram 星星! 新闻: ➡ Telegram 频道:https://t.me/accsforyou_shop ➡ WhatsApp 频道:https://chat.whatsapp.com/K8rBy500nA73z27PxgaJUw?mode=ems_copy_t ➡ Discord 服务器:https://discord.gg/y9AStFFsrh 联系方式与支持: ➡ Telegram:https://t.me/socnet_support ➡ WhatsApp:https://wa.me/79051904467 ➡ Discord:socnet_support ➡ ✉ 邮箱:solomonbog@socnet.store
  • Topics

×
×
  • Create New...

AdBlock Extension Detected!

Our website is made possible by displaying online advertisements to our members.

Please disable AdBlock browser extension first, to be able to use our community.

I've Disabled AdBlock