/*
 * Copyright (C) 2005-2008 L2J_JP / 2008-2013 L2J-SFJP
 * 
 * This file is part of L2J Server.
 * 
 * L2J Server 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 Server 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 ai.individual;

import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javolution.util.FastList;
import ai.npc.AbstractNpcAI;

import com.l2jserver.Config;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.datatables.DoorTable;
import com.l2jserver.gameserver.datatables.SkillTable;
import com.l2jserver.gameserver.datatables.SpawnTable;
import com.l2jserver.gameserver.instancemanager.GrandBossManager;
import com.l2jserver.gameserver.instancemanager.ZoneManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.L2Party;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.StatsSet;
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.skills.AbnormalVisualEffect;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.quest.Quest;
import com.l2jserver.gameserver.model.skills.L2Skill;
import com.l2jserver.gameserver.model.zone.L2ZoneType;
import com.l2jserver.gameserver.model.zone.type.L2BossZone;
import com.l2jserver.gameserver.model.zone.type.L2ScriptZone;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.clientpackets.Say2;
import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo.NpcInfo;
import com.l2jserver.gameserver.network.serverpackets.CreatureSay;
import com.l2jserver.gameserver.network.serverpackets.Earthquake;
import com.l2jserver.gameserver.network.serverpackets.ExShowScreenMessage;
import com.l2jserver.gameserver.network.serverpackets.ExShowScreenMessageNPCString;
import com.l2jserver.gameserver.network.serverpackets.FlyToLocation;
import com.l2jserver.gameserver.network.serverpackets.FlyToLocation.FlyType;
import com.l2jserver.gameserver.network.serverpackets.MagicSkillCanceld;
import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
import com.l2jserver.gameserver.network.serverpackets.NpcSay;
import com.l2jserver.gameserver.network.serverpackets.SocialAction;
import com.l2jserver.gameserver.network.serverpackets.SpecialCamera;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.network.serverpackets.ValidateLocation;
import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;

/**
 * Frintezza AI
 * 
 * @author Darki699
 * @author SANDMAN L2J_JP(modified)
 * @author JOJO
 * 
 * Update by rocknow
 * Updated by L2jOff team
 */
public class Frintezza extends AbstractNpcAI
{
	private static final long Frintezza_Wait_Time = 180000;	//3 min.
	private static final long Frintezza_Despawn_Time = 900000;	//15 min.
	private static final int ROOM_FINAL_TIME = 2100000;	//35 min.
	private static final boolean MODS_ALONE_PLAYER = true;	//少人数用
	private static final boolean MODS_GUIDE_EFFECT = true;	//部屋使用中
	private static final boolean MODS_REMOVE_ITEMS_ON_GROUND = true;	//地面に落ちたアイテムを掃除する
	
	// Music title (skill 5007 & 5008)
	static final int[] MUSIC_TITLE = {
		0,															//0
		1000522, /*憎悪のレクイエム*//*Requiem of Hatred       */	//1 Healing Rhapsody
		1000524, /*疾走のトッカータ*//*Frenetic Toccata        */	//2 Rampaging Opus
		1000527, /*孤独のロンド    *//*Rondo of Solitude       */	//3 Power Concerto
		1000523, /*狂喜のフーガ    *//*Fugue of Jubilation     */	//4 Plagued Concerto
		1000525, /*魅惑のマズルカ  *//*Hypnotic Mazurka        */	//5 Psycho Symphony
	};

	private static final int[][] _invadeLoc =
	{
		{ -87914, -141317, -9170 },
		{ -88781, -142162, -9170 },
		{ -87013, -142211, -9170 },
		{ -87820, -141468, -9170 },
		{ -88003, -141398, -9170 },
		{ -88753, -140439, -9170 }
	};
	
	// [0]skillId, [1]level, [1]chance%
	private static final int[][] DAEMON1_SKILLS =
	{
		{ 5014, 1 ,100},	//デーモン1 一般攻撃。
		{ 5015, 1 ,5  },	//デーモン1 突撃。
		{ 5015, 4 ,5  },	//デーモン1 突撃。
	};
	private static final int[][] DAEMON2_SKILLS =
	{
		{ 5014, 2 ,100},	//デーモン2 一般攻撃。
		{ 5015, 2 ,5  },	//デーモン2 突撃。
		{ 5015, 5 ,5  },	//デーモン2 突撃。
		{ 5018, 1 ,10 },	//デーモン2 魔法陣。
		{ 5016, 1 ,10 },	//スカーレットの束縛
	};
	private static final int[][] DAEMON3_SKILLS =
	{
		{ 5014, 3 ,100},	//デーモン3 一般攻撃。
		{ 5015, 3 ,5  },	//デーモン3 突撃。
		{ 5015, 6 ,5  },	//デーモン3 突撃。
		{ 5018, 2 ,10 },	//デーモン3 魔法陣。
		{ 5019, 1 ,10 },	//デーモン3 範囲HP吸収。
		{ 5016, 1 ,10 },	//スカーレットの束縛
	};
	
	private static final int[][] ROOM1_SPAWN =
	{
		{ 18328,-89122,-141297,-9172,243 },
		{ 18328,-87921,-142557,-9172,16216 },
		{ 18328,-87905,-140111,-9172,49043 },
		{ 18328,-86672,-141320,-9172,32847 },
		{ 18330,-88527,-141505,-9199,63565 },
		{ 18330,-88518,-141002,-9172,58498 },
		{ 18330,-87651,-142023,-9172,22424 },
		{ 18330,-87446,-140862,-9172,31968 },
		{ 18330,-87403,-141457,-9172,31471 },
		{ 18332,-88396,-141259,-9172,4588 },
		{ 18332,-88386,-141618,-9172,62454 },
		{ 18332,-88261,-140891,-9172,57892 },
		{ 18332,-88193,-141966,-9172,2411 },
		{ 18332,-88016,-140689,-9172,54718 },
		{ 18332,-87529,-140833,-9172,33861 },
		{ 18332,-87499,-141749,-9172,21893 },
		{ 18332,-87440,-141400,-9172,31176 },
		{ 18332,-87416,-141119,-9199,35927 },
	};
	private static final int[][] ROOM1_SPAWN2 =
	{
		{ 18329,-88535,-141321,-9172,61312 },
		{ 18329,-88477,-140956,-9172,59524 },
		{ 18329,-88432,-141664,-9172,3041 },
		{ 18329,-88243,-140698,-9172,51115 },
		{ 18329,-88239,-141928,-9172,12588 },
		{ 18329,-87431,-141788,-9172,21704 },
		{ 18329,-87393,-140849,-9172,40141 },
		{ 18329,-87272,-141518,-9172,29202 },
		{ 18329,-87247,-141173,-9172,29572 },
	};
	private static final int[][] ROOM1_SPAWN3 =
	{
		{ 18333,-88155,-141289,-9172,383 },
		{ 18333,-88144,-141739,-9172,8041 },
		{ 18333,-88118,-140946,-9172,51856 },
		{ 18333,-87594,-140967,-9172,42878 },
		{ 18333,-87556,-141633,-9172,27311 },
		{ 18333,-87533,-141319,-9172,30947 },
	};
	private static final int[][] ROOM1_SPAWN4 =
	{
		{ 18331,-88501,-141462,-9172,6971 },
		{ 18331,-88500,-141068,-9199,3142 },
		{ 18331,-88320,-141953,-9172,6757 },
		{ 18331,-88250,-140780,-9199,60827 },
		{ 18331,-87543,-140599,-9172,37147 },
		{ 18331,-87523,-141783,-9172,34503 },
		{ 18331,-87448,-140932,-9199,41661 },
		{ 18331,-87432,-141541,-9172,31729 },
	};
	private static final int[][] ROOM2_SPAWN =
	{
		{ 18339,-88124,-146870,-9188,50849 },
		{ 18339,-88058,-147098,-9188,7459 },
		{ 18339,-87888,-147083,-9188,21495 },
		{ 18339,-87771,-146844,-9188,41760 },
	};
	private static final int[][] ROOM2_SPAWN2 =
	{
		{ 18334,-88752,-146807,-9137,1646 },
		{ 18334,-88751,-146934,-9137,441 },
		{ 18334,-88749,-147167,-9137,0 },
		{ 18334,-88745,-147293,-9137,65382 },
		{ 18334,-87149,-146933,-9138,32537 },
		{ 18334,-87148,-147168,-9138,32768 },
		{ 18334,-87147,-146763,-9138,32315 },
		{ 18334,-87145,-147295,-9138,33007 },
		{ 18335,-88942,-146095,-9172,8353 },
		{ 18335,-88888,-147980,-9172,5345 },
		{ 18335,-88835,-147822,-9172,65135 },
		{ 18335,-88825,-146259,-9172,6947 },
		{ 18335,-87157,-146167,-9199,24103 },
		{ 18335,-87092,-147944,-9172,38710 },
		{ 18335,-87069,-146011,-9172,22449 },
		{ 18335,-86920,-148002,-9172,42205 },
		{ 18336,-88581,-145790,-9172,65215 },
		{ 18336,-88576,-148226,-9172,417 },
		{ 18336,-88573,-148398,-9172,1094 },
		{ 18336,-88553,-148342,-9172,286 },
		{ 18336,-88551,-145731,-9172,174 },
		{ 18336,-88551,-148284,-9172,2604 },
		{ 18336,-88548,-148167,-9172,316 },
		{ 18336,-88547,-145848,-9172,65353 },
		{ 18336,-88547,-145906,-9172,166 },
		{ 18336,-88524,-148399,-9172,394 },
		{ 18336,-88523,-145961,-9172,0 },
		{ 18336,-88519,-145788,-9199,417 },
		{ 18336,-88517,-148225,-9172,0 },
		{ 18336,-88495,-148341,-9172,316 },
		{ 18336,-88493,-148167,-9172,128 },
		{ 18336,-88492,-145905,-9199,65027 },
		{ 18336,-88492,-148285,-9172,0 },
		{ 18336,-88490,-145730,-9172,64735 },
		{ 18336,-88489,-145847,-9199,65062 },
		{ 18336,-87414,-148400,-9172,33104 },
		{ 18336,-87412,-148227,-9172,33184 },
		{ 18336,-87407,-145792,-9172,33234 },
		{ 18336,-87407,-145962,-9172,32851 },
		{ 18336,-87387,-145905,-9172,33346 },
		{ 18336,-87384,-145848,-9172,32896 },
		{ 18336,-87384,-148344,-9172,32768 },
		{ 18336,-87381,-148171,-9172,33594 },
		{ 18336,-87380,-145734,-9172,32065 },
		{ 18336,-87377,-148286,-9172,33057 },
		{ 18336,-87356,-145790,-9172,33057 },
		{ 18336,-87355,-148399,-9172,32768 },
		{ 18336,-87353,-148226,-9172,32768 },
		{ 18336,-87352,-145963,-9172,32676 },
		{ 18336,-87329,-148286,-9172,32520 },
		{ 18336,-87325,-148344,-9172,32961 },
		{ 18336,-87324,-145733,-9172,33202 },
		{ 18336,-87324,-145849,-9172,32768 },
		{ 18336,-87323,-145908,-9172,32994 },
		{ 18336,-87323,-148167,-9172,32622 },
		{ 18337,-89179,-147660,-9172,58363 },
		{ 18337,-89149,-146401,-9172,64055 },
		{ 18337,-89133,-147773,-9172,64764 },
		{ 18337,-89100,-146311,-9172,7099 },
		{ 18337,-89076,-147603,-9172,58998 },
		{ 18337,-89070,-147713,-9172,58038 },
		{ 18337,-89045,-146476,-9172,14768 },
		{ 18337,-89024,-146369,-9172,9438 },
		{ 18337,-88984,-147643,-9172,59041 },
		{ 18337,-88952,-146403,-9172,5827 },
		{ 18337,-87002,-146451,-9172,26398 },
		{ 18337,-86955,-147652,-9172,43290 },
		{ 18337,-86920,-146358,-9172,24719 },
		{ 18337,-86847,-147731,-9172,37672 },
		{ 18337,-86844,-146250,-9172,32315 },
		{ 18337,-86842,-147606,-9172,41760 },
		{ 18337,-86819,-146435,-9172,27617 },
		{ 18337,-86771,-147825,-9172,40275 },
		{ 18337,-86767,-146353,-9172,28435 },
		{ 18337,-86724,-147710,-9172,42225 },
		{ 18338,-89002,-147906,-9172,11874 },
		{ 18338,-88983,-146198,-9172,10425 },
		{ 18338,-88921,-147798,-9172,49152 },
		{ 18338,-88901,-146264,-9172,9611 },
		{ 18338,-88872,-146172,-9172,5345 },
		{ 18338,-88869,-147880,-9172,51316 },
		{ 18338,-87104,-146103,-9172,24270 },
		{ 18338,-87081,-146177,-9172,18061 },
		{ 18338,-87000,-147975,-9172,39533 },
		{ 18338,-86975,-146112,-9172,25420 },
		{ 18338,-86945,-147827,-9172,39163 },
		{ 18338,-86862,-147897,-9172,36345 }
	};
	
	//NPC
	private static final int SCARLET1 = 29046;
	private static final int SCARLET2 = 29047;
	private static final int FRINTEZZA = 29045;
	private static final int GUIDE = 32011;
	private static final int CUBE = 29061;
	private static final int ALARM_DEVICE = 18328;
	private static final int CHOIR_CAPTAIN = 18334;
	private static final int CHOIR_PRAYER  = 18339;
	
	//Items
	private static final int FRINTEZZA_SCROLL = 8073;	//Frintezza's Magic Force Field Removal Scroll.
	private static final int SOUL_BREAKING_ARROW = 8192;	//Soul Breaking Arrow 破魂の矢
	private static final int DEWDROP_OF_DESTRUCTION = 8556;	//Dewdrop of Destruction 破魂の露
	
	//Frintezza Status Tracking :
	private static final byte DORMANT = 0;		//Frintezza is spawned and no one has entered yet. Entry is unlocked
	private static final byte WAITING = 1;		//Frintezza is spawend and someone has entered, triggering a 30 minute window for additional people to enter
	//before he unleashes his attack. Entry is unlocked
	private static final byte FIGHTING = 2; 	//Frintezza is engaged in battle, annihilating his foes. Entry is locked
	private static final byte DEAD = 3;			//Frintezza has been killed. Entry is locked
	
	private static long _lastAction = 0;
	private static long _timeLimit;
	private static boolean _isReachToHall = false;
	private static int _songLevel = 0;			//between 1 and 5
	private static boolean _isInSocialAction = false;	//true is playing SpecialCamera
	private static int _scarletType = 0;		//between 1 and 3
	
	private static int _cameraAngle = 0;
	private static int _scarlet_x = 0;
	private static int _scarlet_y = 0;
	private static int _scarlet_z = 0;
	private static int _scarlet_h = 0;
	private static double _scarlet_hp = 0;	//[r7555]
	private static double _scarlet_mp = 0;	//[r7555]
	
	private static int _killHallAlarmDevice = 0;
	private static int _killDarkChoirPlayer = 0;
	private static int _killDarkChoirCaptain = 0;
	
	protected static L2BossZone _zone;
	private static L2ScriptZone _hall;
	
	private L2GrandBossInstance frintezza, weakScarlet, strongScarlet, activeScarlet;
	private L2MonsterInstance ghost1, ghost2, ghost3, ghost4, portrait1, portrait2, portrait3, portrait4;
	private L2Npc _frintezzaDummy, _overheadDummy, _portraitDummy1, _portraitDummy3, _scarletDummy;
	private L2Npc _guide;
	private static List<L2PcInstance> _playersInside = new FastList<>();
	private static List<L2Npc> _room1Mobs = new FastList<>();
	private static List<L2Npc> _room2Mobs = new FastList<>();
	
	// Boss: Frintezza
	private Frintezza(int id, String name, String descr)
	{
		super(id,name,descr);
		int[] mob = {SCARLET1, SCARLET2, FRINTEZZA, ALARM_DEVICE, 18329, 18330, 18331, 18332, 18333, CHOIR_CAPTAIN, 18335, 18336, 18337, 18338, CHOIR_PRAYER, 29048, 29049, 29050, 29051};
		_zone = GrandBossManager.getInstance().getZone(-87784, -153298, -9181);
		_hall = (L2ScriptZone)ZoneManager.getInstance().getZoneById(12911);	//zone.xml <zone id="12911" type="ScriptZone" shape="Cuboid" ... />

		registerMobs(mob);
		addStartNpc(GUIDE, CUBE);
		addTalkId(GUIDE, CUBE);
		
		addEnterZoneId(12911);
		StatsSet info = GrandBossManager.getInstance().getStatsSet(FRINTEZZA);
		int status = GrandBossManager.getInstance().getBossStatus(FRINTEZZA);
		if (status == DEAD)
		{
			long temp = (info.getLong("respawn_time") - System.currentTimeMillis());
			if (temp > 0)
				startQuestTimer("frintezza_unlock", temp, null, null);
			else
				this/*GrandBossManager.getInstance()*/.setBossStatus(FRINTEZZA,DORMANT);
		}
		else if (status != DORMANT)
			this/*GrandBossManager.getInstance()*/.setBossStatus(FRINTEZZA,DORMANT);
		
 //		//tempfix for messed door cords
 //		for (int i = 0; i < 8; i++)
 //			DoorTable.getInstance().getDoor(17130051+i).setRange(0, 0, 0, 0, 0, 0);
	}
	
	@Override
	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
	{
		if (event == "waiting")
		{
			startQuestTimer("close", 27000, null, null);
			startQuestTimer("camera_1", 30000, null, null);
			_zone.broadcastPacket(new Earthquake(-87784, -153298, -9181, 45, 27));
		}
		else if (event == "room1_spawn")
		{
			for (int[] loc : ROOM1_SPAWN)
				_room1Mobs.add(addSpawn(loc[0], loc[1], loc[2], loc[3], loc[4], false, 0));
		}
		else if (event == "room1_spawn2")
		{
			for (int[] loc : ROOM1_SPAWN2)
				_room1Mobs.add(addSpawn(loc[0], loc[1], loc[2], loc[3], loc[4], false, 0));
		}
		else if (event == "room1_spawn3")
		{
			for (int[] loc : ROOM1_SPAWN3)
				_room1Mobs.add(addSpawn(loc[0], loc[1], loc[2], loc[3], loc[4], false, 0));
		}
		else if (event == "room1_spawn4")
		{
			for (int[] loc : ROOM1_SPAWN4)
				_room1Mobs.add(addSpawn(loc[0], loc[1], loc[2], loc[3], loc[4], false, 0));
		}
		else if (event == "room2_spawn")
		{
			for (int[] loc : ROOM2_SPAWN)
				_room2Mobs.add(addSpawn(loc[0], loc[1], loc[2], loc[3], loc[4], false, 0));
		}
		else if (event == "room2_spawn2")
		{
			for (int[] loc : ROOM2_SPAWN2)
				_room2Mobs.add(addSpawn(loc[0], loc[1], loc[2], loc[3], loc[4], false, 0));

			for (L2Npc actor : _room2Mobs)
				if (actor.getId() == CHOIR_CAPTAIN)
				{
					actor.broadcastPacket(new NpcSay(actor.getObjectId(), Say2.SHOUT, actor.getId(), 1010644/*黒聖歌団のステージをぶち壊すとは．．．許せぬ！*/));
					break;
				}
		}
		else if (event == "room1_open")
		{
			DoorTable.getInstance().getDoor(17130042).openMe();
			DoorTable.getInstance().getDoor(17130043).openMe();
			DoorTable.getInstance().getDoor(17130045).openMe();
			DoorTable.getInstance().getDoor(17130046).openMe();
			
			for (int i = 17130051; i <= 17130058; i++)
				DoorTable.getInstance().getDoor(i).openMe();
		}
		else if (event == "room2_close")
		{
			DoorTable.getInstance().getDoor(17130042).closeMe();
			DoorTable.getInstance().getDoor(17130043).closeMe();
			DoorTable.getInstance().getDoor(17130045).closeMe();
			DoorTable.getInstance().getDoor(17130046).closeMe();
			
			for (int i = 17130061; i <= 17130070; i++)
				DoorTable.getInstance().getDoor(i).openMe();
		}
		else if (event == "room2_open")
		{
			DoorTable.getInstance().getDoor(17130042).openMe();
			DoorTable.getInstance().getDoor(17130043).openMe();
			DoorTable.getInstance().getDoor(17130045).openMe();
			DoorTable.getInstance().getDoor(17130046).openMe();
		}
		else if (event == "room1_del")
		{
			for (L2Npc mob : _room1Mobs)
			{
				if (mob != null)
					mob.deleteMe();
			}
			_room1Mobs.clear();
		}
		else if (event == "room2_del")
		{
			for (L2Npc mob : _room2Mobs)
			{
				if (mob != null)
					mob.deleteMe();
			}
			_room2Mobs.clear();
		}
		else if (event == "room3_del")
		{
			if (ghost1 != null)
				{ ghost1.getSpawn().stopRespawn(); ghost1.deleteMe(); ghost1 = null;}
			if (ghost2 != null)
				{ ghost2.getSpawn().stopRespawn(); ghost2.deleteMe(); ghost2 = null; }
			if (ghost3 != null)
				{ ghost3.getSpawn().stopRespawn(); ghost3.deleteMe(); ghost3 = null; }
			if (ghost4 != null)
				{ ghost4.getSpawn().stopRespawn(); ghost4.deleteMe(); ghost4 = null; }
			if (portrait1 != null)
				{ portrait1.deleteMe(); portrait1 = null; }
			if (portrait2 != null)
				{ portrait2.deleteMe(); portrait2 = null; }
			if (portrait3 != null)
				{ portrait3.deleteMe(); portrait3 = null; }
			if (portrait4 != null)
				{ portrait4.deleteMe(); portrait4 = null; }
			if (frintezza != null)
				{ frintezza.deleteMe(); frintezza = null; }
			if (weakScarlet != null)
				{ weakScarlet.deleteMe(); weakScarlet = null; }
			if (strongScarlet != null)
				{ strongScarlet.deleteMe(); strongScarlet = null; }
			
			activeScarlet = null;
		}
		else if (event == "clean")
		{
			_lastAction = 0;
			_isInSocialAction = false;
			_scarletType = 0;
			_killHallAlarmDevice = 0;
			_killDarkChoirPlayer = 0;
			_killDarkChoirCaptain = 0;
			_playersInside.clear();
		}
		else if (event == "close")
		{
			for (int i = 17130051; i <= 17130058; i++)
				DoorTable.getInstance().getDoor(i).closeMe();
			for (int i = 17130061; i <= 17130070; i++)
				DoorTable.getInstance().getDoor(i).closeMe();
			
			DoorTable.getInstance().getDoor(17130042).closeMe();
			DoorTable.getInstance().getDoor(17130043).closeMe();
			DoorTable.getInstance().getDoor(17130045).closeMe();
			DoorTable.getInstance().getDoor(17130046).closeMe();
		}
		else if (event == "loc_check")
		{
			if (GrandBossManager.getInstance().getBossStatus(FRINTEZZA) == FIGHTING)
			{
				if (!_hall.isInsideZone(npc) || npc.getX() < -90084 || npc.getX() > -85484 || npc.getY() < -155598 || npc.getY() > -150998 || npc.getZ() < -9195)
				{
					npc.enableAllSkills();
					npc.setIsImmobilized(false);
					npc.teleToLocation(-87784,-153298,-9181);
				}
			}
		}
		else if (event == "camera_1")
		{
			/********************/
			/* START 1ST SPAWN  */
			/********************/
			_isInSocialAction = true;
			_scarletType = 1;
			this/*GrandBossManager.getInstance()*/.setBossStatus(FRINTEZZA,FIGHTING);

			//"camera_3"から引越し
			frintezza = (L2GrandBossInstance) addSpawn(FRINTEZZA,-87776,-155083,-9087,16048,false,0);
			GrandBossManager.getInstance().addBoss(frintezza);
			frintezza.setIsInvul(true);
			frintezza.setIsImmobilized(true);
			_zone.updateKnownList(frintezza);

			//"camera_18b"から引越し
			weakScarlet = (L2GrandBossInstance) addSpawn(29046, -88595, -153695, -7446, 16384, false, 0, true);	//[JOJO]
			weakScarlet.setIsInvul(true);
			weakScarlet.setIsImmobilized(true);
			weakScarlet.disableAllSkills();
			_zone.updateKnownList(weakScarlet);
			activeScarlet = weakScarlet;
			
			_frintezzaDummy = addSpawn(29052, -87776, -155083, -9087, 16048,false,0);
			_frintezzaDummy.setIsInvul(true);
			_frintezzaDummy.setIsImmobilized(true);
			
			_overheadDummy = addSpawn(29052, -87784, -153298, -9175, 16384,false,0);	//[r7556]
			_overheadDummy.setIsInvul(true);
			_overheadDummy.setIsImmobilized(true);
			_overheadDummy.setCollisionHeight(600);
			_hall.broadcastPacket(new NpcInfo(_overheadDummy, null));
			
			_portraitDummy1 = addSpawn(29052, -89566, -153168, -9165, 16048,false,0);	//[r7556]
			_portraitDummy1.setIsImmobilized(true);
			_portraitDummy1.setIsInvul(true);
			
			_portraitDummy3 = addSpawn(29052, -86004, -153168, -9165, 16048,false,0);	//[r7556]
			_portraitDummy3.setIsImmobilized(true);
			_portraitDummy3.setIsInvul(true);
			
			_scarletDummy = addSpawn(29053, -87784, -153298, -9175, 16384,false,0);	//[r7556]
			_scarletDummy.setIsInvul(true);
			_scarletDummy.setIsImmobilized(true);
			
			startQuestTimer("stop_pc", 0, null, null);
			startQuestTimer("camera_2", 1000, _overheadDummy, null);
		}
		else if (event == "camera_2")
		{
			_hall.broadcastPacket(new SpecialCamera(_overheadDummy.getObjectId(),0, 75, -89, 0, 100,0,0,1,0));
			startQuestTimer("camera_2b", 0, _overheadDummy, null);
		}
		else if (event == "camera_2b")
		{
			_hall.broadcastPacket(new SpecialCamera(_overheadDummy.getObjectId(),0, 75, -89, 0, 100,0,0,1,0));
			startQuestTimer("camera_3", 0, _overheadDummy, null);
		}
		else if (event == "camera_3")
		{
			_hall.broadcastPacket(new SpecialCamera(_overheadDummy.getObjectId(),300, 90, -10, 6500, 7000,0,0,1,0));
			
			ghost2 = (L2MonsterInstance) addSpawnGhost(29051, -86140, -153991, -9165, 28205,false,0);	//[r7556]
			ghost2.setIsImmobilized(true);
			ghost2.disableAllSkills();
			_zone.updateKnownList(ghost2);
			
			ghost3 = (L2MonsterInstance) addSpawnGhost(29051, -89408, -153980, -9165, 64817,false,0);	//[r7556]
			ghost3.setIsImmobilized(true);
			ghost3.disableAllSkills();
			_zone.updateKnownList(ghost3);
			
			ghost1 = (L2MonsterInstance) addSpawnGhost(29050, -86183, -152443, -9165, 35048,false,0);	//[r7556]
			ghost1.setIsImmobilized(true);
			ghost1.disableAllSkills();
			_zone.updateKnownList(ghost1);
			
			ghost4 = (L2MonsterInstance) addSpawnGhost(29050, -89382, -152443, -9165, 57730,false,0);	//[r7556]
			ghost4.setIsImmobilized(true);
			ghost4.disableAllSkills();
			_zone.updateKnownList(ghost4);
			
			startQuestTimer("camera_4", 6500, _overheadDummy, null);
		}
		else if (event == "camera_4")
		{
			_hall.broadcastPacket(new SpecialCamera(_frintezzaDummy.getObjectId(),1800, 90, 8, 6500, 7000,0,0,1,0));
			startQuestTimer("camera_5", 900, _frintezzaDummy, null);
		}
		else if (event == "camera_5")
		{
			_hall.broadcastPacket(new SpecialCamera(_frintezzaDummy.getObjectId(),140, 90, 10, 2500, 4500,0,0,1,0));
			startQuestTimer("camera_5b", 4000, _frintezzaDummy, null);
		}
		else if (event == "camera_5b")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),40, 75, -10, 0, 1000,0,0,1,0));
			startQuestTimer("camera_6", 0, frintezza, null);
		}
		else if (event == "camera_6")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),40, 75, -10, 0, 12000,0,0,1,0));
			startQuestTimer("camera_7", 1350, frintezza, null);
		}
		else if (event == "camera_7")
		{
			_hall.broadcastPacket(new SocialAction(frintezza,2));
			startQuestTimer("camera_8", 7000, frintezza, null);
		}
		else if (event == "camera_8")
		{
			startQuestTimer("camera_9", 1000, frintezza, null);
			_frintezzaDummy.deleteMe();
			_frintezzaDummy = null;
		}
		else if (event == "camera_9")
		{
			_hall.broadcastPacket(new SocialAction(ghost2,1));
			_hall.broadcastPacket(new SocialAction(ghost3,1));
			startQuestTimer("camera_9b", 400, frintezza, null);
		}
		else if (event == "camera_9b")
		{
			_hall.broadcastPacket(new SocialAction(ghost1,1));
			_hall.broadcastPacket(new SocialAction(ghost4,1));
			
			SpecialCamera cam1 = new SpecialCamera(_portraitDummy1.getObjectId(),1000, 118, 0, 0, 1000,0,0,1,0);
			SpecialCamera cam3 = new SpecialCamera(_portraitDummy3.getObjectId(),1000, 62, 0, 0, 1000,0,0,1,0);
			for (L2Character pc : _hall.getCharactersInside())
			{
				if (pc instanceof L2PcInstance)
				{
					if (pc.getX() < -87784)
						pc.sendPacket(cam1);
					else
						pc.sendPacket(cam3);
				}
			}
			startQuestTimer("camera_9c", 0, frintezza, null);
		}
		else if (event == "camera_9c")
		{
			SpecialCamera cam1 = new SpecialCamera(_portraitDummy1.getObjectId(),1000, 118, 0, 0, 10000,0,0,1,0);
			SpecialCamera cam3 = new SpecialCamera(_portraitDummy3.getObjectId(),1000, 62, 0, 0, 10000,0,0,1,0);
			for (L2Character pc : _hall.getCharactersInside())
			{
				if (pc instanceof L2PcInstance)
				{
					if (pc.getX() < -87784)
						pc.sendPacket(cam1);
					else
						pc.sendPacket(cam3);
				}
			}
			startQuestTimer("camera_10", 2000, frintezza, null);
		}
		else if (event == "camera_10")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),240, 90, 0, 0, 1000,0,0,1,0));
			startQuestTimer("camera_11", 0, frintezza, null);
		}
		else if (event == "camera_11")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),240, 90, 25, 5500, 10000,0,0,1,0));
			_hall.broadcastPacket(new SocialAction(frintezza,3));
			_portraitDummy1.deleteMe();
			_portraitDummy3.deleteMe();
			_portraitDummy1 = null;
			_portraitDummy3 = null;
			
			startQuestTimer("camera_12", 4500, frintezza, null);
		}
		else if (event == "camera_12")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),100, 195, 35, 0, 10000,0,0,1,0));
			startQuestTimer("camera_13", 700, frintezza, null);
		}
		else if (event == "camera_13")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),100, 195, 35, 0, 10000,0,0,1,0));
			startQuestTimer("camera_14", 1300, frintezza, null);
		}
		else if (event == "camera_14")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),120, 180, 45, 1500, 10000,0,0,1,0));
			_hall.broadcastPacket(new MagicSkillUse(frintezza, frintezza, 5006, 1, 34000, 0));
			startQuestTimer("camera_16", 1500, frintezza, null);
		}
		else if (event == "camera_16")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),520, 135, 45, 8000, 10000,0,0,1,0));
			startQuestTimer("camera_17", 7500, frintezza, null);
		}
		else if (event == "camera_17")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),1500, 110, 25, 10000, 13000,0,0,1,0));
			startQuestTimer("camera_18", 9500, frintezza, null);
		}
		else if (event == "camera_18")
		{
			_hall.broadcastPacket(new SpecialCamera(_overheadDummy.getObjectId(),930, 160, -20, 0, 1000,0,0,1,0));
			startQuestTimer("camera_18b", 0, _overheadDummy, null);
		}
		else if (event == "camera_18b")
		{
			_hall.broadcastPacket(new SpecialCamera(_overheadDummy.getObjectId(), 600, 180, -25, 0, 10000,0,0,1,0));	//[r7556]
			_hall.broadcastPacket(new MagicSkillUse(_scarletDummy,_overheadDummy, 5004, 1, 5800, 0));	//[r7556]
			
			startQuestTimer("camera_19",  2400, _scarletDummy, null);	//[r7556]
			startQuestTimer("camera_19b", 5000, _scarletDummy, null);	//[r7556]
			startQuestTimer("camera_19c", 6300, _scarletDummy, null);	//+[JOJO]
		}
		else if (event == "camera_19")
		{
			weakScarlet.teleToLocation(-87784, -153298, -9175);	//[r7556]
		}
		else if (event == "camera_19b")
		{
			_hall.broadcastPacket(new SpecialCamera(_scarletDummy.getObjectId(), 800, 180, 10, 1000, 10000,0,0,1,0));	//[r7556]
			startQuestTimer("camera_20", 3500/*2100*/, _scarletDummy, null);	//[r7556]
		}
		else if (event == "camera_19c")	//+[JOJO]
		{
			throwUp(npc, 500, SystemMessage.getSystemMessage(SystemMessageId.YOU_FEEL_S1_EFFECT).addSkillName(5004, 1));
		}
		else if (event == "camera_20")
		{
			_hall.broadcastPacket(new SpecialCamera(_scarletDummy/*weakScarlet*/.getObjectId(),300, 60, 8, 0, 10000,0,0,1,0));
			startQuestTimer("camera_21", 2000, weakScarlet, null);
		}
		else if (event == "camera_21")
		{
			_hall.broadcastPacket(new SpecialCamera(weakScarlet.getObjectId(),500, 90, 10, 1000/*3000*/, 5000,0,0,1,0));
			startQuestTimer("camera_22", 3000, weakScarlet, null);
		}
		else if (event == "camera_22")
		{
			portrait2 = (L2MonsterInstance) addSpawn(29049, -86140, -153991, -9191, 28205,false,0);
			_zone.updateKnownList(portrait2);
			
			portrait3 = (L2MonsterInstance) addSpawn(29049, -89408, -153980, -9191, 64817,false,0);
			_zone.updateKnownList(portrait3);
			
			portrait1 = (L2MonsterInstance) addSpawn(29048, -86183, -152443, -9191, 35048,false,0);
			_zone.updateKnownList(portrait1);
			
			portrait4 = (L2MonsterInstance) addSpawn(29048, -89382, -152443, -9191, 57730,false,0);
			_zone.updateKnownList(portrait4);
			
			_overheadDummy.deleteMe();
			_scarletDummy.deleteMe();
			_overheadDummy = null;
			_scarletDummy = null;
			
			startQuestTimer("camera_23", 2000, weakScarlet, null);
		}
		else if (event == "camera_23")
		{
			startQuestTimer("morph_end", 0, weakScarlet, null);//+[JOJO]
			startQuestTimer("start_pc", 0, weakScarlet, null);
			long delay = 10000 + Rnd.get(10000);
			startQuestTimer("songs_play", delay, frintezza, null);
			startQuestTimer("skill01", delay, weakScarlet, null);
			startQuestTimer("loc_check", 60000, weakScarlet, null, true);
			_lastAction = System.currentTimeMillis();
			startQuestTimer("frintezza_despawn", 60000, null, null, true);//+[JOJO]
			
			weakScarlet.setIsInvul(false);
			weakScarlet.setShowSummonAnimation(false);
			
			frintezza.setIsInvul(false);
			frintezza.disableAllSkills();
			
			/********************/
			/* END OF 1ST SPAWN */
			/********************/
		}
		else if (event == "stop_pc")
		{
			for (L2Character cha : _hall.getCharactersInside())
			{
				cha.abortAttack();
				cha.abortCast();
				cha.disableAllSkills();
				cha.setTarget(null);
				cha.stopMove(null);
				cha.setIsImmobilized(true);
				cha.getAI().setIntention(AI_INTENTION_IDLE);
			}
		}
		else if (event == "stop_npc")
		{
		//	int heading = npc.getHeading();
		//	if (heading < 32768)
		//		_cameraAngle = Math.abs(180 - (int)(heading / 182.044444444));
		//	else
		//		_cameraAngle = Math.abs(540 - (int)(heading / 182.044444444));
		}
		else if (event == "start_pc")
		{
			for (L2Character cha : _hall.getCharactersInside())
			{
				if (cha != frintezza)
				{
					cha.enableAllSkills();
					cha.setIsImmobilized(false);
				}
			}
		}
		else if (event == "start_npc")
		{
			npc.setRunning();
			npc.setIsInvul(false);
		}
		else if (event == "morph_end")
		{
			_isInSocialAction = false;
		}
		else if (event == "morph_01")
		{
			/********************/
			/* START 2ND MORPH  */
			/********************/
		/*	activeScarlet = weakScarlet;	*/
			weakScarlet.stopSkillEffects(false, 5008);
			_cameraAngle = calcCameraAngle(weakScarlet);
			_hall.broadcastPacket(new SpecialCamera(weakScarlet.getObjectId(),250, _cameraAngle, 12, 2000, 15000,0,0,1,0));
			startQuestTimer("morph_02", 3000, weakScarlet, null);
		}
		else if (event == "morph_02")
		{
			_hall.broadcastPacket(new SocialAction(weakScarlet,1));
			weakScarlet.setRHandId(7903);
			startQuestTimer("morph_03", 4000, weakScarlet, null);
		}
		else if (event == "morph_03")
		{
			startQuestTimer("morph_04", 1500, weakScarlet, null);
		}
		else if (event == "morph_04")
		{
			_hall.broadcastPacket(new SocialAction(weakScarlet,4));
			L2Skill skill = SkillTable.getInstance().getInfo(5017, 1);
			if (skill != null)
				skill.applyEffects(weakScarlet, weakScarlet);
			
			startQuestTimer("morph_end", 6000, weakScarlet, null);
			startQuestTimer("start_pc", 3000, weakScarlet, null);
			startQuestTimer("start_npc", 3000, weakScarlet, null);
			long delay = 10000 + Rnd.get(10000);
			startQuestTimer("songs_play", delay, frintezza, null);
			startQuestTimer("skill02", delay, weakScarlet, null);
			startQuestTimer("loc_check", 60000, weakScarlet, null, true);
			_lastAction = System.currentTimeMillis();
			startQuestTimer("frintezza_despawn", 60000, null, null, true);//+[JOJO]
			/********************/
			/* END OF 2ND MORPH */
			/********************/
		}
		else if (event == "morph_05a")
		{
			/********************/
			/* START 3RD MORPH  */
			/********************/
			_cameraAngle = calcCameraAngle(weakScarlet);
			_hall.broadcastPacket(new SocialAction(frintezza,4));
		}
		else if (event == "morph_05")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),250, 120, 15, 0, 1000,0,0,1,0));
			startQuestTimer("morph_06", 0, frintezza, null);
		}
		else if (event == "morph_06")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),250, 120, 15, 0, 10000,0,0,1,0));
			
			_scarlet_x = weakScarlet.getX();
			_scarlet_y = weakScarlet.getY();
			_scarlet_z = weakScarlet.getZ();
			_scarlet_h = weakScarlet.getHeading();
			_scarlet_hp = weakScarlet.getCurrentHp();	//[r7555]
			_scarlet_mp = weakScarlet.getCurrentMp();	//[r7555]
		//	weakScarlet.deleteMe();
		//	weakScarlet = null;
			activeScarlet = null;
		//	weakScarlet = (L2GrandBossInstance) addSpawn(29046, _Scarlet_x, _Scarlet_y, _Scarlet_z, _Scarlet_h, false,0);
			weakScarlet.setIsInvul(true);
			weakScarlet.setIsImmobilized(true);
			weakScarlet.disableAllSkills();
		//	weakScarlet.setRHandId(7903);
			_zone.updateKnownList(weakScarlet);
			
			startQuestTimer("morph_07", 4200, frintezza, null);
		}
		else if (event == "morph_07")
		{
			_hall.broadcastPacket(new MagicSkillUse(frintezza, frintezza, 5006, 1, 34000, 0));
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),500, 70, 15, 3000, 10000,0,0,1,0));
			startQuestTimer("morph_08", 3000, frintezza, null);
		}
		else if (event == "morph_08")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),2500, 90, 12, 6000, 10000,0,0,1,0));
			startQuestTimer("morph_09", 3000, frintezza, null);
		}
		else if (event == "morph_09")
		{
			_hall.broadcastPacket(new SpecialCamera(weakScarlet.getObjectId(),250, _cameraAngle, 12, 0, 1000,0,0,1,0));
			startQuestTimer("morph_10", 0, weakScarlet, null);
		}
		else if (event == "morph_10")
		{
			_hall.broadcastPacket(new SpecialCamera(weakScarlet.getObjectId(),250, _cameraAngle, 12, 0, 10000,0,0,1,0));
			startQuestTimer("morph_11", 500, weakScarlet, null);
		}
		else if (event == "morph_11")
		{
			weakScarlet.doDie(weakScarlet);
			_hall.broadcastPacket(new SpecialCamera(weakScarlet.getObjectId(),450, _cameraAngle, 14, 8000, 8000,0,0,1,0));
			
			startQuestTimer("morph_12", 6250, weakScarlet, null);
			startQuestTimer("morph_13", 7200, weakScarlet, null);
		}
		else if (event == "morph_12")
		{
			weakScarlet.deleteMe();
			weakScarlet = null;
		}
		else if (event == "morph_13")
		{
			strongScarlet = (L2GrandBossInstance) addSpawn(SCARLET2, _scarlet_x, _scarlet_y, _scarlet_z, _scarlet_h, false,0);
			strongScarlet.setIsInvul(true);
			strongScarlet.setIsImmobilized(true);
			strongScarlet.disableAllSkills();
			strongScarlet.setCurrentHpMp(_scarlet_hp, _scarlet_mp);	//[r7555]
			_zone.updateKnownList(strongScarlet);
			activeScarlet = strongScarlet;
			
			_hall.broadcastPacket(new SpecialCamera(strongScarlet.getObjectId(),450, _cameraAngle, 12, 500, 14000,0,0,1,0));
			
			startQuestTimer("morph_14", 3000, strongScarlet, null);
		}
		else if (event == "morph_14")
		{
			startQuestTimer("morph_15", 5100, strongScarlet, null);
		}
		else if (event == "morph_15")
		{
			_hall.broadcastPacket(new SocialAction(strongScarlet,2));
			L2Skill skill = SkillTable.getInstance().getInfo(5017, 1);
			if (skill != null)
				skill.applyEffects(strongScarlet, strongScarlet);
			
			startQuestTimer("morph_end", 9000, strongScarlet, null);
			startQuestTimer("start_pc", 6000, strongScarlet, null);
			startQuestTimer("start_npc", 6000, strongScarlet, null);
			long delay = 10000 + Rnd.get(10000);
			startQuestTimer("songs_play", delay, frintezza, null);
			startQuestTimer("skill03", delay, strongScarlet, null);
			startQuestTimer("loc_check", 60000, strongScarlet, null, true);
			_lastAction = System.currentTimeMillis();
			startQuestTimer("frintezza_despawn", 60000, null, null, true);//+[JOJO]
			/********************/
			/* END OF 3RD MORPH */
			/********************/
		}
		else if (event == "morph_16")
		{
			/********************/
			/*    START DIE     */
			/********************/
			_cameraAngle = calcCameraAngle(strongScarlet);
			_hall.broadcastPacket(new SpecialCamera(strongScarlet.getObjectId(),300, _cameraAngle - 180 , 5, 0, 7000,0,0,1,0));
			startQuestTimer("morph_17", 0, strongScarlet, null);
		}
		else if (event == "morph_17")
		{
			_hall.broadcastPacket(new SpecialCamera(strongScarlet.getObjectId(),200, _cameraAngle, 85, 4000, 10000,0,0,1,0));
			startQuestTimer("morph_17b", 7400, frintezza, null);
			startQuestTimer("morph_18", 7500, frintezza, null);
		}
		else if (event == "morph_17b")
		{
			frintezza.doDie(frintezza);
		}
		else if (event == "morph_18")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),100, 120, 5, 0, 7000,0,0,1,0));
			startQuestTimer("morph_19", 0, frintezza, null);
		}
		else if (event == "morph_19")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),100, 90, 5, 5000, 15000,0,0,1,0));
			startQuestTimer("morph_20", 7000, frintezza, null);
			startQuestTimer("spawn_cubes", 7000, frintezza, null);
		}
		else if (event == "morph_20")
		{
			_hall.broadcastPacket(new SpecialCamera(frintezza.getObjectId(),900, 90, 25, 7000, 10000,0,0,1,0));
			startQuestTimer("start_pc", 7000, frintezza, null);
			startQuestTimer("morph_20a", 8000, frintezza, null);	//+[JOJO]
		}
		else if (event == "morph_20a")	//+[JOJO]
		{
			// onKill()から引越し
			startQuestTimer("clean", 30000, null, null);
			startQuestTimer("close", 30000, null, null);
			startQuestTimer("room1_del", 60000, null, null);
			startQuestTimer("room2_del", 60000, null, null);
			startQuestTimer("room3_del", 60000, null, null);
			startQuestTimer("remove_players", 900000, null, null);		//15min.
			_timeLimit = System.currentTimeMillis() + 900000;			//15min.
			startQuestTimer("remove_players_countdown", 5000, null, null);	//+[JOJO]

			this/*GrandBossManager.getInstance()*/.setBossStatus(FRINTEZZA,DEAD);
			long respawnTime = (long)Config.Interval_Of_Frintezza_Spawn + Rnd.get(Config.Random_Of_Frintezza_Spawn);
			startQuestTimer("frintezza_unlock", respawnTime, npc, null);
			// also save the respawn time so that the info is maintained past reboots
			StatsSet info = GrandBossManager.getInstance().getStatsSet(FRINTEZZA);
			info.set("respawn_time", System.currentTimeMillis() + respawnTime);
			GrandBossManager.getInstance().setStatsSet(FRINTEZZA,info);

			/********************/
			/*    END OF DIE    */
			/********************/
		}
		else if (event == "songs_play")
		{
			if (frintezza != null && !frintezza.isDead() && ! _isInSocialAction)
			{
				long delay;
				if ((delay = frintezza.getCastEndTimeLeftMilli()) > 0)
				{
					startQuestTimer(event, delay + 500, npc, player);
				}
				else
				{
					if (_scarletType == 3 && strongScarlet.getCurrentHp() < strongScarlet.getMaxHp() * 0.6 && Rnd.get(100) < 80)
						_songLevel = 1;
					else
						_songLevel = Rnd.get(1, 5);
					
					L2Skill skill = SkillTable.getInstance().getInfo(5007, _songLevel);
					_hall.broadcastPacket(new MagicSkillUse(frintezza, frintezza, skill.getId(), skill.getLevel(), skill.getHitTime(), skill.getReuseDelay()));
//					frintezza.doCast(skill);
					_hall.broadcastPacket(new ExShowScreenMessageNPCString(MUSIC_TITLE[_songLevel], 10000));
			//		_hall.broadcastPacket(new ExShowScreenMessageNPCString(/*type=*/0, /*messageId=*/0, /*position=*/ExShowScreenMessageNPCString.TOP_CENTER, /*unk1=*/0, /*size=*/ExShowScreenMessageNPCString.SIZE_LARGE, /*unk2=*/0, /*unk3=*/0, /*showEffect=*/true, /*time=*/10000, /*fade=*/true, /*npcStringId=*/MUSIC_TITLE[_songLevel]));
					
					delay = skill.getHitTime() + Rnd.get(5000);
					startQuestTimer("songs_play",  delay, frintezza, null);
					startQuestTimer("songs_effect", 0, activeScarlet, null);
				}
			}
		}
		else if (event == "songs_effect")
		{
			if (npc/*==activeScarlet*/ != null && !npc.isDead() && ! _isInSocialAction)
			{
				long delay;
				if ((delay = npc.getCastEndTimeLeftMilli()) > 0)
				{
					startQuestTimer(event, delay + 500, npc, player);
				}
				else
				{
					L2Skill skill = SkillTable.getInstance().getInfo(5008, _songLevel);
					npc.doCast(skill);
				}
			}
		}
		else if (event == "stop_effect")
		{
			throw new RuntimeException();
		}
		else if (event == "attack_stop")
		{
			cancelQuestTimers("skill01");
			cancelQuestTimers("skill02");
			cancelQuestTimers("skill03");
			cancelQuestTimers("songs_play");
			cancelQuestTimers("songs_effect");

			if (frintezza != null)
				_hall.broadcastPacket(new MagicSkillCanceld(frintezza.getObjectId()));
		}
		else if (event == "check_hp")
		{
			throw new RuntimeException();
		}
		else if (event == "skill01")
		{
			if (activeScarlet != null && !activeScarlet.isDead() && _scarletType == 1 && ! _isInSocialAction)
				scarletAI_RandomSkill(event, npc, player, DAEMON1_SKILLS);
		}
		else if (event == "skill02")
		{
			if (activeScarlet != null && !activeScarlet.isDead() && _scarletType == 2 && ! _isInSocialAction)
				scarletAI_RandomSkill(event, npc, player, DAEMON2_SKILLS);
		}
		else if (event == "skill03")
		{
			if (activeScarlet != null && !activeScarlet.isDead() && _scarletType == 3 && ! _isInSocialAction)
				scarletAI_RandomSkill(event, npc, player, DAEMON3_SKILLS);
		}
		else if (event == "float_effect")
		{
			throw new RuntimeException();
		}
		else if (event == "action")
		{
			_hall.broadcastPacket(new SocialAction(npc,1));
		}
		else if (event == "bomber")
		{
			throw new RuntimeException();
		}
		else if (event == "room_final")
		{
			_zone.broadcastPacket(new NpcSay(npc.getObjectId(),1,npc.getId(),"Exceeded his time limit, challenge failed!"));
			oustAllObjects();
			
			cancelAllQuestTimers();
			startQuestTimer("clean", 1000, null, null);
			startQuestTimer("close", 1000, null, null);
			startQuestTimer("room1_del", 1000, null, null);
			startQuestTimer("room2_del", 1000, null, null);
			startQuestTimer("room3_del", 1000, null, null);
			
			this/*GrandBossManager.getInstance()*/.setBossStatus(FRINTEZZA,DORMANT);
		}
		else if (event == "frintezza_despawn")
		{
			long temp = (System.currentTimeMillis() - _lastAction);
			if (temp > Frintezza_Despawn_Time)
			{
				_timeLimit = 0;
				cancelAllQuestTimers();
				
				oustAllObjects();

				startQuestTimer("attack_stop", 1000, null, null);
				startQuestTimer("clean", 1000, null, null);
				startQuestTimer("close", 1000, null, null);
				startQuestTimer("room1_del", 1000, null, null);
				startQuestTimer("room2_del", 1000, null, null);
				startQuestTimer("room3_del", 1000, null, null);
				
				this/*GrandBossManager.getInstance()*/.setBossStatus(FRINTEZZA,DORMANT);
			}
			else if (_timeLimit != 0)
			{
				int timeLeft = (int)((_timeLimit - System.currentTimeMillis() + 29999) / 60000);
				if (timeLeft > 0)
					if (timeLeft % 5 == 0 || timeLeft <= 4)	// 30,25,20,15,10,5,4,3,2,1
						_zone.broadcastPacket(new ExShowScreenMessageNPCString(1010643/*"残り$s1分です。"*/, 10000).addNumber(timeLeft));
			}
		}
		else if (event == "spawn_cubes")
		{
			addSpawn(CUBE, -87784, -153298, -9179, 16384,false,900000);
		}
		else if (event == "frintezza_unlock")
		{
			this/*GrandBossManager.getInstance()*/.setBossStatus(FRINTEZZA,DORMANT);
		}
		else if (event == "remove_players_countdown")	//+[JOJO]
		{
			int timeLeft = (int)((_timeLimit - System.currentTimeMillis() + 29999) / 60000);
			if (timeLeft > 0)
			{
				if (timeLeft % 5 == 0 || timeLeft <= 4)	// 15,10,5,4,3,2,1
					_zone.broadcastPacket(new CreatureSay(0, Say2.SCREEN_ANNOUNCE, "ダンジョン 消滅", timeLeft + "分"));
				long delay = (_timeLimit - System.currentTimeMillis()) % 60000;
				if (delay == 0) delay = 60000;
				startQuestTimer(event, delay, null, null);
			}
		}
		else if (event == "remove_players")
		{
			oustAllObjects();
		}
		else if (MODS_ALONE_PLAYER && event.equals("invadeParty"))	/*html event*/
		{
			L2Party party;
			if ((party = player.getParty()) != null && party.isLeader(player))
			{
				List<L2PcInstance> team1 = new FastList<>();
				List<L2PcInstance> team2 = new FastList<>();
				Iterator<L2PcInstance> i = party.getMembers().iterator();
				team1.add(i.next());
				while (i.hasNext())
					team2.add(i.next());
				List<List<L2PcInstance>> cc = new FastList<>();
				cc.add(team1);
				cc.add(team2);
				return doInvade(npc, player, cc);
			}
			else
				return "<html><body>パーティーを率いるリーダーが「フリンテッサの結界解除スクロール」を持っていれば、フリンテッサ様の結界内部に入れます。</body></html>";
		}
		else if (MODS_ALONE_PLAYER && event.equals("invadePc"))	/*html event*/
		{
			List<L2PcInstance> team = new FastList<>();
			team.add(player);
			List<List<L2PcInstance>> cc = new FastList<>();
			cc.add(team);
			return doInvade(npc, player, cc);
		}
		else if (event.length() != 0)
			throw new RuntimeException(event);
		
		return super.onAdvEvent(event, npc, player);
	}
	
	@Override
	public String onTalk(L2Npc npc, L2PcInstance player)
	{
		if (npc.getId() == CUBE)
		{
			int x = 150037 + Rnd.get(500);
			int y = -57720 + Rnd.get(500);
			player.teleToLocation(x, y, -2976);
			return null;
		}
		
		String htmltext = "";
		assert npc.getId() == GUIDE;
		if (player.getParty() != null
		 && player.getParty().getCommandChannel() != null
		 && player.getParty().getCommandChannel().getLeader() == player)
		{
			if (MODS_GUIDE_EFFECT) _guide = npc;
			if (player.getParty().getCommandChannel().getPartys().size() < 4 || player.getParty().getCommandChannel().getPartys().size() > 5)	//[r7555]
			{
				htmltext = "<html><body>Your command channel needs to have at least 4 parties and a maximum of 5.</body></html>";	//[r7605]
			}
			else
			{
				List<List<L2PcInstance>> cc = new FastList<>();
				for (L2Party party : player.getParty().getCommandChannel().getPartys())
					cc.add(party.getMembers());
				htmltext = doInvade(npc, player, cc);
			}
		}
		else
		{
			if (MODS_ALONE_PLAYER)
				htmltext = "<html><body>フリンテッサ テレポーター:<br>"
					+ "1人で結界に入るのは危険です。<br>"
					+ "<font color=6699FF>連合チャンネルで入る</font><br>"
					+ "<a action=\"bypass -h Quest Frintezza invadeParty\">1パーティーで入る</a><br>"
					+ "<a action=\"bypass -h Quest Frintezza invadePc\">1人で入る</a>"
					+ "</body></html>";
			else
				htmltext = "<html><body>No reaction. Contact must be initiated by the Command Channel Leader.</body></html>";	//= "32011-2.htm"
		}
		return htmltext;
	}
	
	private String doInvade(L2Npc npc, L2PcInstance player, List<List<L2PcInstance>> cc)
	{
		synchronized(this) //Synch to prevent 2 command channels entering at once	//[r7912]
		{
			if (GrandBossManager.getInstance().getBossStatus(FRINTEZZA) == DEAD)	//[r7605]
				return "<html><body>There is nothing beyond the Magic Force Field. Come back later.<br>(You may not enter because Frintezza is not inside the Imperial Tomb.)</body></html>";
			
			if (GrandBossManager.getInstance().getBossStatus(FRINTEZZA) != DORMANT)
				return "<html><body>Someone else is already inside the Magic Force Field. Try again later.</body></html>";
			
			boolean ready = true;
			for (List<L2PcInstance> pp : cc)
			{
				if (pp == null)
					continue;
				for (L2PcInstance member : pp)
				{
					if (member == null)
						continue;
					if (member.getLevel() < 74)
					{
						npc.broadcastPacket(new NpcSay(npc.getObjectId(), Say2.ALL, npc.getId(), "レベル74以上のキャラクターのみ入場できます。"));
						ready = false;
					}
					if (!member.isInsideRadius(npc, 700, false, false))
					{
						npc.broadcastPacket(new NpcSay(npc.getObjectId(), Say2.ALL, npc.getId(), "離れすぎているプレイヤーがいるため入場できません。"));
						ready = false;
					}
				//	if (_PlayersInside.size() > 45)
				//	{
				//		npc.broadcastPacket(new NpcSay(npc.getObjectId(), Say2.ALL, npc.getId(), "定員を超えているため入場できません。"));
				//	//	member.sendMessage("The number of challenges have been full, so can not enter.");
				//		check = false;
				//	}
				}
			}
			if (! ready)
				return "<html><body>条件を満たしていないため入場できません。</body></html>";
			
			if (! player.destroyItemByItemId("Quest", FRINTEZZA_SCROLL, 1, player, true))
				return "<html><body>「フリンテッサの結界解除スクロール」を持っていれば、フリンテッサ様の結界内部に入れます。</body></html>";
			//	return = "<html><body>You dont have required item.</body></html>";
			
			this/*GrandBossManager.getInstance()*/.setBossStatus(FRINTEZZA, WAITING);
		}	//[r7912]
		startQuestTimer("close", 0, null, null);
		startQuestTimer("room1_spawn", 5000, null, null);
		startQuestTimer("room_final", ROOM_FINAL_TIME, npc, null);
		_timeLimit = System.currentTimeMillis() + ROOM_FINAL_TIME;
		startQuestTimer("frintezza_despawn", 60000, null, null, true);
		
		int locCycle = 0;
		int outside = 0;
		for (List<L2PcInstance> pp : cc)
		{
			if (pp == null)
				continue;
			for (L2PcInstance member : pp)
			{
				if (member == null)
					continue;
				_playersInside.add(member);
				_zone.allowPlayerEntry(member, 300);
				int[] loc = _invadeLoc[locCycle];
				member.teleToLocation(loc[0] + Rnd.get(50), loc[1] + Rnd.get(50), loc[2]);
				
				destroyItem(member, SOUL_BREAKING_ARROW);
				destroyItem(member, DEWDROP_OF_DESTRUCTION);
				if (locCycle == 0) {} else ++outside;
			}
			if (++locCycle > 5)	// 0,1,2,3,4,5,1,2,3,4,5,1...
				locCycle = 1;
		}
		
		// 少人数用特別ルール：外周（アラーム部屋）に誰もいないと脱出できないので扉を１つ開けておく
		if (MODS_ALONE_PLAYER && outside == 0)
			DoorTable.getInstance().getDoor(17130057).openMe();
		
		_lastAction = System.currentTimeMillis();
		// Cancel quest if _PlayersInside is empty.
		if (_playersInside.size() == 0)
			_lastAction = 0;
		
		return null;
	}
	
	@Override
	public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet)
	{
		_lastAction = System.currentTimeMillis();
		_scarletAI_PreviousTarget = null;
		switch (npc.getId())
		{
		case FRINTEZZA:
			npc.getStatus().setCurrentHp(npc.getMaxHp(), false);
			return null;
		
		case SCARLET1:
			synchronized (this)
			{
				if (!_isInSocialAction)
				{
					if (_scarletType == 1)
					{
						if (npc.getCurrentHp() < npc.getMaxHp() * 0.75 && GrandBossManager.getInstance().getBossStatus(FRINTEZZA) == FIGHTING)
						{
							_isInSocialAction = true;
							_scarletType = 2;
							cancelAllQuestTimers();
							startQuestTimer("attack_stop", 0, null, null);
							startQuestTimer("stop_pc", 1000, null, null);
							startQuestTimer("stop_npc", 1000, npc, null);
							startQuestTimer("morph_01", 1100, npc, null);
						}
					}
					else if (_scarletType == 2)
					{
						if (npc.getCurrentHp() < npc.getMaxHp() * 0.5 && GrandBossManager.getInstance().getBossStatus(FRINTEZZA) == FIGHTING)
						{
							_isInSocialAction = true;
							_scarletType = 3;
							cancelAllQuestTimers();
							startQuestTimer("attack_stop", 0, null, null);
							startQuestTimer("stop_pc", 2000, null, null);
							startQuestTimer("stop_npc", 2000, npc, null);
							startQuestTimer("morph_05a", 2000, npc, null);
							startQuestTimer("morph_05", 2100, npc, null);
						}
					}
				}
			}
			break;
			
		case 29050:
		case 29051:
			//skills/5000-5099.xml
			// <skill id="5011" levels="1" name="Frintezza's Bomber Ghost">
			// <cond>
			//   <player hp="10" />
			// </cond>
			double hp = npc.getCurrentHp();
			double mm = npc.getMaxHp() * 0.10;
			if (hp >= mm && hp - damage > 0 && hp - damage < mm)
				npc.enableSkill(SkillTable.getInstance().getInfo(5011, 1));
			break;
		}
		
		return super.onAttack(npc, attacker, damage, isPet);
	}
	
	@Override
	public String onSpawn(L2Npc npc)
	{
		switch (npc.getId())
		{
		case SCARLET1:
		case SCARLET2:
			npc.disableCoreAI(true);
			break;
		case 29050:
		case 29051:
			npc.disableSkill(SkillTable.getInstance().getInfo(5011, 1), 0);	//Bomb
			break;
		}
		((com.l2jserver.gameserver.model.actor.L2Attackable)npc).setOnKillDelay(100);	//Default 5000ms.
		return super.onSpawn(npc);
	}

	@Override
	public String onSkillSee(L2Npc npc, L2PcInstance player, L2Skill skill, L2Object[] targets, boolean isPet)
	{
		if (targets.length > 0 && targets[0] == npc)
		{
			if (npc == frintezza)
				npc.getStatus().setCurrentHp(npc.getMaxHp(), false);
			switch (skill.getId())
			{
			case 2234:
				// skills/2200-2299.xml
				//  <skill id="2234" name="Soul Breaking Arrow">
				//  <target npcId="29045"/> <!-- Frintezza -->
				cancelQuestTimers("songs_play");
				cancelQuestTimers("songs_effect");
				npc.abortCast();
				_hall.broadcastPacket(new MagicSkillCanceld(npc.getObjectId()));
				for (L2Character ch : _hall.getCharactersInside())
				{
					if (ch instanceof L2PcInstance)
					{
						L2PcInstance pc = (L2PcInstance)ch;
						pc.stopSkillEffects(false, 5008);
					}
				}
				startQuestTimer("songs_play", Rnd.get(30000, 120000), npc, null);
				break;
//			case 2276:
//				// skills/2200-2299.xml
//				//  <skill id="2276" name="Dewdrop of Destruction">
//				//  <target npcId="29048 29049"/> <!-- Evil Spirit -->
//				npc.doDie(player);
//				break;
			}
		}
		
		return super.onSkillSee(npc, player, skill, targets, isPet);
	}
	
	@Override
	public String onEnterZone(L2Character character, L2ZoneType zone)
	{
		assert zone.getId() == 12911;
		if (character instanceof L2PcInstance)
		{
			synchronized (this)
			{
				if (! _isReachToHall)
				{
					_isReachToHall = true;
					_lastAction = System.currentTimeMillis();
					_timeLimit = 0;
					cancelAllQuestTimers();
					startQuestTimer("waiting", Frintezza_Wait_Time, null, null);
					
					ThreadPoolManager.getInstance().scheduleGeneral(new Runnable(){@Override public void run(){
						_zone.broadcastPacket(new ExShowScreenMessage((Frintezza_Wait_Time / 60000) + "分後にフリンテッサ様が登場します。", 10000));
					}}, 3000);
				}
			}
		}
	
		return super.onEnterZone(character, zone);
	}
	
	@Override
	public String onKill(L2Npc npc, L2PcInstance killer, boolean isPet)
	{
		switch (npc.getId())
		{
		case FRINTEZZA:
			if (!_isInSocialAction)
				throw new RuntimeException();
			break;
		
		case SCARLET1:
			if ((_scarletType == 1 || _scarletType == 2) && ! _isInSocialAction && GrandBossManager.getInstance().getBossStatus(FRINTEZZA) == FIGHTING)
			{
				// Do 3rd morph.
				npc.setIsDead(false);
				DecayTaskManager.getInstance().cancelDecayTask(npc);
				_scarletType = 2;
			/*(	_isInSocialAction = false;	)*/
				npc.setCurrentHp(npc.getMaxHp() * 0.45);
				npc.setCurrentMp(npc.getMaxMp() * 0.45);
				return this.onAttack(npc, killer, 0, isPet);	//Redirect to onAttack.
			}
			break;
			
		case SCARLET2:
			_isInSocialAction = true;
			_scarletType = 0;
			
			cancelAllQuestTimers();
			startQuestTimer("attack_stop", 0, null, null);	//+[JOJO]
			startQuestTimer("stop_pc", 0, null, null);
			startQuestTimer("stop_npc", 0, npc, null);
			startQuestTimer("morph_16", 0, npc, null);
			break;
			
		case ALARM_DEVICE:
			if (Rnd.get(100) < 33)
				killer.addItem("Quest", DEWDROP_OF_DESTRUCTION, 1, npc, true);	// Dewdrop of Destruction
			
			final int killHallAlarmDevice;
			synchronized (this) {killHallAlarmDevice = ++_killHallAlarmDevice;}	//[JOJO]槍・範囲狩対策
			if (killHallAlarmDevice == 4)
			{
				startQuestTimer("room1_del", 1000, null, null);
				startQuestTimer("room2_spawn", 3000, null, null);
				startQuestTimer("room1_open", 5000, null, null);	//+[JOJO]
			}
			break;
			
		case 18329:
		case 18330:
		case 18331:
		case 18332:
		case 18333:
			if (Rnd.get(100) < 10)
				killer.addItem("Quest", DEWDROP_OF_DESTRUCTION, 1, npc, true);	// Dewdrop of Destruction
			break;
			
		case CHOIR_PRAYER:
			final int killDarkChoirPlayer;
			synchronized (this) {killDarkChoirPlayer = ++_killDarkChoirPlayer;}	//[JOJO]槍・範囲狩対策
			if (killDarkChoirPlayer == 2)
			{
				startQuestTimer("room2_close", 1000, null, null);	//+[JOJO]
				startQuestTimer("room2_spawn2", 3000, null, null);
			}
			break;
			
		case CHOIR_CAPTAIN:
			if (Rnd.get(100) < 33)
				killer.addItem("Quest", SOUL_BREAKING_ARROW, 1, npc, true);	// Soul Breaking Arrow
			
			final int killDarkChoirCaptain;
			synchronized (this) {killDarkChoirCaptain = ++_killDarkChoirCaptain;}	//[JOJO]槍・範囲狩対策
			if (killDarkChoirCaptain == 8)
			{
				startQuestTimer("room2_del", 3000, null, null);
				startQuestTimer("room2_open", 5000, null, null);	//+[JOJO]
			}
			else	//[JOJO]
			{
				int npcstring = Rnd.get(100) < 50 ? 1010645/*黒聖歌団の演奏を妨げる邪魔者を消せ！*/
				                                  : 1010646/*死の演奏が聞こえないのか！黒聖歌団の恐ろしさを見せてやれ！*/;
				npc.broadcastPacket(new NpcSay(npc.getObjectId(), Say2.SHOUT, npc.getId(), npcstring));
			}
			break;
			
		case 29048:
		case 29049:
			if (npc == portrait1)
			{
				if (ghost1 != null) { ghost1.getSpawn().stopRespawn(); ghost1.doDie(ghost1); }
			}
			else if (npc == portrait2)
			{
				if (ghost2 != null) { ghost2.getSpawn().stopRespawn(); ghost2.doDie(ghost2); }
			}
			else if (npc == portrait3)
			{
				if (ghost3 != null) { ghost3.getSpawn().stopRespawn(); ghost3.doDie(ghost3); }
			}
			else if (npc == portrait4)
			{
				if (ghost4 != null) { ghost4.getSpawn().stopRespawn(); ghost4.doDie(ghost4); }
			}
			else
				throw new RuntimeException();
			break;
		}
		
		return super.onKill(npc,killer,isPet);
	}
	
	
	void setBossStatus(int bossId, int status)
	{
		GrandBossManager.getInstance().setBossStatus(FRINTEZZA, status);
		
		if (status == DORMANT)
			_isReachToHall = false;
		
		if (MODS_GUIDE_EFFECT)
		{
			if (_guide == null)
				_guide = SpawnTable.getInstance().getSpawnOne(GUIDE).getLastSpawn();
			if (status == DORMANT)
				_guide.stopAbnormalVisualEffect(true, AbnormalVisualEffect.VP_UP);
			else
				_guide.startAbnormalVisualEffect(true, AbnormalVisualEffect.VP_UP);
		}
	}
	
	private L2Npc addSpawnGhost(int npcId, int x, int y, int z, int heading, boolean randomOffSet, long despawnDelay)
	{
		L2Npc result = Quest.addSpawn(npcId, x, y, z, heading, /*randomOffSet=*/false, /*despawnDelay=*/0);
		L2Spawn spawn = result.getSpawn();
		spawn.setAmount(1);
		spawn.setRespawnDelay(60);
		spawn.startRespawn();
		return result;
	}
	
	private void oustAllObjects()
	{
		_zone.oustAllPlayers();
		
		if (MODS_REMOVE_ITEMS_ON_GROUND)
		{
			L2Npc dummy = addSpawn(CUBE, -87784, -153298, -9179, 16384, false, 0);
			for (L2Object o : com.l2jserver.gameserver.model.L2World.getInstance().getVisibleObjects(dummy))
			{
				if (o instanceof L2ItemInstance && _hall.isInsideZone(o))
				{
					L2ItemInstance i = (L2ItemInstance)o;
					i.decayMe();
				}
			}
			dummy.deleteMe();
		}
	}
	
	private L2Character _scarletAI_PreviousTarget;
	private L2Skill _scarletAI_ScheduledSkill;	//Scheduled skill.
	private int _scarletAI_X, _scarletAI_Y;		//Deadlock Detection.
	
	private void scarletAI_RandomSkill(String event, L2Npc npc_, L2PcInstance player, int[][] daemonSkills)
	{
		assert npc_ == activeScarlet;
		L2GrandBossInstance npc = (L2GrandBossInstance)npc_;
		long delay;
		if ((delay = npc.getCastEndTimeLeftMilli()) > 0)
		{
			startQuestTimer(event, delay + 500, npc, player);
			return;
		}
		else
		{
			L2Skill skill;
			if ((skill = _scarletAI_ScheduledSkill) == null)
			{
				skill = _scarletAI_ScheduledSkill = getRandomSkill(daemonSkills);
			}
			
			L2Character target = npc.getTarget() instanceof L2Character ? (L2Character)npc.getTarget() : null;
			if (target == null || target.isDead() || target == _scarletAI_PreviousTarget && !npc.isMoving())
			{
				target = getRandomTarget();
				if (target == null)
				{
					startQuestTimer(event, 60000, npc, null);
					return;
				}
				npc.setTarget(target);
				npc.addDamageHate(target, 0, 1000/*FIXME*/);
			}
			
			int range = skill.getCastRange();
			if (target.isMoving()) range = range * 80 / 100;
			if (Util.checkIfInRange(range, npc, target, true))
			{
				_scarletAI_PreviousTarget = target;
				_scarletAI_ScheduledSkill = null; //Done.
				_scarletAI_X = _scarletAI_Y = 0;
				npc.stopMove(null);			//[r7602]
				npc.getAI().setIntention(AI_INTENTION_IDLE);	//[JOJO]
				npc.setIsCastingNow(true);	//[r7592]
				npc.doCast(skill);
				startQuestTimer(event, skill.getHitTime() + Math.min(3000, skill.getCoolTime()), npc, null);
				return;
			}
			else if (npc.isMoving())
			{
				startQuestTimer(event, 500, npc, player);
				return;
			}
			else
			{
				/**com.l2jserver.gameserver.ai.CtrlIntention intention = npc.getAI().getIntention();**/
				if (/**(intention == AI_INTENTION_IDLE || intention == AI_INTENTION_ACTIVE)
						&&**/ npc.getX() == _scarletAI_X && npc.getY() == _scarletAI_Y)
				{
					// AI_INTENTION_FOLLOW aborted. See com.l2jserver.gameserver.ai.AbstractAI.FollowTask
					npc.setTarget(null);
					_scarletAI_PreviousTarget = null;
					_scarletAI_ScheduledSkill = null;
					_scarletAI_X = _scarletAI_Y = 0;
					startQuestTimer(event, 5, npc, player);
					return;
				}
				else
				{
					_scarletAI_X = npc.getX();
					_scarletAI_Y = npc.getY();
					npc.getAI().setIntention(AI_INTENTION_FOLLOW, target, null);
					startQuestTimer(event, 1000, npc, player);
					return;
				}
			}
		}
	}
	
	private L2Skill getRandomSkill(int[][] daemonSkills)
	{
		assert daemonSkills == DAEMON1_SKILLS || daemonSkills == DAEMON2_SKILLS ||daemonSkills == DAEMON3_SKILLS;
		int rnd = Rnd.get(100);
		for (int i = daemonSkills.length; --i >= 0; )
		{
			int[] t = daemonSkills[i];
			int chance = t[2];
			if ((rnd -= chance) <= 0)
				return SkillTable.getInstance().getInfo(t[0], t[1]);
		}
		throw new RuntimeException();
	}
	
	private L2Character getRandomTarget()
	{
		int count;
		
		Collection<L2Character> lst = _hall.getCharactersInside();
		L2Character[] array = new L2Character[lst.size()];
		
		count = 0;
		for (L2Character cha : lst)
			if (cha != null && !cha.isDead() && cha.isVisible())
			{
				if (Util.checkIfInRange(900, frintezza, cha, true))
				{
					if (cha instanceof L2PcInstance)
						array[count++] = cha;
				}
			}
		if (count > 0) return array[Rnd.get(count)];
		
		count = 0;
		for (L2Character cha : lst)
			if (cha != null && !cha.isDead() && cha.isVisible())
			{
				if (Util.checkIfInRange(2950, activeScarlet, cha, true))
				{
					if (cha instanceof L2PcInstance)
						array[count++] = cha;
					else if (cha instanceof L2MonsterInstance && !isFrintezzaFriend(((L2MonsterInstance)cha).getId()))
						array[count++] = cha;
				}
			}
		if (count > 0) return array[Rnd.get(count)];
		
		return null;
	}
	
	private void destroyItem(L2PcInstance pc, int itemId)
	{
		L2ItemInstance i = pc.getInventory().getItemByItemId(itemId);
		if (i != null)
			pc.destroyItemByItemId("Quest", itemId, i.getCount(), pc, true);
	}
	
	private int calcCameraAngle(L2Character npc)
	{
		int heading = npc.getHeading();
		int a = 180 - (int) (heading / 182.044444444);
		if (a < 0)
			a += 360;
		return a;
	}
	
	private void throwUp(L2Character attacker, final double range, SystemMessage msg)
	{
		final int mx = attacker.getX(), my = attacker.getY();
		for (L2Character target : _hall.getCharactersInside())
		{
			if (target == attacker)
				continue;
			if (target instanceof L2Npc && isFrintezzaFriend(((L2Npc)target).getId()))
				continue;
			double dx = target.getX() - mx;
			double dy = target.getY() - my;
			if (dx == 0 && dy == 0) dx = dy = range / 2;
			double aa = range / Math.sqrt(dx * dx + dy * dy);
			if (aa > 1.0)
			{
				int x = mx + (int)(dx * aa);
				int y = my + (int)(dy * aa);
				int z = target.getZ();
//				if (Config.GEODATA == 0)
//				{
//					if (horizontalDistance(CAMERA_X, CAMERA_Y, x, y) >= CAMERA_R)
//					{
//						x = mx - (int)(dx * aa);
//						y = my - (int)(dy * aa);
//					}
//				}
				
				target.getAI().setIntention(AI_INTENTION_IDLE);
				target.abortAttack();
				target.abortCast();
				
				target.broadcastPacket(new FlyToLocation(target, x, y, z, FlyType.THROW_UP));
				target.setXYZ(x, y, z);
				target.setHeading(Util.calculateHeadingFrom(x, y, mx, my));
				target.broadcastPacket(new ValidateLocation(target));
				if (msg != null) target.sendPacket(msg);
				if (target instanceof L2PcInstance) ((L2PcInstance)target).standUp();
			}
		}
	}
	
	private boolean isFrintezzaFriend(int npcId)
	{
		return npcId >= 29045 && npcId <= 29053;
	}
	
	
	public static void main(String[] args)
	{
		// now call the constructor (starts up the ai)
		new Frintezza(-1,"Frintezza","ai");
	}
}
/* Tagjump

Eclipse AnyEdit: Open File Under Cursol (Ctrl+Alt+R)

../../../html/default/32011.htm
../../../html/default/32011.htm.ja
../../../html/default/32011-1.htm
../../../html/default/32011-1.htm.ja
../../../html/default/32011-2.htm
../../../stats/skills/5000-5099.xml
	<skill id="5006" levels="1" name="Frintezza's Melody">
	<skill id="5007" levels="5" name="Frintezza's Songs">
	<skill id="5008" levels="5" name="Frintezza's Songs">
	<skill id="5011" levels="1" name="Frintezza's Bomber Ghost">
	<skill id="5014" levels="3" name="Frintezza's Daemon Attack">
	<skill id="5015" levels="6" name="Frintezza's Daemon Charge">
	<skill id="5016" levels="1" name="Yoke of Scarlet">
../../../stats/skills/2200-2299.xml
	<skill id="2234" levels="1" name="Soul Breaking Arrow">
	<skill id="2276" levels="1" name="Dewdrop of Destruction">
../../../zones/zone.xml
	<zone id="12011" type="BossZone" ...>
	<zone id="12911" type="ScriptZone" ... />
../../../../sql/zone_vertices.sql
	(12011, ...)
	(12911, ...)
../../../../sql/npc_ja.sql
	DELETE FROM npcskills WHERE npcid=29050 AND skillid=5010;
	DELETE FROM npcskills WHERE npcid=29051 AND skillid=5009;
../../../../sql/etcitem_ja.sql
	UPDATE etcitem SET handler='ItemSkills', skill='2234-1;', dropable='false' WHERE item_id=8192; -- Soul Breaking Arrow
	UPDATE etcitem SET handler='ItemSkills', skill='2276-1;', dropable='false' WHERE item_id=8556; -- Dewdrop of Destruction

Eclipse AnyEdit: Open Type Under Cursol (Ctrl+Alt+T)

../../../../../L2_GameServer/java/com/l2jserver/gameserver/skills/effects/EffectFloatStun.java
../../../../../L2_GameServer/java/com/l2jserver/gameserver/skills/effects/EffectDanceStun.java

 */