This commit is contained in:
mobius
2015-01-01 20:02:50 +00:00
parent eeae660458
commit a6a3718849
17894 changed files with 2818932 additions and 0 deletions

View File

@@ -0,0 +1,268 @@
/*
* Copyright (C) 2004-2014 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 hellbound.AI.Zones.AnomicFoundry;
import java.util.Map;
import javolution.util.FastMap;
import ai.npc.AbstractNpcAI;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.datatables.SpawnTable;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Attackable;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.network.NpcStringId;
import com.l2jserver.gameserver.network.clientpackets.Say2;
import hellbound.HellboundEngine;
/**
* Anomic Foundry.
* @author GKR
*/
public final class AnomicFoundry extends AbstractNpcAI
{
// NPCs
private static int LABORER = 22396;
private static int FOREMAN = 22397;
private static int LESSER_EVIL = 22398;
private static int GREATER_EVIL = 22399;
// Misc
private final Map<Integer, Integer> _atkIndex = new FastMap<>();
// npcId, x, y, z, heading, max count
//@formatter:off
private static int[][] SPAWNS =
{
{LESSER_EVIL, 27883, 248613, -3209, -13248, 5},
{LESSER_EVIL, 26142, 246442, -3216, 7064, 5},
{LESSER_EVIL, 27335, 246217, -3668, -7992, 5},
{LESSER_EVIL, 28486, 245913, -3698, 0, 10},
{GREATER_EVIL, 28684, 244118, -3700, -22560, 10},
};
//@formatter:on
private int respawnTime = 60000;
private final int respawnMin = 20000;
private final int respawnMax = 300000;
private final int[] _spawned =
{
0,
0,
0,
0,
0
};
public AnomicFoundry()
{
super(AnomicFoundry.class.getSimpleName(), "hellbound/AI/Zones");
addAggroRangeEnterId(LABORER);
addAttackId(LABORER);
addKillId(LABORER, LESSER_EVIL, GREATER_EVIL);
addSpawnId(LABORER, LESSER_EVIL, GREATER_EVIL);
addTeleportId(LABORER, LESSER_EVIL, GREATER_EVIL);
startQuestTimer("make_spawn_1", respawnTime, null, null);
}
@Override
public final String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
if (event.equalsIgnoreCase("make_spawn_1"))
{
if (HellboundEngine.getInstance().getLevel() >= 10)
{
int idx = getRandom(3);
if (_spawned[idx] < SPAWNS[idx][5])
{
addSpawn(SPAWNS[idx][0], SPAWNS[idx][1], SPAWNS[idx][2], SPAWNS[idx][3], SPAWNS[idx][4], false, 0, false);
respawnTime += 10000;
}
startQuestTimer("make_spawn_1", respawnTime, null, null);
}
}
else if (event.equalsIgnoreCase("make_spawn_2"))
{
if (_spawned[4] < SPAWNS[4][5])
{
addSpawn(SPAWNS[4][0], SPAWNS[4][1], SPAWNS[4][2], SPAWNS[4][3], SPAWNS[4][4], false, 0, false);
}
}
else if (event.equalsIgnoreCase("return_laborer"))
{
if ((npc != null) && !npc.isDead())
{
((L2Attackable) npc).returnHome();
}
}
else if (event.equalsIgnoreCase("reset_respawn_time"))
{
respawnTime = 60000;
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isSummon)
{
if (getRandom(10000) < 2000)
{
requestHelp(npc, player, 500, FOREMAN);
requestHelp(npc, player, 500, LESSER_EVIL);
requestHelp(npc, player, 500, GREATER_EVIL);
}
return super.onAggroRangeEnter(npc, player, isSummon);
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, Skill skill)
{
int atkIndex = _atkIndex.containsKey(npc.getObjectId()) ? _atkIndex.get(npc.getObjectId()) : 0;
if (atkIndex == 0)
{
broadcastNpcSay(npc, Say2.NPC_ALL, NpcStringId.ENEMY_INVASION_HURRY_UP);
cancelQuestTimer("return_laborer", npc, null);
startQuestTimer("return_laborer", 60000, npc, null);
if (respawnTime > respawnMin)
{
respawnTime -= 5000;
}
else if ((respawnTime <= respawnMin) && (getQuestTimer("reset_respawn_time", null, null) == null))
{
startQuestTimer("reset_respawn_time", 600000, null, null);
}
}
if (getRandom(10000) < 2000)
{
atkIndex++;
_atkIndex.put(npc.getObjectId(), atkIndex);
requestHelp(npc, attacker, 1000 * atkIndex, FOREMAN);
requestHelp(npc, attacker, 1000 * atkIndex, LESSER_EVIL);
requestHelp(npc, attacker, 1000 * atkIndex, GREATER_EVIL);
if (getRandom(10) < 1)
{
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location((npc.getX() + getRandom(-800, 800)), (npc.getY() + getRandom(-800, 800)), npc.getZ(), npc.getHeading()));
}
}
return super.onAttack(npc, attacker, damage, isSummon, skill);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
if (getSpawnGroup(npc) >= 0)
{
_spawned[getSpawnGroup(npc)]--;
SpawnTable.getInstance().deleteSpawn(npc.getSpawn(), false);
}
else if (npc.getId() == LABORER)
{
if (getRandom(10000) < 8000)
{
broadcastNpcSay(npc, Say2.NPC_ALL, NpcStringId.PROCESS_SHOULDN_T_BE_DELAYED_BECAUSE_OF_ME);
if (respawnTime < respawnMax)
{
respawnTime += 10000;
}
else if ((respawnTime >= respawnMax) && (getQuestTimer("reset_respawn_time", null, null) == null))
{
startQuestTimer("reset_respawn_time", 600000, null, null);
}
}
_atkIndex.remove(npc.getObjectId());
}
return super.onKill(npc, killer, isSummon);
}
@Override
public final String onSpawn(L2Npc npc)
{
SpawnTable.getInstance().addNewSpawn(npc.getSpawn(), false);
if (getSpawnGroup(npc) >= 0)
{
_spawned[getSpawnGroup(npc)]++;
}
if (npc.getId() == LABORER)
{
npc.setIsNoRndWalk(true);
}
return super.onSpawn(npc);
}
@Override
protected void onTeleport(L2Npc npc)
{
if ((getSpawnGroup(npc) >= 0) && (getSpawnGroup(npc) <= 2))
{
_spawned[getSpawnGroup(npc)]--;
SpawnTable.getInstance().deleteSpawn(npc.getSpawn(), false);
npc.scheduleDespawn(100);
if (_spawned[3] < SPAWNS[3][5])
{
addSpawn(SPAWNS[3][0], SPAWNS[3][1], SPAWNS[3][2], SPAWNS[3][3], SPAWNS[3][4], false, 0, false);
}
}
else if (getSpawnGroup(npc) == 3)
{
startQuestTimer("make_spawn_2", respawnTime * 2, null, null);
_spawned[3]--;
SpawnTable.getInstance().deleteSpawn(npc.getSpawn(), false);
npc.scheduleDespawn(100);
}
}
private static int getSpawnGroup(L2Npc npc)
{
final int coordX = npc.getSpawn().getX();
final int coordY = npc.getSpawn().getY();
final int npcId = npc.getId();
for (int i = 0; i < 5; i++)
{
if ((SPAWNS[i][0] == npcId) && (SPAWNS[i][1] == coordX) && (SPAWNS[i][2] == coordY))
{
return i;
}
}
return -1;
}
// Zoey76: TODO: This should be done with onFactionCall(..)
private static void requestHelp(L2Npc requester, L2PcInstance agressor, int range, int helperId)
{
for (L2Spawn spawn : SpawnTable.getInstance().getSpawns(helperId))
{
final L2MonsterInstance monster = (L2MonsterInstance) spawn.getLastSpawn();
if ((monster != null) && (agressor != null) && !monster.isDead() && monster.isInsideRadius(requester, range, true, false) && !agressor.isDead())
{
monster.addDamageHate(agressor, 0, 1000);
}
}
}
}

View File

@@ -0,0 +1,3 @@
<html><body>Kendal:<br>
(It feels creepy for some reason.)
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Kendal:<br>
It was really horrible. They were tortured, abused, killed. That's how it was. Beleth is evil, and so is Darion. Hmph. Go teach them a lesson!
</body></html>

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2004-2014 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 hellbound.AI.Zones.BaseTower;
import java.util.Map;
import javolution.util.FastMap;
import ai.npc.AbstractNpcAI;
import com.l2jserver.gameserver.datatables.DoorTable;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.base.ClassId;
import com.l2jserver.gameserver.model.holders.SkillHolder;
/**
* Base Tower.
* @author GKR
*/
public final class BaseTower extends AbstractNpcAI
{
// NPCs
private static final int GUZEN = 22362;
private static final int KENDAL = 32301;
private static final int BODY_DESTROYER = 22363;
// Skills
private static final SkillHolder DEATH_WORD = new SkillHolder(5256, 1);
// Misc
private static final Map<Integer, L2PcInstance> BODY_DESTROYER_TARGET_LIST = new FastMap<>();
public BaseTower()
{
super(BaseTower.class.getSimpleName(), "hellbound/AI/Zones");
addKillId(GUZEN);
addKillId(BODY_DESTROYER);
addFirstTalkId(KENDAL);
addAggroRangeEnterId(BODY_DESTROYER);
}
@Override
public final String onFirstTalk(L2Npc npc, L2PcInstance player)
{
final ClassId classId = player.getClassId();
if (classId.equalsOrChildOf(ClassId.HELL_KNIGHT) || classId.equalsOrChildOf(ClassId.SOULTAKER))
{
return "32301-02.htm";
}
return "32301-01.htm";
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
if (event.equalsIgnoreCase("CLOSE"))
{
DoorTable.getInstance().getDoor(20260004).closeMe();
}
return super.onAdvEvent(event, npc, player);
}
@Override
public String onAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isSummon)
{
if (!BODY_DESTROYER_TARGET_LIST.containsKey(npc.getObjectId()))
{
BODY_DESTROYER_TARGET_LIST.put(npc.getObjectId(), player);
npc.setTarget(player);
npc.doSimultaneousCast(DEATH_WORD.getSkill());
}
return super.onAggroRangeEnter(npc, player, isSummon);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
switch (npc.getId())
{
case GUZEN:
{
// Should Kendal be despawned before Guzen's spawn? Or it will be crowd of Kendal's
addSpawn(KENDAL, npc.getSpawn().getLocation(), false, npc.getSpawn().getRespawnDelay(), false);
DoorTable.getInstance().getDoor(20260003).openMe();
DoorTable.getInstance().getDoor(20260004).openMe();
startQuestTimer("CLOSE", 60000, npc, null, false);
break;
}
case BODY_DESTROYER:
{
if (BODY_DESTROYER_TARGET_LIST.containsKey(npc.getObjectId()))
{
final L2PcInstance pl = BODY_DESTROYER_TARGET_LIST.get(npc.getObjectId());
if ((pl != null) && pl.isOnline() && !pl.isDead())
{
pl.stopSkillEffects(true, DEATH_WORD.getSkillId());
}
BODY_DESTROYER_TARGET_LIST.remove(npc.getObjectId());
}
break;
}
}
return super.onKill(npc, killer, isSummon);
}
}

View File

@@ -0,0 +1,3 @@
<html><body>Jerian:<br>
I cannot smell any Devil's blood, or it's too far away.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Jerian:<br>
You are not a party leader. Lead a group into battle, or just send your party leader to talk to me.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Jerian:<br>
This foe is still too powerful. Come back when he is weakened.
</body></html>

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2004-2014 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 hellbound.AI.Zones.TowerOfInfinitum;
import java.util.HashMap;
import java.util.Map;
import ai.npc.AbstractNpcAI;
import com.l2jserver.gameserver.model.L2Party;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.util.Util;
import hellbound.HellboundEngine;
/**
* Tower Of Infinitum.
* @author GKR
*/
public final class TowerOfInfinitum extends AbstractNpcAI
{
// NPCs
private static final int JERIAN = 32302;
private static final int GK_FIRST = 32745;
private static final int GK_LAST = 32752;
// Skills
private static final int PASS_SKILL = 2357;
// Misc
private static final Map<Integer, Location[]> TELE_COORDS = new HashMap<>();
static
{
TELE_COORDS.put(32745, new Location[]
{
new Location(-22208, 277122, -13376),
null
});
TELE_COORDS.put(32746, new Location[]
{
new Location(-22208, 277106, -11648),
new Location(-22208, 277074, -15040)
});
TELE_COORDS.put(32747, new Location[]
{
new Location(-22208, 277120, -9920),
new Location(-22208, 277120, -13376)
});
TELE_COORDS.put(32748, new Location[]
{
new Location(-19024, 277126, -8256),
new Location(-22208, 277106, -11648)
});
TELE_COORDS.put(32749, new Location[]
{
new Location(-19024, 277106, -9920),
new Location(-22208, 277122, -9920)
});
TELE_COORDS.put(32750, new Location[]
{
new Location(-19008, 277100, -11648),
new Location(-19024, 277122, -8256)
});
TELE_COORDS.put(32751, new Location[]
{
new Location(-19008, 277100, -13376),
new Location(-19008, 277106, -9920)
});
TELE_COORDS.put(32752, new Location[]
{
new Location(14602, 283179, -7500),
new Location(-19008, 277100, -11648)
});
}
public TowerOfInfinitum()
{
super(TowerOfInfinitum.class.getSimpleName(), "hellbound/AI/Zones");
addStartNpc(JERIAN);
addTalkId(JERIAN);
for (int i = GK_FIRST; i <= GK_LAST; i++)
{
addStartNpc(i);
addTalkId(i);
}
}
private static final Location ENTER_LOCATION = new Location(-22204, 277056, -15023);
@Override
public final String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
String htmltext = event;
final int npcId = npc.getId();
if (event.equalsIgnoreCase("enter") && (npcId == JERIAN))
{
if (HellboundEngine.getInstance().getLevel() >= 11)
{
L2Party party = player.getParty();
if ((party != null) && (party.getLeaderObjectId() == player.getObjectId()))
{
for (L2PcInstance partyMember : party.getMembers())
{
if (!Util.checkIfInRange(300, partyMember, npc, true) || !partyMember.isAffectedBySkill(PASS_SKILL))
{
return "32302-02.htm";
}
}
for (L2PcInstance partyMember : party.getMembers())
{
partyMember.teleToLocation(ENTER_LOCATION, true);
}
htmltext = null;
}
else
{
htmltext = "32302-02a.htm";
}
}
else
{
htmltext = "32302-02b.htm";
}
}
else if ((event.equalsIgnoreCase("up") || event.equalsIgnoreCase("down")) && (npcId >= GK_FIRST) && (npcId <= GK_LAST))
{
final int direction = event.equalsIgnoreCase("up") ? 0 : 1;
final L2Party party = player.getParty();
if (party == null)
{
htmltext = "gk-noparty.htm";
}
else if (!party.isLeader(player))
{
htmltext = "gk-noreq.htm";
}
else
{
for (L2PcInstance partyMember : party.getMembers())
{
if (!Util.checkIfInRange(1000, partyMember, npc, false) || (Math.abs(partyMember.getZ() - npc.getZ()) > 100))
{
return "gk-noreq.htm";
}
}
final Location tele = TELE_COORDS.get(npcId)[direction];
if (tele != null)
{
for (L2PcInstance partyMember : party.getMembers())
{
partyMember.teleToLocation(tele, true);
}
}
htmltext = null;
}
}
return htmltext;
}
}

View File

@@ -0,0 +1,3 @@
<html><body>Teleport Device:<br>
The teleport device is not working because you're not a party member.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Teleport Device:<br>
The teleport device does not work because you're not a party leader or a party member is too far away.
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Control Device:<br>
You see a very complex machine standing <font color="LEVEL">in the middle of the room</font>. Its multi-colored controls feature a number of symbols, glyphs, and pictures.<br>
One bright red button stands out among the rest...<br>
<a action="bypass -h Quest TowerOfNaia 18492-05.htm">Push the button.</a><br>
<a action="bypass -h npc_%objectId%_Quest">Quest.</a>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Control Device:<br>
It looks like a very complicated and elaborate machine, but you see no moving parts. Perhaps it is broken.<br>
(Time is needed to reactivate it. Please wait a moment.)
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Controller:<br>
With a loud noise, a series of bright green lights emanate from the image representing the pillar, and a powerful energy vortex forms nearby.<br>
It appears as though you may now enter the Tower of Naia.<br>
(Only one party can enter at a time.)<br>
<a action="bypass -h Quest TowerOfNaia teleport">Enter the Tower of Naia.</a>
</body></html>

View File

@@ -0,0 +1,8 @@
<html><body>Old Dwarven Ghost:<br>
The room behind me is...a storage area for magical energy...extracted by Beleth...from Hellbound Isle... It is <font color="LEVEL">Core Naia</font>...<br>
Nature spirits...that once inhabited... the air, the earth and even living things...were trapped in this place for a long, long time... They mutated...as they tried to...escape.<br>
They still possess their elemental nature...fire, water, wind and earth...and they exist separately...<br>
But they are now interconnected... When they sense danger...they join together in...a powerful spiritual body...<br>
They are extremely powerful now...difficult to defeat...<br>
<a action="bypass -h Quest TowerOfNaia 32370-03b.htm">Ask about the spiritual body.</a>
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Old Dwarven Ghost:<br>
One existence in Core Naia... a collective body of numerous nature spirits... Its name is... <font color="LEVEL">Epidos</font>...<br>
It creates small creatures...<br>
When you see...a strange creature in the room...do not kill it...unless you can withstand the fury of Epidos...<br>
(Before facing the Raid Boss Epidos, it is recommended you have a party of at least 27 characters. Epidos possesses water, fire, wind and earth attributes.)
</body></html>

View File

@@ -0,0 +1,967 @@
/*
* Copyright (C) 2004-2014 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 hellbound.AI.Zones.TowerOfNaia;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javolution.util.FastList;
import javolution.util.FastMap;
import ai.npc.AbstractNpcAI;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.datatables.DoorTable;
import com.l2jserver.gameserver.datatables.SkillData;
import com.l2jserver.gameserver.instancemanager.GlobalVariablesManager;
import com.l2jserver.gameserver.instancemanager.ZoneManager;
import com.l2jserver.gameserver.model.L2Party;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.zone.L2ZoneType;
import com.l2jserver.gameserver.network.NpcStringId;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.clientpackets.Say2;
import com.l2jserver.gameserver.util.MinionList;
import com.l2jserver.gameserver.util.Util;
/**
* Tower Of Naia.
* @author GKR
*/
public final class TowerOfNaia extends AbstractNpcAI
{
// Challenge states
private static final int STATE_SPORE_CHALLENGE_IN_PROGRESS = 1;
private static final int STATE_SPORE_CHALLENGE_SUCCESSFULL = 2;
private static final int STATE_SPORE_IDLE_TOO_LONG = 3;
// Some constants
private static final int SELF_DESPAWN_LIMIT = 600; // Challenge discontinues after 600 self-despawns by timer
private static final int ELEMENT_INDEX_LIMIT = 120; // Epidos spawns when index reaches 120 points
private static final int LOCK = 18491;
private static final int CONTROLLER = 18492;
private static final int ROOM_MANAGER_FIRST = 18494;
private static final int ROOM_MANAGER_LAST = 18505;
private static final int MUTATED_ELPY = 25604;
private static final int SPORE_BASIC = 25613;
private static final int SPORE_FIRE = 25605;
private static final int SPORE_WATER = 25606;
private static final int SPORE_WIND = 25607;
private static final int SPORE_EARTH = 25608;
private static final int DWARVEN_GHOST = 32370;
private static final int[] EPIDOSES =
{
25610,
25609,
25612,
25611
}; // Order is important!
private static final int[] TOWER_MONSTERS =
{
18490,
22393,
22394,
22395,
22411,
22412,
22413,
22439,
22440,
22441,
22442
};
private static final int[] ELEMENTS =
{
25605,
25606,
25607,
25608
};
private static final int[] OPPOSITE_ELEMENTS =
{
25606,
25605,
25608,
25607
};
private static final String[] ELEMENTS_NAME =
{
"Fire",
"Water",
"Wind",
"Earth"
};
//@formatter:off
private static final int[][] SPORES_MOVE_POINTS =
{
{-46080, 246368, -14183},
{-44816, 246368, -14183},
{-44224, 247440, -14184},
{-44896, 248464, -14183},
{-46064, 248544, -14183},
{-46720, 247424, -14183},
};
private static final int[][] SPORES_MERGE_POSITION =
{
{-45488, 246768, -14183},
{-44767, 247419, -14183},
{-46207, 247417, -14183},
{-45462, 248174, -14183},
};
//@formatter:on
private static final NpcStringId[] SPORES_NPCSTRING_ID =
{
NpcStringId.IT_S_S1,
NpcStringId.S1_IS_STRONG,
NpcStringId.IT_S_ALWAYS_S1,
NpcStringId.S1_WON_T_DO
};
private static Map<Integer, int[]> DOORS = new HashMap<>();
private static Map<Integer, Integer> ZONES = new HashMap<>();
private static Map<Integer, int[][]> SPAWNS = new HashMap<>();
private L2MonsterInstance _lock;
private final L2Npc _controller;
private int _counter;
private int _despawnedSporesCount;
private final int[] _indexCount =
{
0,
0
};
private int _challengeState;
private int _winIndex;
private final Map<Integer, Boolean> _activeRooms = new FastMap<>();
private final Map<Integer, List<L2Npc>> _spawns = new FastMap<>();
private final FastList<L2Npc> _sporeSpawn = new FastList<L2Npc>().shared();
static
{
// Format: entrance_door, exit_door
DOORS.put(18494, new int[]
{
18250001,
18250002
});
DOORS.put(18495, new int[]
{
18250003,
18250004
});
DOORS.put(18496, new int[]
{
18250005,
18250006
});
DOORS.put(18497, new int[]
{
18250007,
18250008
});
DOORS.put(18498, new int[]
{
18250009,
18250010
});
DOORS.put(18499, new int[]
{
18250011,
18250101
});
DOORS.put(18500, new int[]
{
18250013,
18250014
});
DOORS.put(18501, new int[]
{
18250015,
18250102
});
DOORS.put(18502, new int[]
{
18250017,
18250018
});
DOORS.put(18503, new int[]
{
18250019,
18250103
});
DOORS.put(18504, new int[]
{
18250021,
18250022
});
DOORS.put(18505, new int[]
{
18250023,
18250024
});
ZONES.put(18494, 200020);
ZONES.put(18495, 200021);
ZONES.put(18496, 200022);
ZONES.put(18497, 200023);
ZONES.put(18498, 200024);
ZONES.put(18499, 200025);
ZONES.put(18500, 200026);
ZONES.put(18501, 200027);
ZONES.put(18502, 200028);
ZONES.put(18503, 200029);
ZONES.put(18504, 200030);
ZONES.put(18505, 200031);
//@formatter:off
SPAWNS.put(18494, new int[][]
{
{22393, -46371, 246400, -9120, 0},
{22394, -46435, 245830, -9120, 0},
{22394, -46536, 246275, -9120, 0},
{22393, -46239, 245996, -9120, 0},
{22394, -46229, 246347, -9120, 0},
{22394, -46019, 246198, -9120, 0},
});
SPAWNS.put(18495, new int[][]
{
{22439, -48146, 249597, -9124, -16280},
{22439, -48144, 248711, -9124, 16368},
{22439, -48704, 249597, -9104, -16380},
{22439, -49219, 249596, -9104, -16400},
{22439, -49715, 249601, -9104, -16360},
{22439, -49714, 248696, -9104, 15932},
{22439, -49225, 248710, -9104, 16512},
{22439, -48705, 248708, -9104, 16576},
});
SPAWNS.put(18496, new int[][]
{
{22441, -51176, 246055, -9984, 0},
{22441, -51699, 246190, -9984, 0},
{22442, -52060, 245956, -9984, 0},
{22442, -51565, 246433, -9984, 0},
});
SPAWNS.put(18497, new int[][]
{
{22440, -49754, 243866, -9968, -16328},
{22440, -49754, 242940, -9968, 16336},
{22440, -48733, 243858, -9968, -16208},
{22440, -48745, 242936, -9968, 16320},
{22440, -49264, 242946, -9968, 16312},
{22440, -49268, 243869, -9968, -16448},
{22440, -48186, 242934, -9968, 16576},
{22440, -48185, 243855, -9968, -16448},
});
SPAWNS.put(18498, new int[][]
{
{22411, -46355, 246375, -9984, 0},
{22411, -46167, 246160, -9984, 0},
{22393, -45952, 245748, -9984, 0},
{22394, -46428, 246254, -9984, 0},
{22393, -46490, 245871, -9984, 0},
{22394, -45877, 246309, -9984, 0},
});
SPAWNS.put(18499, new int[][]
{
{22395, -48730, 248067, -9984, 0},
{22395, -49112, 248250, -9984, 0},
});
SPAWNS.put(18500, new int[][]
{
{22393, -51954, 246475, -10848, 0},
{22394, -51421, 246512, -10848, 0},
{22394, -51404, 245951, -10848, 0},
{22393, -51913, 246206, -10848, 0},
{22394, -51663, 245979, -10848, 0},
{22394, -51969, 245809, -10848, 0},
{22412, -51259, 246357, -10848, 0},
});
SPAWNS.put(18501, new int[][]
{
{22395, -48856, 243949, -10848, 0},
{22395, -49144, 244190, -10848, 0},
});
SPAWNS.put(18502, new int[][]
{
{22441, -46471, 246135, -11704, 0},
{22441, -46449, 245997, -11704, 0},
{22441, -46235, 246187, -11704, 0},
{22441, -46513, 246326, -11704, 0},
{22441, -45889, 246313, -11704, 0},
});
SPAWNS.put(18503, new int[][]
{
{22395, -49067, 248050, -11712, 0},
{22395, -48957, 248223, -11712, 0},
});
SPAWNS.put(18504, new int[][]
{
{22413, -51748, 246138, -12568, 0},
{22413, -51279, 246200, -12568, 0},
{22413, -51787, 246594, -12568, 0},
{22413, -51892, 246544, -12568, 0},
{22413, -51500, 245781, -12568, 0},
{22413, -51941, 246045, -12568, 0},
});
SPAWNS.put(18505, new int[][]
{
{18490, -48238, 243347, -13376, 0},
{18490, -48462, 244022, -13376, 0},
{18490, -48050, 244045, -13376, 0},
{18490, -48229, 243823, -13376, 0},
{18490, -47871, 243208, -13376, 0},
{18490, -48255, 243528, -13376, 0},
{18490, -48461, 243780, -13376, 0},
{18490, -47983, 243197, -13376, 0},
{18490, -47841, 243819, -13376, 0},
{18490, -48646, 243764, -13376, 0},
{18490, -47806, 243850, -13376, 0},
{18490, -48456, 243447, -13376, 0},
});
//@formatter:on
}
public TowerOfNaia()
{
super(TowerOfNaia.class.getSimpleName(), "hellbound/AI/Zones");
addFirstTalkId(CONTROLLER);
addStartNpc(CONTROLLER);
addStartNpc(DWARVEN_GHOST);
addTalkId(CONTROLLER);
addTalkId(DWARVEN_GHOST);
addAttackId(LOCK);
addKillId(LOCK);
addKillId(MUTATED_ELPY);
addSpawnId(MUTATED_ELPY);
addKillId(SPORE_BASIC);
addSpawnId(SPORE_BASIC);
for (int npcId = SPORE_FIRE; npcId <= SPORE_EARTH; npcId++)
{
addKillId(npcId);
addSpawnId(npcId);
}
for (int npcId = ROOM_MANAGER_FIRST; npcId <= ROOM_MANAGER_LAST; npcId++)
{
addFirstTalkId(npcId);
addTalkId(npcId);
addStartNpc(npcId);
initRoom(npcId);
}
for (int npcId : TOWER_MONSTERS)
{
addKillId(npcId);
}
_lock = (L2MonsterInstance) addSpawn(LOCK, 16409, 244438, 11620, -1048, false, 0, false);
_controller = addSpawn(CONTROLLER, 16608, 244420, 11620, 31264, false, 0, false);
_counter = 90;
_despawnedSporesCount = 0;
_challengeState = 0;
_winIndex = -1;
initSporeChallenge();
spawnElpy();
}
@Override
public final String onFirstTalk(L2Npc npc, L2PcInstance player)
{
final int npcId = npc.getId();
if (npcId == CONTROLLER)
{
if (_lock == null)
{
return "18492-02.htm";
}
return "18492-01.htm";
}
else if ((npcId >= ROOM_MANAGER_FIRST) && (npcId <= ROOM_MANAGER_LAST))
{
if (_activeRooms.containsKey(npcId) && !_activeRooms.get(npcId))
{
if (player.getParty() == null)
{
player.sendPacket(SystemMessageId.YOU_MUST_BE_IN_A_PARTY_IN_ORDER_TO_OPERATE_THE_MACHINE);
return null;
}
return "manager.htm";
}
}
return super.onFirstTalk(npc, player);
}
@Override
public final String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
String htmltext = event;
// Timer. Spawns Naia Lock
if (event.equalsIgnoreCase("spawn_lock"))
{
htmltext = null;
_lock = (L2MonsterInstance) addSpawn(LOCK, 16409, 244438, 11620, -1048, false, 0, false);
_counter = 90;
}
// Timer. Depending of _challengeState despans all spawned spores, or spores, reached assembly point
else if (event.equalsIgnoreCase("despawn_total"))
{
// Spores is not attacked too long - despawn them all, reinit values
if (_challengeState == STATE_SPORE_IDLE_TOO_LONG)
{
for (L2Npc spore : _sporeSpawn)
{
if ((spore != null) && !spore.isDead())
{
spore.deleteMe();
}
}
_sporeSpawn.clear();
initSporeChallenge();
}
// Spores are moving to assembly point. Despawn all reached, check for reached spores count.
else if ((_challengeState == STATE_SPORE_CHALLENGE_SUCCESSFULL) && (_winIndex >= 0))
{
// Requirements are met, despawn all spores, spawn Epidos
if ((_despawnedSporesCount >= 10) || _sporeSpawn.isEmpty())
{
if (!_sporeSpawn.isEmpty())
{
for (L2Npc spore : _sporeSpawn)
{
if ((spore != null) && !spore.isDead())
{
spore.deleteMe();
}
}
}
_sporeSpawn.clear();
_despawnedSporesCount = 0;
int[] coords = SPORES_MERGE_POSITION[_winIndex];
addSpawn(EPIDOSES[_winIndex], coords[0], coords[1], coords[2], 0, false, 0, false);
initSporeChallenge();
}
// Requirements aren't met, despawn reached spores
else
{
Iterator<L2Npc> it = _sporeSpawn.iterator();
while (it.hasNext())
{
L2Npc spore = it.next();
if ((spore != null) && !spore.isDead() && (spore.getX() == spore.getSpawn().getX()) && (spore.getY() == spore.getSpawn().getY()))
{
spore.deleteMe();
it.remove();
_despawnedSporesCount++;
}
}
startQuestTimer("despawn_total", 3000, null, null);
}
}
}
if (npc == null)
{
return null;
}
final int npcId = npc.getId();
if (event.equalsIgnoreCase("despawn_spore") && !npc.isDead() && (_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS))
{
htmltext = null;
_sporeSpawn.remove(npc);
npc.deleteMe();
if (npcId == SPORE_BASIC)
{
spawnRandomSpore();
spawnRandomSpore();
}
else if ((npcId >= SPORE_FIRE) && (npcId <= SPORE_EARTH))
{
_despawnedSporesCount++;
if (_despawnedSporesCount < SELF_DESPAWN_LIMIT)
{
spawnOppositeSpore(npcId);
}
else
{
_challengeState = STATE_SPORE_IDLE_TOO_LONG;
startQuestTimer("despawn_total", 60000, null, null);
}
}
}
else if (event.equalsIgnoreCase("18492-05.htm"))
{
if ((_lock == null) || (_lock.getCurrentHp() > (_lock.getMaxHp() / 10)))
{
htmltext = null;
if (_lock != null)
{
_lock.deleteMe();
_lock = null;
}
cancelQuestTimers("spawn_lock");
startQuestTimer("spawn_lock", 300000, null, null);
npc.setTarget(player);
npc.doCast(SkillData.getInstance().getSkill(5527, 1));
}
}
else if (event.equalsIgnoreCase("teleport") && (_lock != null))
{
htmltext = null;
L2Party party = player.getParty();
if (party != null)
{
if (Util.checkIfInRange(3000, party.getLeader(), npc, true))
{
for (L2PcInstance partyMember : party.getMembers())
{
if (Util.checkIfInRange(2000, partyMember, npc, true))
{
partyMember.teleToLocation(-47271, 246098, -9120, true);
}
}
_lock.deleteMe();
_lock = null;
cancelQuestTimers("spawn_lock");
startQuestTimer("spawn_lock", 1200000, null, null);
}
else
{
npc.setTarget(player);
npc.doCast(SkillData.getInstance().getSkill(5527, 1));
}
}
else
{
player.teleToLocation(-47271, 246098, -9120);
_lock.deleteMe();
_lock = null;
cancelQuestTimers("spawn_lock");
startQuestTimer("spawn_lock", 1200000, null, null);
}
}
else if (event.equalsIgnoreCase("go") && _activeRooms.containsKey(npcId) && !_activeRooms.get(npcId))
{
htmltext = null;
L2Party party = player.getParty();
if (party != null)
{
removeForeigners(npcId, party);
startRoom(npcId);
ThreadPoolManager.getInstance().scheduleGeneral(new StopRoomTask(npcId), 300000);
}
else
{
player.sendPacket(SystemMessageId.YOU_MUST_BE_IN_A_PARTY_IN_ORDER_TO_OPERATE_THE_MACHINE);
}
}
return htmltext;
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, Skill skill)
{
if ((_lock != null) && (npc.getObjectId() == _lock.getObjectId()))
{
int remaindedHpPercent = (int) ((npc.getCurrentHp() * 100) / npc.getMaxHp());
if ((remaindedHpPercent <= _counter) && (_controller != null))
{
if (_counter == 50)
{
MinionList.spawnMinion(_lock, 18493);
}
else if (_counter == 10)
{
MinionList.spawnMinion(_lock, 18493);
MinionList.spawnMinion(_lock, 18493);
}
broadcastNpcSay(_controller, Say2.NPC_ALL, NpcStringId.EMERGENCY_EMERGENCY_THE_OUTER_WALL_IS_WEAKENING_RAPIDLY);
_counter -= 10;
}
}
return super.onAttack(npc, attacker, damage, isSummon, skill);
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
int npcId = npc.getId();
if (npcId == LOCK)
{
_lock = null;
cancelQuestTimers("spawn_lock");
startQuestTimer("spawn_lock", 300000, null, null);
}
else if (Arrays.binarySearch(TOWER_MONSTERS, npcId) >= 0)
{
int managerId = 0;
for (L2ZoneType zone : ZoneManager.getInstance().getZones(npc.getX(), npc.getY(), npc.getZ()))
{
if (ZONES.containsValue(zone.getId()))
{
for (int i : ZONES.keySet())
{
if (ZONES.get(i) == zone.getId())
{
managerId = i;
break;
}
}
}
}
if ((managerId > 0) && _spawns.containsKey(managerId))
{
List<L2Npc> spawned = _spawns.get(managerId);
spawned.remove(npc);
if (spawned.isEmpty() && DOORS.containsKey(managerId))
{
int[] doorList = DOORS.get(managerId);
DoorTable.getInstance().getDoor(doorList[1]).openMe();
_spawns.remove(managerId);
}
}
}
else if (npcId == MUTATED_ELPY)
{
_challengeState = STATE_SPORE_CHALLENGE_IN_PROGRESS;
markElpyRespawn();
DoorTable.getInstance().getDoor(18250025).closeMe();
ZoneManager.getInstance().getZoneById(200100).setEnabled(true);
for (int i = 0; i < 10; i++)
{
addSpawn(SPORE_BASIC, -45474, 247450, -13994, 49152, false, 0, false);
}
}
else if ((npcId == SPORE_BASIC) && (_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS))
{
_sporeSpawn.remove(npc);
spawnRandomSpore();
spawnRandomSpore();
}
else if ((npcId >= SPORE_FIRE) && (npcId <= SPORE_EARTH) && ((_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS) || (_challengeState == STATE_SPORE_CHALLENGE_SUCCESSFULL)))
{
_sporeSpawn.remove(npc);
if (_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS)
{
_despawnedSporesCount--;
int sporeGroup = getSporeGroup(npcId);
if (sporeGroup >= 0)
{
if ((npcId == SPORE_FIRE) || (npcId == SPORE_WIND))
{
_indexCount[sporeGroup] += 2;
}
else
{
_indexCount[sporeGroup] -= 2;
}
if (_indexCount[Math.abs(sporeGroup - 1)] > 0)
{
_indexCount[Math.abs(sporeGroup - 1)]--;
}
else if (_indexCount[Math.abs(sporeGroup - 1)] < 0)
{
_indexCount[Math.abs(sporeGroup - 1)]++;
}
if ((Math.abs(_indexCount[sporeGroup]) < ELEMENT_INDEX_LIMIT) && (Math.abs(_indexCount[sporeGroup]) > 0) && ((_indexCount[sporeGroup] % 20) == 0) && (getRandom(100) < 50))
{
String el = ELEMENTS_NAME[Arrays.binarySearch(ELEMENTS, npcId)];
for (L2Npc spore : _sporeSpawn)
{
if ((spore != null) && !spore.isDead() && (spore.getId() == npcId))
{
broadcastNpcSay(spore, Say2.NPC_ALL, SPORES_NPCSTRING_ID[getRandom(4)], el);
}
}
}
if (Math.abs(_indexCount[sporeGroup]) < ELEMENT_INDEX_LIMIT)
{
if ((((_indexCount[sporeGroup] > 0) && ((npcId == SPORE_FIRE) || (npcId == SPORE_WIND))) || ((_indexCount[sporeGroup] <= 0) && ((npcId == SPORE_WATER) || (npcId == SPORE_EARTH)))) && (getRandom(1000) > 200))
{
spawnOppositeSpore(npcId);
}
else
{
spawnRandomSpore();
}
}
else
// index value was reached
{
_challengeState = STATE_SPORE_CHALLENGE_SUCCESSFULL;
_despawnedSporesCount = 0;
_winIndex = Arrays.binarySearch(ELEMENTS, npcId);
int[] coord = SPORES_MERGE_POSITION[_winIndex];
for (L2Npc spore : _sporeSpawn)
{
if ((spore != null) && !spore.isDead())
{
moveTo(spore, coord);
}
}
startQuestTimer("despawn_total", 3000, null, null);
}
}
}
}
return super.onKill(npc, killer, isSummon);
}
@Override
public final String onSpawn(L2Npc npc)
{
final int npcId = npc.getId();
if (npcId == MUTATED_ELPY)
{
DoorTable.getInstance().getDoor(18250025).openMe();
ZoneManager.getInstance().getZoneById(200100).setEnabled(false);
ZoneManager.getInstance().getZoneById(200101).setEnabled(true);
ZoneManager.getInstance().getZoneById(200101).setEnabled(false);
}
else if (((npcId == SPORE_BASIC) || ((npcId >= SPORE_FIRE) && (npcId <= SPORE_EARTH))) && (_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS))
{
_sporeSpawn.add(npc);
npc.setIsRunning(false);
int[] coord = SPORES_MOVE_POINTS[getRandom(SPORES_MOVE_POINTS.length)];
npc.getSpawn().setX(coord[0]);
npc.getSpawn().setY(coord[1]);
npc.getSpawn().setZ(coord[2]);
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(coord[0], coord[1], coord[2], 0));
startQuestTimer("despawn_spore", 60000, npc, null);
}
return super.onSpawn(npc);
}
private int getSporeGroup(int sporeId)
{
int ret;
switch (sporeId)
{
case SPORE_FIRE:
case SPORE_WATER:
ret = 0;
break;
case SPORE_WIND:
case SPORE_EARTH:
ret = 1;
break;
default:
ret = -1;
}
return ret;
}
protected void initRoom(int managerId)
{
removeAllPlayers(managerId);
_activeRooms.put(managerId, false);
if (DOORS.containsKey(managerId))
{
int[] doorList = DOORS.get(managerId);
DoorTable.getInstance().getDoor(doorList[0]).openMe();
DoorTable.getInstance().getDoor(doorList[1]).closeMe();
}
if (_spawns.containsKey(managerId) && (_spawns.get(managerId) != null))
{
for (L2Npc npc : _spawns.get(managerId))
{
if ((npc != null) && !npc.isDead())
{
npc.deleteMe();
}
}
_spawns.get(managerId).clear();
_spawns.remove(managerId);
}
}
private void initSporeChallenge()
{
_despawnedSporesCount = 0;
_challengeState = 0;
_winIndex = -1;
_indexCount[0] = 0;
_indexCount[1] = 0;
ZoneManager.getInstance().getZoneById(200100).setEnabled(false);
ZoneManager.getInstance().getZoneById(200101).setEnabled(false);
ZoneManager.getInstance().getZoneById(200101).setEnabled(true);
}
private void markElpyRespawn()
{
final long respawnTime = (getRandom(43200, 216000) * 1000) + System.currentTimeMillis();
GlobalVariablesManager.getInstance().set("elpy_respawn_time", respawnTime);
}
private int moveTo(L2Npc npc, int[] coords)
{
int time = 0;
if (npc != null)
{
double distance = npc.calculateDistance(coords[0], coords[1], coords[2], true, false);
int heading = Util.calculateHeadingFrom(npc.getX(), npc.getY(), coords[0], coords[1]);
time = (int) ((distance / npc.getWalkSpeed()) * 1000);
npc.setIsRunning(false);
npc.disableCoreAI(true);
npc.setIsNoRndWalk(true);
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(coords[0], coords[1], coords[2], heading));
npc.getSpawn().setX(coords[0]);
npc.getSpawn().setY(coords[1]);
npc.getSpawn().setZ(coords[2]);
}
return time == 0 ? 100 : time;
}
private void spawnElpy()
{
final long respawnTime = GlobalVariablesManager.getInstance().getLong("elpy_respawn_time", 0);
if (respawnTime <= System.currentTimeMillis())
{
addSpawn(MUTATED_ELPY, -45474, 247450, -13994, 49152, false, 0, false);
}
else
{
ThreadPoolManager.getInstance().scheduleGeneral(() -> addSpawn(MUTATED_ELPY, -45474, 247450, -13994, 49152, false, 0, false), respawnTime - System.currentTimeMillis());
}
}
private L2Npc spawnRandomSpore()
{
return addSpawn(getRandom(SPORE_FIRE, SPORE_EARTH), -45474, 247450, -13994, 49152, false, 0, false);
}
private L2Npc spawnOppositeSpore(int srcSporeId)
{
final int idx = Arrays.binarySearch(ELEMENTS, srcSporeId);
return idx >= 0 ? addSpawn(OPPOSITE_ELEMENTS[idx], -45474, 247450, -13994, 49152, false, 0, false) : null;
}
private void startRoom(int managerId)
{
_activeRooms.put(managerId, true);
if (DOORS.containsKey(managerId))
{
int[] doorList = DOORS.get(managerId);
DoorTable.getInstance().getDoor(doorList[0]).closeMe();
}
if (SPAWNS.containsKey(managerId))
{
int[][] spawnList = SPAWNS.get(managerId);
List<L2Npc> spawned = new FastList<>();
for (int[] spawn : spawnList)
{
L2Npc spawnedNpc = addSpawn(spawn[0], spawn[1], spawn[2], spawn[3], spawn[4], false, 0, false);
spawned.add(spawnedNpc);
}
if (!spawned.isEmpty())
{
_spawns.put(managerId, spawned);
}
}
}
private void removeForeigners(int managerId, L2Party party)
{
if ((party != null) && ZONES.containsKey(managerId) && (ZoneManager.getInstance().getZoneById(ZONES.get(managerId)) != null))
{
L2ZoneType zone = ZoneManager.getInstance().getZoneById(ZONES.get(managerId));
for (L2PcInstance player : zone.getPlayersInside())
{
if (player != null)
{
L2Party charParty = player.getParty();
if ((charParty == null) || (charParty.getLeaderObjectId() != party.getLeaderObjectId()))
{
player.teleToLocation(16110, 243841, 11616);
}
}
}
}
}
private void removeAllPlayers(int managerId)
{
if (ZONES.containsKey(managerId) && (ZoneManager.getInstance().getZoneById(ZONES.get(managerId)) != null))
{
L2ZoneType zone = ZoneManager.getInstance().getZoneById(ZONES.get(managerId));
for (L2PcInstance player : zone.getPlayersInside())
{
if (player != null)
{
player.teleToLocation(16110, 243841, 11616);
}
}
}
}
private class StopRoomTask implements Runnable
{
private final int _managerId;
public StopRoomTask(int managerId)
{
_managerId = managerId;
}
@Override
public void run()
{
initRoom(_managerId);
}
}
}

View File

@@ -0,0 +1,4 @@
<html><body>Ingenious Contraption:<br>
You are unable to fathom the purpose for which the machine was built. You do see what looks like a switch. Would you like to flip it?<br>
<a action="bypass -h Quest TowerOfNaia go">Flip the switch.</a>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Spooky Tombstone:<br>
You see arcane symbols and intricate patterns engraved on the tombstone, which seems to radiate magical power. Between the symbols are carved 5 deep grooves, each of which looks just wide enough to insert a small disc. Interestingly, each groove is lined with a different metal: tin, lead, iron, copper and white gold.<br>
<a action="bypass -h npc_%objectId%_Quest TullyWorkshop">Insert Tully's medal in the groove.</a>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Spooky Tombstone:<br>
You see arcane symbols and intricate patterns engraved on the tombstone, which seems to radiate magical power. Between the symbols are carved 5 deep grooves, each of which looks just wide enough to insert a small disc. Interestingly, each groove is lined with a different metal: tin, lead, iron, copper and white gold.<br>
(In order to advance, you must form a party that possesses all of Tully's 5 medals; each member must own a different type.)
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Spooky Tombstone:<br>
You don't have enough medals to fill all 5 grooves.<br>
(In order to advance, you must form a party that possesses all of Tully's 5 medals; each member must own a different type.)
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Spooky Tombstone:<br>
The 5 metal grooves shine with an eery, baleful light.<br>
(In order to advance, you must form a party that possesses all of Tully's 5 medals; each member must own a different type.)
</body></html>

View File

@@ -0,0 +1,8 @@
<html><body>Old Dwarven Ghost:<br>
...I am Tully...shielded by my spirit...the defense system of this Steel Citadel is impenetrable...<br>
You cannot enter...<br>
You will be killed by the intruder-destruction device...unless... are you willing to help me...?<br>
<a action="bypass -h Quest TullyWorkshop 32370-01a.htm">Ask him how to help.</a><br>
<a action="bypass -h Quest TullyWorkshop 32370-01d.htm">Ask him about the Spooky Tombstone.</a><br>
<a action="bypass -h npc_%objectId%_Quest">Quest.</a>
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Old Dwarven Ghost:<br>
I was...enticed by those devils...and built this castle for them...but their greed did not stop there... They stole my soul and made me their slave forever...<br>
The body you just destroyed...was only a shell... This castle and I have become one... I cannot die even if I want to...<br>
My soul is watched by Darion...who resides atop <font color="LEVEL">this tower</font>... Please destroy him...so that I... can finally rest in peace...<br>
<a action="bypass -h Quest TullyWorkshop 32370-01b.htm">Accept.</a><br>
<a action="bypass -h Quest TullyWorkshop 32370-01c.htm">Refuse.</a>
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Old Dwarven Ghost:<br>
Thank the gods... Finally someone willing to help me...<br>
I will send you...up the stairs of this tower... That is all I can do for now...<br>
You will encounter powerful devils...who will try your strength...<br>But unintentionally... some of the devils...may be of help to you...<br>
Watch out... and increase your strength...in order to survive in this tower...<br>
<a action="bypass -h Quest TullyWorkshop teleport">Ask him to send you to the next floor.</a>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Old Dwarven Ghost:<br>
I knew it... Then...perish...with my tower...
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Old Dwarven Ghost:<br>
The gravestone...is a result of...my technology.<br>
It is a portal...designed to <font color="LEVEL">transport...personnel and equipment...to a special workshop</font> filled with evil creatures...<br>
Only those qualified...may use it...<br>
<a action="bypass -h Quest TullyWorkshop 32370-01e.htm">Ask how to use the gravestone.</a><br>
<a action="bypass -h Quest TullyWorkshop 32370-01a.htm">Ask again how to help.</a>
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Old Dwarven Ghost:<br>
The Tombstone is...a complicated 5-stage mechanism... It has a built-in...safety lock device... To release the device, you need each key... You need <font color="LEVEL">5 small metal medals</font>.<br>
Those medals...can be found...somewhere in this maze...<br>
The medal's unique owner recognition device won't allow one person to have more than one medal...so don't go looking for the medals...by yourself...<br>
<a action="bypass -h Quest TullyWorkshop 32370-01a.htm">Ask again how you can help.</a>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Old Dwarven Ghost:<br>
Ah... No... The particle formation... between the targets... If it's too thick... Activating... the gap control device... is like... committing... suicide... In other words... if you aren't properly gathered together... it means... that I can't send you... to where... you wish to go...
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Spirit of an Aged Dwarf:<br>
Thank you for...releasing my soul... In return...I will teach you the key to my technology...so that you may enter...the <font color="LEVEL">Tower of Naia</font>....<br>
<a action="bypass -h Quest TullyWorkshop 32370-02a.htm">Ask how.</a>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Old Dwarven Ghost:<br>
On the upper level...of the sealed Tower of Naia...is a lock that blocks...the only exit...in order to <font color="LEVEL">prevent energy from leaking outside...and a control device</font> for the lock...<br>
<a action="bypass -h Quest TullyWorkshop 32370-02b.htm">Keep listening.</a>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Old Dwarven Ghost:<br>
Because the exterior of this lock...is the super metal Tolecium...it is virtually invicible...to physical attack... However, it is...very <font color="LEVEL">sensitive to attribute energy</font>...If it is continually exposed to...energy with nature attributes...its surface will be temporarily weakened... Below 1/10, the different energy intensities...between the exterior...and the interior...may cause a strong...extruding effect.<br>
If you are lucky...you can take advantage of that effect...to enter the Tower of Naia...<br>
<a action="bypass -h Quest TullyWorkshop 32370-02c.htm">Keep listening.</a>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Old Dwarven Ghost:<br>
But...the concentrated magical energy...within the lock device...may cause mutations...and abnormalities...<br>
Strange creatures...for example...
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Old Dwarven Ghost:<br>
You're here...<br>
Dark magic was forced in...and is slowly rotting this place...within the Tower... It has altered it...from my original design... I wish I could help you... I knew you would come here...after overcoming the barriers...<br>
But you cannot relax yet... The most formidable obstacle still awaits you... Before you face the final trial...listen to this old man's advice, I pray...<br>
<a action="bypass -h Quest TowerOfNaia 32370-03a.htm">Listen</a>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Complex Machinery:<br>
You see some very intricate controls, but nothing seems to be functioning at the moment.<br>
<a action="bypass -h Quest TullyWorkshop touch_device">Touch various controls to try and make them move.</a><br>
<a action="bypass -h npc_%objectId%_Quest">Quest.</a>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Complex Machinery:<br>
You see some very intricate controls, but nothing seems to be functioning at the moment.<br>
<a action="bypass -h Quest TullyWorkshop touch_device">Try to determine the machine's purpose and operational principles.</a><br>
<a action="bypass -h talk_select">Quest.</a>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Ingenious Contraption:<br>
You see an array of complex and bewildering equipment.<br>
One shining button stands out among the rest.<br>
<a action="bypass -h Quest TullyWorkshop take_reward">Press the button.</a>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Complex Machinery:<br>
The controls suddenly come to life, whirring and shining in various colors.<br>
One large glowing button particularly stands out from the rest.<br>
<a action="bypass -h Quest TullyWorkshop take_reward">Press the big button.</a>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Complex Machinery:<br>
You were able to successfully identify the nonfunctional part and make some quick repairs to it.<br>
The controls light up and begin to hum softly. One large glowing button particularly stands out from the rest.<br>
<a action="bypass -h Quest TullyWorkshop take_reward">Press the big button.</a>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Complex Machinery:<br>
The controls are still dark and unmovable.<br>
(You must wait until the device becomes operational before attempting anything else.)
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Ingenious Contraption:<br>
With a loud noise, the red light on the front of the machine begins to flash on and off.<br>
(Only characters who do not possess any of Tully's Medals may receive the medal. It may only be received once.)<br>
<a action="bypass -h Quest TullyWorkshop take_reward">Press the large button again.</a>
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Mysterious Agent:<br>
Ah, Darion's favorite retainer... They overthrew him at the peak of his power -- an accomplishment not to be underestimated.<br>
My superiors are interested in you. Will you follow me and them?<br>
<a action="bypass -h Quest TullyWorkshop tele_to_7th_floor">Accept.</a><br>
<a action="bypass -h Quest TullyWorkshop despawn_agent">Refuse.</a><br>
<a action="bypass -h npc_%objectId%_Quest">Quest.</a>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Mysterious Agent:<br>
Where is your leader? I am too busy to speak with you commoners...<br>
(Only the party leader may speak with him.)
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Mysterious Agent:<br>
If you wish to have the masters' blesing, you must concentrate. Do not all come here at once, either!<br>
(Some members of your party are too far away to allow your group to move.)
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Mysterious Agent:<br>
Be welcome, then. Salute the lords behind me politely. Yes, well done... Now then, my masters wish a favor from you. As you are of humble origin and likely unable to understand their words, I will deliver their message.<br>
<a action="bypass -h Quest TullyWorkshop 32372-02a.htm">Listen to his story.</a>
</body></html>

View File

@@ -0,0 +1,8 @@
<html><body>Mysterious Agent:<br>
It is obvious that only the strongest lords should govern this citadel.<br>
Although they have already humbled the devils who previously ruled this tower, there is one more obstacle to their victory: that arrogant beast called <font color="LEVEL">Darion</font>, Beleth's chief lackey.<br>
Once he is eliminated, my superiors will be free to become the true masters of this tower.<br>
Well...heh heh...perhaps just one of the lords...<br>
<a action="bypass -h Quest TullyWorkshop 32372-02b.htm">Ask why the lords cannot do it by themselves.<br>
<a action="bypass -h Quest TullyWorkshop 32372-02c.htm">Ask about compensation.</a>
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Mysterious Agent:<br>
It is not that they do not have the power to defeat Darion, but...<br>
The terms of the treaty they signed with Beleth specified that they could not act against Darion directly. At least that is what I heard...<br>
<a action="bypass -h Quest TullyWorkshop 32372-02c.htm">Ask about compensation</a><br>
<a action="bypass -h Quest TullyWorkshop buff">Agree to defeat Darion.</a><br>
<a action="bypass -h Quest TullyWorkshop refuse">Refuse.</a>
</body></html>

View File

@@ -0,0 +1,8 @@
<html><body>Mysterious Agent:<br>
Compensation? Silence! You are fortunate that my masters did not hear you utter such a profane word.<br>
Do you really think that defeating a couple of minions is comparable to defeating Darion? If you agree to face Darion, our lords may choose to bless you <font color="LEVEL">greenhorns</font> in your battle.<br>
Of course, that depends on their feelings towards you at the time.<br>
<a action="bypass -h Quest TullyWorkshop 32372-02b.htm">Ask why the lords themselves cannot fight with Darion.</a><br>
<a action="bypass -h Quest TullyWorkshop buff">Agree to defeat Darion.</a><br>
<a action="bypass -h Quest TullyWorkshop refuse">Refuse.</a>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Dorian:<br>
Some members of your party are too far away to allow me to help them enter.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Dorian:<br>
I will only speak with the party leader.
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Teleportation Cubic:<br>
It appears that it is possible to move to a different floor.<br>
<a action="bypass -h Quest TullyWorkshop cube68_tp_1">Go to the 8th floor.</a><br>
<a action="bypass -h npc_%objectId%_Quest">Quest.</a>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Teleportation Cubic:<br>
It appears that it is possible to move to a different floor.<br>
<a action="bypass -h Quest TullyWorkshop cube68_tp_2">Go up to the rooftop.</a><br>
<a action="bypass -h npc_%objectId%_Quest">Quest.</a>
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Teleportation Cubic:<br>
It appears that it is possible to move to a different floor.<br>
<a action="bypass -h Quest TullyWorkshop cube68_tp_0">Return to the 6th floor.</a><br>
<a action="bypass -h Quest TullyWorkshop cube68_tp_1">Return to the 8th floor.</a><br>
<a action="bypass -h npc_%objectId%_Quest">Quest.</a>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Teleportation Cubic:<br>
Because of the strong energy field, movement is currently impossible.<br>
<a action="bypass -h npc_%objectId%_Quest">Quest.</a>
</body></html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
<html><body>Tully's Gatekeeper:<br>
The mysterious grave resembles machinery. It looks operational...<br>
<a action="bypass -h Quest TullyWorkshop open">Attempt to operate the machine.</a>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Tully's Gatekeeper:<br>
The machine does not appear to be functional. It looks like someone attempted to operate it improperly and damaged it.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Tully's Gatekeeper:<br>
The machine is currently not operational, and it will take some time to repair it.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Tully's Gatekeeper:<br>
The mysterious gravestone seems to resemble a machine. It looks operational.<br>
<a action="bypass -h Quest TullyWorkshop enter">Operate the machine.</a>
</body></html>