Helios branch.

This commit is contained in:
MobiusDev
2017-01-20 09:19:54 +00:00
parent 34b1bd66b4
commit 37cff65ca0
20608 changed files with 3742400 additions and 0 deletions

View File

@@ -0,0 +1,264 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances;
import java.util.List;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.enums.InstanceReenterType;
import com.l2jmobius.gameserver.instancemanager.InstanceManager;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.PcCondOverride;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.instancezone.InstanceTemplate;
import com.l2jmobius.gameserver.network.NpcStringId;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
import ai.AbstractNpcAI;
/**
* Abstract class for Instances.
* @author FallenAngel
*/
public abstract class AbstractInstance extends AbstractNpcAI
{
private final int[] _templateIds;
public AbstractInstance(int... templateId)
{
if (templateId.length == 0)
{
throw new IllegalStateException("No template ids were provided!");
}
_templateIds = templateId;
}
public int[] getTemplateId()
{
return _templateIds;
}
public boolean isInInstance(Instance instance)
{
return (instance != null) && CommonUtil.contains(_templateIds, instance.getTemplateId());
}
/**
* Get instance world associated with {@code player}.<br>
* @param player player who wants get instance world
* @return instance world if found, otherwise null
*/
public Instance getPlayerInstance(L2PcInstance player)
{
return InstanceManager.getInstance().getPlayerInstance(player, false);
}
/**
* Show an on screen message to each player inside instance.
* @param instance instance where message should be broadcasted
* @param npcStringId the NPC string to display
* @param position the position of the message on the screen
* @param time the duration of the message in milliseconds
* @param params values of parameters to replace in the NPC String (like S1, C1 etc.)
*/
public void showOnScreenMsg(Instance instance, NpcStringId npcStringId, int position, int time, String... params)
{
instance.broadcastPacket(new ExShowScreenMessage(npcStringId, position, time, params));
}
/**
* Show an on screen message to each player inside instance.
* @param instance instance where message should be broadcasted
* @param npcStringId the NPC string to display
* @param position the position of the message on the screen
* @param time the duration of the message in milliseconds
* @param showEffect show visual effect near text
* @param params values of parameters to replace in the NPC String (like S1, C1 etc.)
*/
public void showOnScreenMsg(Instance instance, NpcStringId npcStringId, int position, int time, boolean showEffect, String... params)
{
instance.broadcastPacket(new ExShowScreenMessage(npcStringId, position, time, showEffect, params));
}
/**
* Put player into instance world.<br>
* If instance world doesn't found for player then try to create new one.
* @param player player who wants to enter into instance
* @param npc NPC which allows to enter into instance
* @param templateId template ID of instance where player wants to enter
*/
protected final void enterInstance(L2PcInstance player, L2Npc npc, int templateId)
{
Instance instance = getPlayerInstance(player);
if (instance != null) // Player has already any instance active
{
if (instance.getTemplateId() != templateId)
{
player.sendPacket(SystemMessageId.YOU_HAVE_ENTERED_ANOTHER_INSTANT_ZONE_THEREFORE_YOU_CANNOT_ENTER_CORRESPONDING_DUNGEON);
return;
}
onEnter(player, instance, false);
}
else
{
// Get instance template
final InstanceManager manager = InstanceManager.getInstance();
final InstanceTemplate template = manager.getInstanceTemplate(templateId);
if (template == null)
{
_log.warning("Player " + player.getName() + " (" + player.getObjectId() + ") wants to create instance with unknown template id " + templateId + "!");
return;
}
// Get instance enter scope
final List<L2PcInstance> enterGroup = template.getEnterGroup(player);
// When nobody can enter
if (enterGroup == null)
{
_log.warning("Instance " + template.getName() + " (" + templateId + ") has invalid group size limits!");
return;
}
// Validate conditions for group
if (!player.canOverrideCond(PcCondOverride.INSTANCE_CONDITIONS) && (!template.validateConditions(enterGroup, npc, this::showHtmlFile) || !validateConditions(enterGroup, npc, template)))
{
return;
}
// Check if maximum world count limit is exceeded
if ((template.getMaxWorlds() != -1) && (manager.getWorldCount(templateId) >= template.getMaxWorlds()))
{
player.sendPacket(SystemMessageId.THE_NUMBER_OF_INSTANT_ZONES_THAT_CAN_BE_CREATED_HAS_BEEN_EXCEEDED_PLEASE_TRY_AGAIN_LATER);
return;
}
// Check if any player from enter group has active instance
for (L2PcInstance member : enterGroup)
{
if (getPlayerInstance(member) != null)
{
enterGroup.forEach(p -> p.sendPacket(SystemMessageId.YOU_HAVE_ENTERED_ANOTHER_INSTANT_ZONE_THEREFORE_YOU_CANNOT_ENTER_CORRESPONDING_DUNGEON));
return;
}
}
// Create new instance for enter player group
instance = manager.createInstance(template, player);
// Move each player from enter group to instance
for (L2PcInstance member : enterGroup)
{
instance.addAllowed(member);
onEnter(member, instance, true);
}
// Apply condition success effects
template.applyConditionEffects(enterGroup);
// Set re-enter for instances with re-enter on start
if (instance.getReenterType().equals(InstanceReenterType.ON_ENTER))
{
instance.setReenterTime();
}
}
}
/**
* This function is called when player enter into instance trough NPC.
* @param player player who enter
* @param instance instance world where player enter
* @param firstEnter when {@code true} player enter first time, otherwise player entered multiple times
*/
protected void onEnter(L2PcInstance player, Instance instance, boolean firstEnter)
{
teleportPlayerIn(player, instance);
}
/**
* This method is used to teleport player into instance by start NPC.<br>
* When you override whole method, XML teleport data won't be applied.
* @param player player which should be teleported
* @param instance instance where player should be teleported
*/
protected void teleportPlayerIn(L2PcInstance player, Instance instance)
{
final Location loc = instance.getEnterLocation();
if (loc != null)
{
player.teleToLocation(loc, instance);
}
else
{
_log.warning("Missing start location for instance instance.getName() (" + instance.getId() + ")");
}
}
/**
* This method is used to teleport player from instance world by NPC.
* @param player player which should be ejected
* @param instance instance from player should be removed
*/
protected void teleportPlayerOut(L2PcInstance player, Instance instance)
{
instance.ejectPlayer(player);
}
/**
* Sets instance to finish state. <br>
* See {@link Instance#finishInstance()} for more details.
* @param player player used for determine current instance world
*/
protected void finishInstance(L2PcInstance player)
{
final Instance inst = player.getInstanceWorld();
if (inst != null)
{
inst.finishInstance();
}
}
/**
* Sets instance to finish state.<br>
* See {@link Instance#finishInstance(int)} for more details.
* @param player player used for determine current instance world
* @param delay finish delay in minutes
*/
protected void finishInstance(L2PcInstance player, int delay)
{
final Instance inst = player.getInstanceWorld();
if (inst != null)
{
inst.finishInstance(delay);
}
}
/**
* This method is supposed to be used for validation of additional conditions that are too much specific to instance world (to avoid useless core conditions).<br>
* These conditions are validated after conditions defined in XML template.
* @param group group of players which wants to enter (first player inside list is player who make enter request)
* @param npc NPC used for enter
* @param template template of instance world which should be created
* @return {@code true} when conditions are valid, otherwise {@code false}
*/
protected boolean validateConditions(List<L2PcInstance> group, L2Npc npc, InstanceTemplate template)
{
return true;
}
}

View File

@@ -0,0 +1,243 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.CastleDungeon;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ThreadPoolManager;
import com.l2jmobius.gameserver.enums.QuestSound;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.entity.Castle;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.instancezone.InstanceTemplate;
import com.l2jmobius.gameserver.model.quest.QuestState;
import instances.AbstractInstance;
import quests.Q00512_BladeUnderFoot.Q00512_BladeUnderFoot;
/**
* <b>Castle dungeon</b> instance for quest <b>BladeUnderFoot (512)</b>
* @author Mobius
*/
public final class CastleDungeon extends AbstractInstance
{
// NPCs
private static final Map<Integer, Integer> NPCS = new HashMap<>();
static
{
NPCS.put(36403, 13); // Gludio
NPCS.put(36404, 14); // Dion
NPCS.put(36405, 15); // Giran
NPCS.put(36406, 16); // Oren
NPCS.put(36407, 17); // Aden
NPCS.put(36408, 18); // Innadril
NPCS.put(36409, 19); // Goddard
NPCS.put(36410, 20); // Rune
NPCS.put(36411, 21); // Schuttgart
}
// Monsters
private static final int[] RAIDS1 =
{
25546,
25549,
25552
};
private static final int[] RAIDS2 =
{
25553,
25554,
25557,
25560
};
private static final int[] RAIDS3 =
{
25563,
25566,
25569
};
// Item
private static final int MARK = 9798;
// Locations
private static final Location SPAWN_LOC = new Location(12230, -49139, -3013);
// Misc
private static final int MARK_COUNT = 2520;
private static final long REENTER = 24 * 3600000; // 24 hours
private static final Map<Integer, Long> REENETER_HOLDER = new ConcurrentHashMap<>();
public CastleDungeon()
{
super(NPCS.values().stream().mapToInt(Integer::valueOf).toArray());
// NPCs
addStartNpc(NPCS.keySet());
addTalkId(NPCS.keySet());
// Monsters
addKillId(RAIDS1);
addKillId(RAIDS2);
addKillId(RAIDS3);
// Instance
addInstanceCreatedId(NPCS.values());
addInstanceDestroyId(NPCS.values());
}
@Override
public String onTalk(L2Npc npc, L2PcInstance player)
{
final int npcId = npc.getId();
if (NPCS.containsKey(npcId))
{
enterInstance(player, npc, NPCS.get(npcId));
}
return null;
}
@Override
public String onKill(L2Npc npc, L2PcInstance player, boolean isSummon)
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
if (CommonUtil.contains(RAIDS3, npc.getId()))
{
// Get players with active quest
final List<L2PcInstance> members = new ArrayList<>();
for (L2PcInstance member : world.getPlayers())
{
final QuestState qs = member.getQuestState(Q00512_BladeUnderFoot.class.getSimpleName());
if ((qs != null) && qs.isCond(1))
{
members.add(member);
}
}
// Distribute marks between them
if (!members.isEmpty())
{
final long itemCount = MARK_COUNT / members.size();
for (L2PcInstance member : members)
{
giveItems(member, MARK, itemCount);
playSound(member, QuestSound.ITEMSOUND_QUEST_ITEMGET);
}
}
world.finishInstance();
}
else
{
world.incStatus();
spawnRaid(world);
}
}
return super.onKill(npc, player, isSummon);
}
@Override
public void onInstanceCreated(Instance instance, L2PcInstance player)
{
// Put re-enter for instance
REENETER_HOLDER.put(instance.getTemplateId(), System.currentTimeMillis() + REENTER);
// Schedule spawn of first raid
spawnRaid(instance);
}
@Override
protected boolean validateConditions(List<L2PcInstance> group, L2Npc npc, InstanceTemplate template)
{
final L2PcInstance groupLeader = group.get(0);
final Castle castle = npc.getCastle();
if (castle == null)
{
showHtmlFile(groupLeader, "noProperPledge.html");
return false;
}
else if (REENETER_HOLDER.containsKey(template.getId()))
{
final long time = REENETER_HOLDER.get(template.getId());
if (time > System.currentTimeMillis())
{
showHtmlFile(groupLeader, "enterRestricted.html");
return false;
}
REENETER_HOLDER.remove(template.getId());
}
return true;
}
@Override
public void onInstanceDestroy(Instance instance)
{
// Stop running spawn task
final ScheduledFuture<?> task = instance.getParameters().getObject("spawnTask", ScheduledFuture.class);
if ((task != null) && !task.isDone())
{
task.cancel(true);
}
instance.setParameter("spawnTask", null);
}
/**
* Spawn raid boss according to instance status.
* @param instance instance world where instance should be spawned
*/
private void spawnRaid(Instance instance)
{
final ScheduledFuture<?> spawnTask = ThreadPoolManager.getInstance().scheduleGeneral(() ->
{
// Get template id of raid
final int npcId;
switch (instance.getStatus())
{
case 0:
{
npcId = RAIDS1[getRandom(RAIDS1.length)];
break;
}
case 1:
{
npcId = RAIDS2[getRandom(RAIDS2.length)];
break;
}
default:
{
npcId = RAIDS3[getRandom(RAIDS3.length)];
}
}
// Spawn raid
addSpawn(npcId, SPAWN_LOC, false, 0, false, instance.getId());
// Unset spawn task reference
instance.setParameter("spawnTask", null);
}, 2, TimeUnit.MINUTES);
// Save timer to instance world
instance.setParameter("spawnTask", spawnTask);
}
public static void main(String[] args)
{
new CastleDungeon();
}
}

View File

@@ -0,0 +1,4 @@
<html><body>Prison is currently closed.<br>
You can visit the prison once every 24 hours with your clan.<br>
If prison is closed, check with your clan members to see when the last session started.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>
Only a party may enter the prison. Once you visit the prison, you may not attend another one for at least 24 hours.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>
To enter the prison, your party must include at least 2 people.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>
The clan does not possess this castle.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Kain Van Halter:<br>
Took you long enough. I thought you'd be as swift as the wind, but you're slow as molasses. I got here first with <font color="LEVEL">Ferin</font>. She's the daughter of... Venir, was it? We're finally going to find the Grail. Let's get going.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ChamberOfProphecies teleport">"Let's go."</button>
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Kain Van Halter:<br>
The Grail was literally just a simple grail. it was not what I was looking for.<br>
But, I have no regrets about taking this journey with you.<br>
If it's not the Grail, I'm not interested in it. Do whatever you wish with it.<br>
Let us part ways now.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ChamberOfProphecies exit">"Good bye."</button>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Kain Van Halter:<br>
I hope that the Grail it's talking about is the same one I'm looking for.<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h Quest ChamberOfProphecies status">Quest</button>
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>
Mysterious Wizard:<br>
<font color="LEVEL">Nidrah</font>? I have no idea what you're talking about. Oh, you mean thr vampire shouted at me?<br>
She was cursed with dark magic and was not herself. You believe her?<br>
More importantly, I want to talk about the object you have.<br>
<Button ALIGN="LEFT" ICON="Normal" action="bypass -h Quest ChamberOfProphecies 33980-02.html">"Do you know what this is?"</button>
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>
Mysterious Wizard:<br>
This is a extremely rare gem called <font color="LEVEL">Atelia</font>. To my knowledge, it's only found in the Wind Spirit Realm.<br>
I heard that an Atelia can recognize its owner. You see how it turned back to its original form when you touched it? That means you are the rightful owner.<br>
<Button ALIGN="LEFT" ICON="Normal" action="bypass -h Quest ChamberOfProphecies 33980-03.html">"Why are You telling me this?"</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Mysterious Wizard:<br>
Do You still suspect me? I'm just helping you so that you won't lose what is yours. The Atelia will only end up in Queen's hands back in town, wich means ir will be the Queen's power. Don't you want the power for yourself?<br>
<Button ALIGN="LEFT" ICON="Normal" action="bypass -h Quest ChamberOfProphecies 33980-04.html">"Don't lie to me. This is not mine."</button>
<Button ALIGN="LEFT" ICON="Normal" action="bypass -h Quest ChamberOfProphecies 33980-04.html">"This is the power I will have?"</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Mysterious Wizard:<br>
Really? Having Atelia's power will make you powerful beyond your imagination. You want to give up your last chance to use this power for yourself?<br>
<Button ALIGN="LEFT" ICON="Normal" action="bypass -h Quest ChamberOfProphecies 33980-05.html">"Do not tempt me to covet what is not mine!"</button>
<Button ALIGN="LEFT" ICON="Normal" action="bypass -h Quest ChamberOfProphecies 33980-05.html">"Well, if you say it's mine, then...hehe"</button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Mysterious Wizard:<br>
I'm sorry to hear that. I wanted to see you become more powerful. That's all time has permitted us. Perhaps I will see you later?
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>
Mysterious Wizard:<br>
Long time no see. How do you do?<br>
<Button ALIGN="LEFT" ICON="Normal" action="bypass -h Quest ChamberOfProphecies 33980-01.html">"You are... Nidhar, was it?"</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Grail:<br>
This is the grail the Prophecy Machine was referring to.<br>
Do you think this is THE grail?<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ChamberOfProphecies 33996-02.html">Can I touch it?</button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Grail:<br>
The grail disappeared, leaving behind a gem.
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Grail:<br>
<center>The Atelias's powers are sealed here.</center><br1>
It will regan its from, when the masters of its power comes.<br1>
<center>-Zepharon, King of Ertheia</center><br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h Quest ChamberOfProphecies 33996-01.html">Quest</button>
</body></html>

View File

@@ -0,0 +1,468 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.ChamberOfProphecies;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import com.l2jmobius.gameserver.network.NpcStringId;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
import com.l2jmobius.gameserver.network.serverpackets.ExShowUsm;
import com.l2jmobius.gameserver.network.serverpackets.NpcSay;
import com.l2jmobius.gameserver.network.serverpackets.PlaySound;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import instances.AbstractInstance;
import quests.Q10753_WindsOfFateChoices.Q10753_WindsOfFateChoices;
/**
* Chamber of Propercies instance.
* @author Gigi
*/
public final class ChamberOfProphecies extends AbstractInstance
{
// NPCs
private static final int KAIN_VAN_HALTER = 33979;
private static final int VAN_HALTER = 33999;
private static final int FERIN = 34001;
private static final int GRAIL = 33996;
private static final int MYSTERIOUS_WIZARD = 33980;
// Misc
private static final int TEMPLATE_ID = 255;
private static final int PROPHECY_MACHINE = 39540;
private static final int ATELIA = 39542;
private static final Location FIRST_ROOM_LOC = new Location(-88503, 184754, -10440, 48891);
// Monsters
private static final int[] ATTACABLE_MONSTERS =
{
19568, // Sacred Wizard
19569, // Sacred Soldier
19570, // Sacred Slayer
19571, // Makkum
19572, // Abyssal Shadow
19573, // Secluded Shadow
};
public ChamberOfProphecies()
{
super(TEMPLATE_ID);
addStartNpc(KAIN_VAN_HALTER);
addFirstTalkId(KAIN_VAN_HALTER, GRAIL, MYSTERIOUS_WIZARD);
addTalkId(KAIN_VAN_HALTER, GRAIL, MYSTERIOUS_WIZARD);
addSeeCreatureId(FERIN);
addSeeCreatureId(VAN_HALTER);
addKillId(ATTACABLE_MONSTERS);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
String htmltext = null;
final QuestState qs = player.getQuestState(Q10753_WindsOfFateChoices.class.getSimpleName());
if (event.equals("enterInstance"))
{
enterInstance(player, npc, TEMPLATE_ID);
if (hasQuestItems(player, PROPHECY_MACHINE))
{
takeItems(player, PROPHECY_MACHINE, 1);
}
qs.setCond(16, true);
}
else
{
final Instance world = npc.getInstanceWorld();
if (isInInstance(world))
{
switch (event)
{
case "33979-01.html":
case "33996-01.html":
case "33980-01.html":
case "33980-02.html":
{
htmltext = event;
break;
}
case "33996-02.html":
{
world.broadcastPacket(ExShowUsm.USM_Q015_E); // TODO not show usim movie
world.despawnGroup("q10753_16_instance_grail");
world.spawnGroup("q10753_16_instance_wizard");
giveItems(player, ATELIA, 1);
showOnScreenMsg(player, NpcStringId.TALK_TO_THE_MYSTERIOUS_WIZARD, ExShowScreenMessage.TOP_CENTER, 6000);
htmltext = event;
break;
}
case "33980-03.html":
{
showOnScreenMsg(player, NpcStringId.THIS_CHOICE_CANNOT_BE_REVERSED, ExShowScreenMessage.TOP_CENTER, 6000);
htmltext = event;
break;
}
case "33980-04.html":
{
showOnScreenMsg(player, NpcStringId.THIS_CHOICE_CANNOT_BE_REVERSED, ExShowScreenMessage.TOP_CENTER, 6000);
htmltext = event;
break;
}
case "33980-05.html":
{
world.spawnGroup("q10753_16_instance_halter_2");
world.setStatus(6);
startQuestTimer("DESPAWN_WIZARD", 2000, npc, player);
htmltext = event;
break;
}
case "status":
{
if (world.isStatus(0))
{
htmltext = "33979-01.html";
break;
}
htmltext = "33979-02.html";
break;
}
case "teleport":
{
player.teleToLocation(FIRST_ROOM_LOC);
world.spawnGroup("q10753_16_instance_halter_1_1");
world.spawnGroup("wof_room1");
break;
}
case "ATTACK":
case "ATTACK1":
case "ATTACK2":
{
npc.setIsRunning(true);
((L2Attackable) npc).setCanReturnToSpawnPoint(false);
((L2Attackable) npc).setCanStopAttackByTime(false);
if (npc.isScriptValue(0) && world.getAliveNpcs(ATTACABLE_MONSTERS).isEmpty())
{
npc.setTarget(player);
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, player);
}
else
{
if (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
{
L2World.getInstance().forEachVisibleObjectInRange(npc, L2Npc.class, 3000, chars ->
{
if (CommonUtil.contains(ATTACABLE_MONSTERS, chars.getId()))
{
addAttackDesire(npc, chars);
return;
}
});
}
}
break;
}
case "OPEN_DOOR1":
{
cancelQuestTimers("ATTACK");
world.setStatus(1);
world.openCloseDoor(17230102, true);
world.spawnGroup("wof_room2");
break;
}
case "OPEN_DOOR2":
{
cancelQuestTimers("ATTACK1");
final int halter = world.getTemplateParameters().getInt("vanhalter");
final L2Npc npcss = world.getNpc(halter);
startQuestTimer("ATTACK2", 200, npcss, player, true);
world.setStatus(3);
world.spawnGroup("wof_room3");
world.openCloseDoor(17230103, true);
break;
}
case "BROADCAST_TEXT":
{
npc.setTarget(player);
npc.setRunning();
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, player);
npc.broadcastPacket(new NpcSay(npc.getObjectId(), ChatType.NPC_GENERAL, npc.getId(), NpcStringId.THAT_GUY_KAIN_HAS_A_SMARMY_FACE));
player.sendPacket(new PlaySound(3, "Npcdialog1.apple_quest_7", 0, 0, 0, 0, 0));
break;
}
case "SEY2":
{
if (npc.getId() == FERIN)
{
npc.broadcastPacket(new NpcSay(npc.getObjectId(), ChatType.NPC_GENERAL, npc.getId(), NpcStringId.YOU_CAN_T_DIE_HERE_I_DIDN_T_LEARN_RESURRECT_YET));
player.sendPacket(new PlaySound(3, "Npcdialog1.apple_quest_4", 0, 0, 0, 0, 0));
}
break;
}
case "SEY_KAIN":
{
if (npc.getId() == VAN_HALTER)
{
startQuestTimer("ATTACK1", 200, npc, player, true);
npc.broadcastPacket(new NpcSay(npc.getObjectId(), ChatType.NPC_GENERAL, npc.getId(), NpcStringId.GISELLE_WAS_SUCH_A_SWEET_CHILD));
player.sendPacket(new PlaySound(3, "Npcdialog1.holter_quest_1", 0, 0, 0, 0, 0));
}
break;
}
case "SEY3":
{
if (npc.getId() == FERIN)
{
npc.broadcastPacket(new NpcSay(npc.getObjectId(), ChatType.NPC_GENERAL, npc.getId(), NpcStringId.DO_YOU_THINK_I_LL_GROW_TALLER_IF_I_EAT_LOTS_AND_LOTS));
player.sendPacket(new PlaySound(3, "Npcdialog1.apple_quest_6", 0, 0, 0, 0, 0));
}
break;
}
case "SEY_KAIN_1":
{
if (npc.getId() == VAN_HALTER)
{
npc.broadcastPacket(new NpcSay(npc.getObjectId(), ChatType.NPC_GENERAL, npc.getId(), NpcStringId.SUCH_MONSTERS_IN_A_PLACE_LIKE_THIS_UNBELIEVABLE));
}
break;
}
case "SEY_KAIN_2":
{
if (npc.getId() == VAN_HALTER)
{
npc.broadcastPacket(new NpcSay(npc.getObjectId(), ChatType.NPC_GENERAL, npc.getId(), NpcStringId.THAT_S_THE_MONSTER_THAT_ATTACKED_FAERON_YOU_RE_OUTMATCHED_HERE_GO_AHEAD_I_LL_CATCH_UP));
player.sendPacket(new PlaySound(3, "Npcdialog1.holter_quest_6", 0, 0, 0, 0, 0));
startQuestTimer("SEY_KAIN_3", 7000, npc, player);
}
break;
}
case "SEY4":
{
if (npc.getId() == FERIN)
{
npc.broadcastPacket(new NpcSay(npc.getObjectId(), ChatType.NPC_GENERAL, npc.getId(), NpcStringId.GO_NOW_KAIN_CAN_HANDLE_THIS));
startQuestTimer("REST", 5000, npc, player);
npc.setScriptValue(1);
}
break;
}
case "SEY_KAIN_3":
{
if (npc.getId() == VAN_HALTER)
{
npc.broadcastPacket(new NpcSay(npc.getObjectId(), ChatType.NPC_GENERAL, npc.getId(), NpcStringId.LEAVE_THIS_TO_ME_GO));
startQuestTimer("SEY_KAIN_4", 1000, npc, player);
npc.setScriptValue(1);
}
break;
}
case "REST":
{
if (npc.getId() == FERIN)
{
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, player);
cancelQuestTimers("BROADCAST_TEXT");
}
break;
}
case "SEY_KAIN_4":
{
world.setStatus(5);
world.spawnGroup("q10753_16_instance_grail");
showOnScreenMsg(player, NpcStringId.LEAVE_THIS_PLACE_TO_KAIN_NGO_TO_THE_NEXT_ROOM, ExShowScreenMessage.TOP_CENTER, 6000);
world.openCloseDoor(17230104, true);
cancelQuestTimers("ATTACK2");
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, player);
startQuestTimer("CLOSE", 15000, npc, player);
break;
}
case "CLOSE":
{
world.openCloseDoor(17230104, false);
world.despawnGroup("q10753_16_instance_halter_1_1");
world.despawnGroup("wof_room4");
break;
}
case "DESPAWN_WIZARD":
{
world.despawnGroup("q10753_16_instance_wizard");
break;
}
case "exit":
{
startQuestTimer("finish", 3000, npc, player);
player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THIS_DUNGEON_WILL_EXPIRE_IN_S1_MINUTE_S_YOU_WILL_BE_FORCED_OUT_OF_THE_DUNGEON_WHEN_THE_TIME_EXPIRES).addInt((int) 1.0D));
qs.setCond(17, true);
break;
}
case "finish":
{
world.finishInstance(0);
break;
}
}
}
}
return htmltext;
}
@Override
public String onKill(L2Npc npc, L2PcInstance player, boolean isSummon)
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
switch (world.getStatus())
{
case 0:
{
if (world.getAliveNpcs(ATTACABLE_MONSTERS).isEmpty())
{
final int ferin = world.getTemplateParameters().getInt("ferin");
final int halter = world.getTemplateParameters().getInt("vanhalter");
final L2Npc npcs = world.getNpc(ferin);
final L2Npc npcss = world.getNpc(halter);
startQuestTimer("SEY2", 14000, npcs, player);
startQuestTimer("SEY_KAIN", 24000, npcss, player);
startQuestTimer("OPEN_DOOR1", 5000, npc, player);
}
break;
}
case 1:
{
if (world.getAliveNpcs(ATTACABLE_MONSTERS).isEmpty())
{
world.spawnGroup("wof_room2_1");
world.setStatus(2);
}
break;
}
case 2:
{
if (world.getAliveNpcs(ATTACABLE_MONSTERS).isEmpty())
{
final int ferin = world.getTemplateParameters().getInt("ferin");
final L2Npc npcs = world.getNpc(ferin);
startQuestTimer("SEY3", 8000, npcs, player);
startQuestTimer("OPEN_DOOR2", 5000, npc, player);
}
break;
}
case 3:
{
if (world.getAliveNpcs(ATTACABLE_MONSTERS).isEmpty())
{
world.setStatus(4);
world.spawnGroup("wof_room3_2");
world.openCloseDoor(17230103, false);
final int halter = world.getTemplateParameters().getInt("vanhalter");
final L2Npc npcss = world.getNpc(halter);
startQuestTimer("SEY_KAIN_1", 5000, npcss, player);
}
break;
}
case 4:
{
if (world.getAliveNpcs(ATTACABLE_MONSTERS).isEmpty())
{
world.setStatus(5);
world.spawnGroup("wof_room4"); // TODO spawn animation
final int halter = world.getTemplateParameters().getInt("vanhalter");
final int ferin = world.getTemplateParameters().getInt("ferin");
final L2Npc npcss = world.getNpc(halter);
final L2Npc npcs = world.getNpc(ferin);
startQuestTimer("SEY_KAIN_2", 3000, npcss, player);
startQuestTimer("SEY4", 7000, npcs, player);
}
break;
}
}
}
return null;
}
@Override
public String onFirstTalk(L2Npc npc, L2PcInstance player)
{
final QuestState qs = player.getQuestState(Q10753_WindsOfFateChoices.class.getSimpleName());
String htmltext = null;
switch (npc.getId())
{
case KAIN_VAN_HALTER:
{
if ((qs != null) && qs.isCond(16))
{
htmltext = "33979.html";
}
break;
}
case GRAIL:
{
htmltext = "33996.html";
break;
}
case MYSTERIOUS_WIZARD:
{
if ((qs != null) && qs.isCond(16))
{
htmltext = "33980.html";
}
break;
}
}
return htmltext;
}
@Override
public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
switch (npc.getId())
{
case FERIN:
{
if (creature.isPlayer() && !creature.isDead() && npc.isScriptValue(0))
{
startQuestTimer("BROADCAST_TEXT", 12000, npc, (L2PcInstance) creature);
}
break;
}
case VAN_HALTER:
{
if (creature.isPlayer() && !creature.isDead() && world.isStatus(0))
{
startQuestTimer("ATTACK", 2000, npc, (L2PcInstance) creature, true);
}
break;
}
}
}
return super.onSeeCreature(npc, creature, isSummon);
}
public static void main(String[] args)
{
new ChamberOfProphecies();
}
}

View File

@@ -0,0 +1,450 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.ChambersOfDelusion;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.IntStream;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ThreadPoolManager;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.NpcStringId;
import com.l2jmobius.gameserver.network.serverpackets.Earthquake;
import instances.AbstractInstance;
/**
* Chambers of Delusion.
* @author GKR
*/
public final class ChamberOfDelusion extends AbstractInstance
{
// NPCs
private static final Map<Integer, Integer> ENTRANCE_GATEKEEPER = new HashMap<>();
private static final int[] ROOM_GATEKEEPERS = IntStream.range(32664, 32702).toArray();
private static final int[] AENKINEL =
{
25690, // East
25691, // West
25692, // South
25693, // North
25694, // Square
25695, // Tower
};
private static final int[] BOX =
{
18838, // East, West, South, North
18820, // Square
18823, // Tower
};
// Items
private static final int ENRIA = 4042;
private static final int ASOFE = 4043;
private static final int THONS = 4044;
private static final int LEONARD = 9628;
private static final int DELUSION_MARK = 15311;
// Skills
private static final SkillHolder SUCCESS_SKILL = new SkillHolder(5758, 1);
private static final SkillHolder FAIL_SKILL = new SkillHolder(5376, 4);
// Timers
private static final int ROOM_CHANGE_INTERVAL = 480; // 8 min
private static final int ROOM_CHANGE_RANDOM_TIME = 120; // 2 min
static
{
ENTRANCE_GATEKEEPER.put(32658, 127); // East
ENTRANCE_GATEKEEPER.put(32659, 128); // West
ENTRANCE_GATEKEEPER.put(32660, 129); // South
ENTRANCE_GATEKEEPER.put(32661, 130); // North
ENTRANCE_GATEKEEPER.put(32662, 131); // Square
ENTRANCE_GATEKEEPER.put(32663, 132); // Tower
}
public ChamberOfDelusion()
{
super(ENTRANCE_GATEKEEPER.values().stream().mapToInt(Integer::valueOf).toArray());
addStartNpc(ENTRANCE_GATEKEEPER.keySet());
addStartNpc(ROOM_GATEKEEPERS);
addTalkId(ENTRANCE_GATEKEEPER.keySet());
addTalkId(ROOM_GATEKEEPERS);
addKillId(AENKINEL);
addAttackId(BOX);
addSpellFinishedId(BOX);
addEventReceivedId(BOX);
addInstanceCreatedId(ENTRANCE_GATEKEEPER.values());
addInstanceDestroyId(ENTRANCE_GATEKEEPER.values());
}
@Override
public void onInstanceCreated(Instance instance, L2PcInstance player)
{
// Choose start room
changeRoom(instance);
// Start banish task
final ScheduledFuture<?> banishTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(() ->
{
if (instance.getRemainingTime() < 60000)
{
final ScheduledFuture<?> task = instance.getParameters().getObject("banishTask", ScheduledFuture.class);
task.cancel(false);
}
else
{
for (int objId : instance.getAllowed())
{
final L2PcInstance pl = L2World.getInstance().getPlayer(objId);
if ((pl != null) && pl.isOnline() && !pl.isInParty())
{
instance.finishInstance(0);
break;
}
}
}
}, 60000, 60000);
instance.setParameter("banishTask", banishTask);
}
@Override
public void onInstanceDestroy(Instance instance)
{
// Stop room change task
stopRoomChangeTask(instance);
// Stop banish task
final ScheduledFuture<?> banish = instance.getParameters().getObject("banishTask", ScheduledFuture.class);
if (banish != null)
{
banish.cancel(true);
instance.setParameter("banishTask", null);
}
}
@Override
protected void onEnter(L2PcInstance player, Instance instance, boolean firstEnter)
{
// Teleport player to instance
super.onEnter(player, instance, firstEnter);
// Take items
if (firstEnter)
{
if (hasQuestItems(player, DELUSION_MARK))
{
takeItems(player, DELUSION_MARK, -1);
}
if (player.getParty().isLeader(player))
{
giveItems(player, DELUSION_MARK, 1);
}
}
}
@Override
protected void teleportPlayerIn(L2PcInstance player, Instance instance)
{
final int room = instance.getParameters().getInt("currentRoom");
final Location loc = instance.getEnterLocations().get(room);
player.teleToLocation(loc, instance);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
String htmltext = null;
final Instance world = npc.getInstanceWorld();
if ((player != null) && (world != null) && CommonUtil.contains(ROOM_GATEKEEPERS, npc.getId()))
{
switch (event)
{
case "next_room":
{
if (player.isInParty())
{
htmltext = "no_party.html";
}
else if (player.getParty().isLeader(player))
{
htmltext = "no_leader.html";
}
else if (!hasQuestItems(player, DELUSION_MARK))
{
htmltext = "no_item.html";
}
else
{
takeItems(player, DELUSION_MARK, 1);
stopRoomChangeTask(world);
changeRoom(world);
}
break;
}
case "go_out":
{
if (player.isInParty())
{
htmltext = "no_party.html";
}
else if (player.getParty().isLeader(player))
{
htmltext = "no_leader.html";
}
else
{
world.finishInstance(0);
}
break;
}
case "look_party":
{
if (player.isInParty() && world.isAllowed(player))
{
teleportPlayerIn(player, world);
}
break;
}
}
}
return htmltext;
}
@Override
public String onTalk(L2Npc npc, L2PcInstance player)
{
final int npcId = npc.getId();
if (ENTRANCE_GATEKEEPER.containsKey(npcId))
{
enterInstance(player, npc, ENTRANCE_GATEKEEPER.get(npcId));
}
return null;
}
@Override
public String onSpellFinished(L2Npc npc, L2PcInstance player, Skill skill)
{
if (!npc.isDead() && CommonUtil.contains(BOX, npc.getId()) && ((skill.getId() == FAIL_SKILL.getSkillId()) || (skill.getId() == SUCCESS_SKILL.getSkillId())))
{
npc.doDie(player);
}
return super.onSpellFinished(npc, player, skill);
}
@Override
public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference)
{
switch (eventName)
{
case "SCE_LUCKY":
receiver.setBusy(true);
receiver.doCast(SUCCESS_SKILL.getSkill());
break;
case "SCE_DREAM_FIRE_IN_THE_HOLE":
receiver.setBusy(true);
receiver.doCast(FAIL_SKILL.getSkill());
break;
}
return null;
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet, Skill skill)
{
if (!npc.isBusy() && (npc.getCurrentHp() < (npc.getMaxHp() / 10)))
{
npc.setBusy(true);
if (getRandom(100) < 25) // 25% chance to reward
{
if (getRandom(100) < 33)
{
npc.dropItem(attacker, ENRIA, (int) (3 * Config.RATE_QUEST_DROP));
}
if (getRandom(100) < 50)
{
npc.dropItem(attacker, THONS, (int) (4 * Config.RATE_QUEST_DROP));
}
if (getRandom(100) < 50)
{
npc.dropItem(attacker, ASOFE, (int) (4 * Config.RATE_QUEST_DROP));
}
if (getRandom(100) < 16)
{
npc.dropItem(attacker, LEONARD, (int) (2 * Config.RATE_QUEST_DROP));
}
npc.broadcastEvent("SCE_LUCKY", 2000, null);
npc.doCast(SUCCESS_SKILL.getSkill());
}
else
{
npc.broadcastEvent("SCE_DREAM_FIRE_IN_THE_HOLE", 2000, null);
}
}
return super.onAttack(npc, attacker, damage, isPet, skill);
}
@Override
public String onKill(L2Npc npc, L2PcInstance player, boolean isPet)
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
if (isBigChamber(world))
{
world.setReenterTime();
if (world.getRemainingTime() > (Config.INSTANCE_FINISH_TIME * 60000))
{
world.finishInstance();
}
}
else
{
stopRoomChangeTask(world);
scheduleRoomChange(world, true);
}
world.spawnGroup("boxes");
}
return super.onKill(npc, player, isPet);
}
private boolean isBigChamber(Instance world)
{
return world.getTemplateParameters().getBoolean("isBigRoom");
}
private boolean isBossRoom(Instance world, int room)
{
return room == (world.getEnterLocations().size() - 1);
}
protected void changeRoom(Instance world)
{
final StatsSet params = world.getParameters();
final List<Location> locations = world.getEnterLocations();
int newRoom = params.getInt("currentRoom", 0);
if (isBigChamber(world))
{
// Do nothing, if there are raid room of Sqare or Tower Chamber
if (isBossRoom(world, newRoom))
{
return;
}
// Teleport to raid room 10 min or lesser before instance end time for Tower and Square Chambers
else if (world.getRemainingTime() < 600000)
{
newRoom = locations.size() - 1;
}
}
// 10% chance for teleport to raid room if not here already for Northern, Southern, Western and Eastern Chambers
else if (!isBossRoom(world, newRoom) && (getRandom(100) < 10))
{
newRoom = locations.size() - 1;
}
else
{
// otherwise teleport to another room, except current
while (newRoom == params.getInt("currentRoom", 0))
{
newRoom = getRandom(locations.size() - 1);
}
}
world.setParameter("currentRoom", newRoom);
// Teleport players into new room
world.getPlayers().forEach(p -> teleportPlayerIn(p, world));
// Do not schedule room change for Square and Tower Chambers, if raid room is reached
if (isBigChamber(world) && isBossRoom(world, newRoom))
{
// Add 20 min to instance time if raid room is reached
world.setDuration((int) (TimeUnit.MILLISECONDS.toMinutes(world.getRemainingTime() + 1200000)));
// Broadcast instance duration change (NPC message)
final int raidGatekeeper = world.getTemplateParameters().getInt("raidGatekeeper");
final L2Npc gatekeeper = world.getNpc(raidGatekeeper);
if (gatekeeper != null)
{
gatekeeper.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.MINUTES_ARE_ADDED_TO_THE_REMAINING_TIME_IN_THE_INSTANT_ZONE);
}
}
else
{
scheduleRoomChange(world, false);
}
}
protected void scheduleRoomChange(Instance world, boolean bossRoom)
{
// Schedule next room change only if remaining time is enough
final long nextInterval = (bossRoom) ? 60000L : (ROOM_CHANGE_INTERVAL + getRandom(ROOM_CHANGE_RANDOM_TIME)) * 1000L;
if (world.getRemainingTime() > nextInterval)
{
final ScheduledFuture<?> roomChangeTask = ThreadPoolManager.getInstance().scheduleGeneral(() ->
{
try
{
// Send earthquake packet
for (L2PcInstance player : world.getPlayers())
{
player.sendPacket(new Earthquake(player, 20, 10));
}
// Wait for a while
Thread.sleep(5000);
// Change room
changeRoom(world);
}
catch (Exception e)
{
_log.log(Level.WARNING, "Error occured in room change task: ", e);
}
}, nextInterval - 5000);
world.setParameter("roomChangeTask", roomChangeTask);
}
}
protected void stopRoomChangeTask(Instance world)
{
final ScheduledFuture<?> task = world.getParameters().getObject("roomChangeTask", ScheduledFuture.class);
if (task != null)
{
task.cancel(true);
world.setParameter("roomChangeTask", null);
}
}
public static void main(String[] args)
{
new ChamberOfDelusion();
}
}

View File

@@ -0,0 +1,3 @@
<html><body>Manager:<br>
You have already used an opportunity. There are no more.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Manager:<br>
You're not a party leader. Please bring the party leader here.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Manager:<br>
You're not a member of a party. Please join one quickly.
</body></html>

View File

@@ -0,0 +1,444 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.DarkCloudMansion;
import java.util.List;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.network.NpcStringId;
import instances.AbstractInstance;
/**
* Dark Cloud Mansion instance.
* @author malyelfik
*/
public final class DarkCloudMansion extends AbstractInstance
{
// NPCs
private static final int YIYEN = 32282;
private static final int FAITH = 32288; // Symbol of Faith
private static final int ADVERSITY = 32289; // Symbol of Adversity
private static final int ADVENTURE = 32290; // Symbol of Adventure
private static final int TRUTH = 32291; // Symbol of Truth
private static final int MONOLITH = 32324; // Black Stone Monolith
private static final int COLUMN = 22402; // Shadow Column
// Mobs
private static final int PARME_HEALER = 22400;
private static final int[] BELETH_SUBORDINATE =
{
22272,
22273,
22274
};
private static final int[] MONOLITH_PRIVATES =
{
18369,
18370
};
private static final int[] BELETH_SAMPLE =
{
18371,
18372,
18373,
18374,
18375,
18376,
18377
};
// Skill
private static final SkillHolder INCARNATION = new SkillHolder(5441, 1);
// Items
private static final int CC = 9690; // Contaminated Crystal
// Doors
private static final int START_DOOR = 24230001; // Starting Room
private static final int ROOM_A_DOOR = 24230002; // First Room
private static final int ROOM_B_DOOR = 24230005; // Second Room
private static final int ROOM_C_DOOR = 24230003; // Third Room
private static final int ROOM_D_DOOR = 24230004; // Forth Room
private static final int ROOM_E_DOOR = 24230006; // Fifth Room
// Messages
private static final NpcStringId[] SPAWN_CHAT =
{
NpcStringId.I_M_THE_REAL_ONE,
NpcStringId.PICK_ME,
NpcStringId.TRUST_ME,
NpcStringId.NOT_THAT_DUDE_I_M_THE_REAL_ONE,
NpcStringId.DON_T_BE_FOOLED_DON_T_BE_FOOLED_I_M_THE_REAL_ONE
};
private static final NpcStringId[] DECAY_CHAT =
{
NpcStringId.I_M_THE_REAL_ONE_PHEW,
NpcStringId.CAN_T_YOU_EVEN_FIND_OUT,
NpcStringId.FIND_ME
};
private static final NpcStringId[] SUCCESS_CHAT =
{
NpcStringId.HUH_HOW_DID_YOU_KNOW_IT_WAS_ME,
NpcStringId.EXCELLENT_CHOICE_TEEHEE,
NpcStringId.YOU_VE_DONE_WELL,
NpcStringId.OH_VERY_SENSIBLE
};
private static final NpcStringId[] FAIL_CHAT =
{
NpcStringId.YOU_VE_BEEN_FOOLED,
NpcStringId.SORRY_BUT_I_M_THE_FAKE_ONE
};
// Misc
private static final int TEMPLATE_ID = 9;
public DarkCloudMansion()
{
super(TEMPLATE_ID);
addStartNpc(YIYEN);
addFirstTalkId(MONOLITH, TRUTH, FAITH, ADVERSITY, ADVENTURE);
addTalkId(YIYEN, TRUTH);
addAttackId(COLUMN, PARME_HEALER);
addAttackId(BELETH_SAMPLE);
addKillId(COLUMN, PARME_HEALER);
addKillId(BELETH_SAMPLE);
addKillId(BELETH_SUBORDINATE);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
switch (event)
{
case "DELETE_ONE":
npc.deleteMe();
break;
case "DELETE":
{
world.getNpcs(BELETH_SAMPLE).stream().filter(n -> n != npc).forEach(L2Npc::deleteMe);
world.setParameter("blocked", false);
spawnRoomE(world);
break;
}
case "CHAT":
world.getNpcs(BELETH_SAMPLE).stream().filter(n -> n.isScriptValue(1)).forEach(n -> n.broadcastSay(ChatType.NPC_GENERAL, DECAY_CHAT[getRandom(DECAY_CHAT.length)]));
break;
}
}
return null;
}
@Override
public String onTalk(L2Npc npc, L2PcInstance player)
{
if (npc.getId() == YIYEN)
{
enterInstance(player, npc, TEMPLATE_ID);
}
else
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
teleportPlayerOut(player, world);
}
}
return null;
}
@Override
public String onFirstTalk(L2Npc npc, L2PcInstance player)
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
switch (npc.getId())
{
case FAITH:
{
world.openCloseDoor(ROOM_A_DOOR, true);
npc.showChatWindow(player);
break;
}
case MONOLITH:
{
if (world.isStatus(4) && !npc.isScriptValue(-1))
{
int current = world.getParameters().getInt("current", 0);
if (npc.isScriptValue(current))
{
npc.setScriptValue(-1);
npc.doCast(INCARNATION.getSkill());
world.setParameter("current", ++current);
if (current == 6)
{
world.setStatus(5);
world.spawnGroup("roomBClear");
world.spawnGroup("hall");
world.getNpcs(MONOLITH).forEach(L2Npc::deleteMe);
}
}
else
{
addSpawn(MONOLITH_PRIVATES[getRandom(MONOLITH_PRIVATES.length)], npc, false, 0, false, world.getId());
}
world.openCloseDoor(ROOM_B_DOOR, false);
}
break;
}
case ADVERSITY:
{
world.openCloseDoor(ROOM_B_DOOR, true);
npc.showChatWindow(player);
break;
}
case ADVENTURE:
{
if (world.isStatus(7))
{
world.spawnGroup("roomC").forEach(n -> n.setScriptValue(8));
world.setStatus(8);
world.openCloseDoor(ROOM_D_DOOR, true);
}
npc.showChatWindow(player);
break;
}
case TRUTH:
{
if (!hasQuestItems(player, CC))
{
giveItems(player, CC, 1);
}
npc.showChatWindow(player);
break;
}
}
}
return null;
}
@Override
public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
switch (npc.getId())
{
case PARME_HEALER:
{
if (world.isStatus(2) && (attacker.getY() < 179986))
{
world.openCloseDoor(ROOM_A_DOOR, false);
}
break;
}
case COLUMN:
{
if (world.isStatus(7))
{
world.openCloseDoor(ROOM_D_DOOR, false);
if (npc.isHpBlocked() && (getRandom(100) < 12))
{
addSpawn(BELETH_SUBORDINATE[getRandom(BELETH_SUBORDINATE.length)], npc, false, 0, false, world.getId());
}
}
break;
}
default:
{
if (world.isStatus(9))
{
handleRoomE(world, npc);
}
}
}
}
return super.onAttack(npc, attacker, damage, isSummon);
}
@Override
public String onKill(L2Npc npc, L2PcInstance player, boolean isSummon)
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
switch (world.getStatus())
{
case 0: // Start room
{
if (world.getAliveNpcs().isEmpty())
{
world.setStatus(1);
world.spawnGroup("hall");
world.openCloseDoor(START_DOOR, true);
}
break;
}
case 1: // Hall
{
if (world.getAliveNpcs().isEmpty())
{
world.setStatus(2);
world.spawnGroup("roomA");
world.openCloseDoor(ROOM_A_DOOR, true);
}
break;
}
case 2: // Room A - cleared
{
if (world.getAliveNpcs().isEmpty())
{
world.setStatus(3);
world.spawnGroup("roomAClear");
world.spawnGroup("hall");
}
break;
}
case 3: // Hall
{
if (world.getAliveNpcs(BELETH_SUBORDINATE).isEmpty())
{
world.setStatus(4);
world.openCloseDoor(ROOM_B_DOOR, true);
int current = 0;
final List<L2Npc> desks = world.spawnGroup("roomB");
while (!desks.isEmpty())
{
final L2Npc desk = desks.remove(getRandom(desks.size()));
desk.setScriptValue(current++);
}
}
break;
}
case 5: // Hall
{
if (world.getAliveNpcs(BELETH_SUBORDINATE).isEmpty())
{
world.setStatus(6);
world.spawnGroup("roomC");
world.openCloseDoor(ROOM_C_DOOR, true);
}
break;
}
case 6: // Room C
{
if (world.getAliveNpcs(BELETH_SUBORDINATE).isEmpty())
{
world.setStatus(7);
world.spawnGroup("roomD");
world.openCloseDoor(ROOM_D_DOOR, true);
for (int i = 1; i <= 7; i++)
{
final List<L2Npc> row = world.spawnGroup("roomD" + i);
for (int x = 0; x < 2; x++)
{
final L2Npc col = row.remove(getRandom(row.size()));
col.setIsInvul(true);
}
}
}
break;
}
case 8: // Room C
{
final long roomClear = world.getAliveNpcs(BELETH_SUBORDINATE).stream().filter(n -> n.isScriptValue(8)).count();
if (roomClear == 0)
{
world.setStatus(9);
world.openCloseDoor(ROOM_E_DOOR, true);
spawnRoomE(world);
}
break;
}
case 9:
{
handleRoomE(world, npc);
break;
}
}
}
return null;
}
private void spawnRoomE(Instance world)
{
final List<L2Npc> npcs = world.spawnGroup("roomE");
for (L2Npc n : npcs)
{
n.broadcastSay(ChatType.NPC_GENERAL, SPAWN_CHAT[getRandom(SPAWN_CHAT.length)]);
}
for (int i = 0; i < 3; i++)
{
final L2Npc n = npcs.remove(getRandom(npcs.size()));
n.setScriptValue(1);
}
}
private void handleRoomE(Instance world, L2Npc npc)
{
if (CommonUtil.contains(BELETH_SAMPLE, npc.getId()))
{
final StatsSet params = world.getParameters();
if (!params.getBoolean("blocked", false))
{
if (npc.isScriptValue(1))
{
final int found = params.getInt("found", 0) + 1;
world.setParameter("found", found);
npc.setScriptValue(-1);
npc.broadcastSay(ChatType.NPC_GENERAL, SUCCESS_CHAT[getRandom(SUCCESS_CHAT.length)]);
if (found != 3)
{
startQuestTimer("DELETE_ONE", 1500, npc, null);
}
else
{
world.setStatus(10);
world.getNpcs().forEach(L2Npc::deleteMe);
world.spawnGroup("roomEClear");
}
}
else if (!npc.isScriptValue(-1))
{
world.setParameter("blocked", true);
world.setParameter("found", 0);
npc.broadcastSay(ChatType.NPC_GENERAL, FAIL_CHAT[getRandom(FAIL_CHAT.length)]);
npc.setScriptValue(-1);
startQuestTimer("CHAT", 4000, npc, null);
startQuestTimer("DELETE", 4500, npc, null);
}
world.openCloseDoor(ROOM_E_DOOR, false);
}
}
}
public static void main(String[] args)
{
new DarkCloudMansion();
}
}

View File

@@ -0,0 +1,4 @@
<html><body>Priest Cerenas:<br>
You are learning, adventurer. I will pray for your illumination.<br>
(You have already completed this quest.)
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Priest Cerenas:<br>
No, no, this will not do. You have not proven yourself worthy. (You must have started the "Soul Testing" quest to enter.)
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Eva's Avatar:<br>
The Goddess of Destruction has finally resurrected! The energy of water continues to be contaminated....<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvasHiddenSpace exitInstance">Back</Button>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h npc_%objectId%_Quest">Quest</Button>
</body></html>

View File

@@ -0,0 +1,93 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.EvasHiddenSpace;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import instances.AbstractInstance;
import quests.Q10369_NoblesseSoulTesting.Q10369_NoblesseSoulTesting;
/**
* Eva's Hidden Space instance zone.
* @author Gladicek, St3eT
*/
public final class EvasHiddenSpace extends AbstractInstance
{
// NPCs
private static final int CERENAS = 31281;
private static final int EVAS_AVATAR = 33686;
// Misc
private static final int TEMPLATE_ID = 217;
public EvasHiddenSpace()
{
super(TEMPLATE_ID);
addStartNpc(CERENAS);
addTalkId(CERENAS, EVAS_AVATAR);
addFirstTalkId(EVAS_AVATAR);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
String htmltext = null;
final QuestState qs = player.getQuestState(Q10369_NoblesseSoulTesting.class.getSimpleName());
if (event.equals("enterInstance"))
{
if ((qs != null) && qs.isStarted())
{
enterInstance(player, npc, TEMPLATE_ID);
if (qs.isCond(14))
{
qs.setCond(15);
}
else if (qs.isCond(17))
{
qs.setCond(18);
}
}
else if ((qs != null) && qs.isCompleted())
{
htmltext = "31281-01.html";
}
else
{
htmltext = "31281.html";
}
}
else if (event.equals("exitInstance"))
{
final Instance world = getPlayerInstance(player);
if (world != null)
{
teleportPlayerOut(player, world);
}
}
return htmltext;
}
public static void main(String[] args)
{
new EvasHiddenSpace();
}
}

View File

@@ -0,0 +1,3 @@
<html><body>Aden Vanguard Member:<br>
Go ahead and speak with the Captain and help the Aden Vanguard out!
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Aden Vanguard Captain Adolph:<br>
You've caught me resting after a particularly tough battle. So, you're the soldier Orven has sent? Good! You can help my squad with that never-ending stream of monsters. Got any questions?<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvilIncubator 33170-02.html">"I'm ready."</Button>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Aden Vanguard Captain Adolph:<br>
See my subordinates there? They are elite veterans who have known many victories. Two of them will wait here, while you, me, and the other two enter the enemy base.<br>
Go and talk to my squad and pick out two you'd like to have at your side.
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Aden Vanguard Captain Adolph:<br>
I see you've chosen strong allies.<br1>
This battle will be fierce, and if you see me go down, do not hesitate to run. We can't afford to lose any more people, and if that happens, you will need time to regroup.<br1>
Now, I hope you're ready to take this fight to the enemy, because we're going into their base.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvilIncubator teamSelected">"I'm ready."</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Aden Vanguard Captain Adolph:<br>
Focus on the enemies in front of you. Looking away in battle is suicide.
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Aden Vanguard Captain Adolph:<br>
Phew! What a fight, eh? Now, there is a boss beyond that door, so let's take a short break. We must call him out and defeat him before we can leave.<br>
I was worried, but I think I will be able to bring out your full potential. I will lend you this temporarily.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvilIncubator giveItem">"Thank you."</Button>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Aden Vanguard Captain Adolph:<br>
The kingdom gave me this ring, and I've been holding onto it for someone worthy. Now I've seen you in action, I think you should borrow it for the coming fight.<br>
It is imbued with a magic that allows the wearer to draw upon power that they will some day come to possess. With this, you will be formidable indeed.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Aden Vanguard Captain Adolph:<br>
When you're rested, let's take on those monsters. Are you ready?<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvilIncubator resumeFight">"I'm ready."</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Aden Vanguard Captain Adolph:<br>
I don't know where you came from, but Aden Castle is certainly glad of your help. Now, go back to your master for further instructions. I'm sure you need to report on what happened here, and I think we have things under control now.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Aden Vanguard Captain Adolph:<br>
As Shilen grows in power, more monsters will emerge from here. We can't just let this happen without fighting back!<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h Quest EvilIncubator">Quest</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Priest Alice:<br>
Speak with Adolph first.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Priest Alice:<br>
Do you need my help? I can heal, and provide support in combat.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvilIncubator selectHelper">Select</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Alice:<br>
This is dangerous. Quit fidgeting and concentrate.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Priest Alice:<br>
Even the strongest soldier will fall if they cannot be healed in battle. My healing power will lead us to victory.<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h Quest EvilIncubator">Quest</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Officer Barton:<br>
Speak with Adolph.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Officer Barton:<br>
You need my help? Well, you look like a capable sort. I need someone like you to watch my back.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvilIncubator selectHelper">Select</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Officer Barton:<br>
I hope to see you on the battlefield again, but for now I must observe the enemy from here.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Officer Barton:<br>
Not to brag, but a star soldier such as myself can make the difference between winning and losing.<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h Quest EvilIncubator">Quest</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Sniper Hayuk:<br>
You don't want to talk to me, you want Captain Adolph.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Sniper Hayuk:<br>
Do you need my help? Of course! I will cover your back! You can trust me.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvilIncubator selectHelper">"Yes, I could use some help."</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Hayuk:<br>
We are in the middle of a battle. Let's deal with the enemies first.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Sniper Hayuk:<br>
If you can defeat your enemy before they get close, you can annihilate their forces without incurring any damage to your own team. Heh! That there is what's called strategy!<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h Quest EvilIncubator">Quest</Button>
</body></html>

View File

@@ -0,0 +1,2 @@
<html><body>Magus Eliyah:<br>
Instead of talking to me, perhaps you should speak with Adolph first?</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Magus Eliyah:<br>
You need my help? Well, if you protect me from the monsters, I suppose I can help.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest EvilIncubator selectHelper">Select</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Magus Eliyah:<br>
What is it? I don't think you have any business with me, do you? Now get out there and concentrate on living to fight another day.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Magus Eliyah:<br>
The Ivory Tower's magic is much too powerful for these monsters. We have no need to skulk on the sidelines!<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h Quest EvilIncubator">Quest</Button>
</body></html>

View File

@@ -0,0 +1,567 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.EvilIncubator;
import java.util.EnumMap;
import java.util.List;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.FriendlyNpcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.events.impl.character.OnCreatureDeath;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import com.l2jmobius.gameserver.model.quest.State;
import com.l2jmobius.gameserver.network.NpcStringId;
import com.l2jmobius.gameserver.network.serverpackets.Earthquake;
import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
import instances.AbstractInstance;
import quests.Q10341_DayOfDestinyHumansFate.Q10341_DayOfDestinyHumansFate;
import quests.Q10342_DayOfDestinyElvenFate.Q10342_DayOfDestinyElvenFate;
import quests.Q10343_DayOfDestinyDarkElfsFate.Q10343_DayOfDestinyDarkElfsFate;
import quests.Q10344_DayOfDestinyOrcsFate.Q10344_DayOfDestinyOrcsFate;
import quests.Q10345_DayOfDestinyDwarfsFate.Q10345_DayOfDestinyDwarfsFate;
import quests.Q10346_DayOfDestinyKamaelsFate.Q10346_DayOfDestinyKamaelsFate;
/**
* Evil Incubator instance zone.
* @author St3eT
*/
public final class EvilIncubator extends AbstractInstance
{
// NPCs
private static final int VANGUARD_MEMBER = 33165;
private static final int ADOLPH = 33170;
private static final int ALICE = 33171;
private static final int BARTON = 33172;
private static final int HAYUK = 33173;
private static final int ELIYAH = 33174;
private static final int[] MONSTERS =
{
27431, // Slayer
27432, // Tracker
27433, // Priest of Darkness
27434, // Guardian of Darkness
27430, // Screaming Shaman
};
private static final int[] BOSSES =
{
27425, // Death Wound
27426, // Death Wound
27427, // Death Wound
27428, // Death Wound
27429, // Death Wound
};
private static final int[] HELPERS =
{
33170, // Adolph
// 33171, // Priest Alice
33172, // Officer Barton
33173, // Sniper Hayuk
33174, // Magus Eliyah
33414, // Van Archer
33415, // Van Infantry
};
// Items
private static final int SOE = 736; // Scroll of Escape
// Locations
private static final Location PLAYER_BATTLE_LOC = new Location(56167, -175615, -7944);
private static final Location BATTLE_LOC_2 = new Location(56005, -175536, -7952);
private static final Location BATTLE_LOC_3 = new Location(56325, -175536, -7952);
private static final Location BOSS_LOC = new Location(56165, -177550, -7944);
// Misc
private static final int TEMPLATE_ID = 185;
//@formatter:off
private static final EnumMap<Race, String> RACE_QUESTS = new EnumMap<>(Race.class);
static
{
RACE_QUESTS.put(Race.HUMAN, Q10341_DayOfDestinyHumansFate.class.getSimpleName());
RACE_QUESTS.put(Race.ELF, Q10342_DayOfDestinyElvenFate.class.getSimpleName());
RACE_QUESTS.put(Race.DARK_ELF, Q10343_DayOfDestinyDarkElfsFate.class.getSimpleName());
RACE_QUESTS.put(Race.ORC, Q10344_DayOfDestinyOrcsFate.class.getSimpleName());
RACE_QUESTS.put(Race.DWARF, Q10345_DayOfDestinyDwarfsFate.class.getSimpleName());
RACE_QUESTS.put(Race.KAMAEL, Q10346_DayOfDestinyKamaelsFate.class.getSimpleName());
}
private static final EnumMap<ClassId, Integer> CLASS_BOSS = new EnumMap<>(ClassId.class);
static
{
CLASS_BOSS.put(ClassId.GLADIATOR, 27426);
CLASS_BOSS.put(ClassId.WARLORD, 27426);
CLASS_BOSS.put(ClassId.PALADIN, 27425);
CLASS_BOSS.put(ClassId.DARK_AVENGER, 27425);
CLASS_BOSS.put(ClassId.TREASURE_HUNTER, 27426);
CLASS_BOSS.put(ClassId.HAWKEYE, 27428);
CLASS_BOSS.put(ClassId.SORCERER, 27428);
CLASS_BOSS.put(ClassId.NECROMANCER, 27428);
CLASS_BOSS.put(ClassId.WARLOCK, 27428);
CLASS_BOSS.put(ClassId.BISHOP, 27427);
CLASS_BOSS.put(ClassId.PROPHET, 27429);
CLASS_BOSS.put(ClassId.TEMPLE_KNIGHT, 27425);
CLASS_BOSS.put(ClassId.SWORDSINGER, 27429);
CLASS_BOSS.put(ClassId.PLAINS_WALKER, 27426);
CLASS_BOSS.put(ClassId.SILVER_RANGER, 27428);
CLASS_BOSS.put(ClassId.SPELLSINGER, 27428);
CLASS_BOSS.put(ClassId.ELEMENTAL_SUMMONER, 27428);
CLASS_BOSS.put(ClassId.ELDER, 27427);
CLASS_BOSS.put(ClassId.SHILLIEN_KNIGHT, 27425);
CLASS_BOSS.put(ClassId.BLADEDANCER, 27429);
CLASS_BOSS.put(ClassId.ABYSS_WALKER, 27426);
CLASS_BOSS.put(ClassId.PHANTOM_RANGER, 27428);
CLASS_BOSS.put(ClassId.SPELLHOWLER, 27428);
CLASS_BOSS.put(ClassId.PHANTOM_SUMMONER, 27428);
CLASS_BOSS.put(ClassId.SHILLIEN_ELDER, 27427);
CLASS_BOSS.put(ClassId.DESTROYER, 27426);
CLASS_BOSS.put(ClassId.TYRANT, 27426);
CLASS_BOSS.put(ClassId.OVERLORD, 27429);
CLASS_BOSS.put(ClassId.WARCRYER, 27429);
CLASS_BOSS.put(ClassId.BOUNTY_HUNTER, 27426);
CLASS_BOSS.put(ClassId.WARSMITH, 27426);
CLASS_BOSS.put(ClassId.BERSERKER, 27426);
CLASS_BOSS.put(ClassId.MALE_SOULBREAKER, 27426);
CLASS_BOSS.put(ClassId.FEMALE_SOULBREAKER, 27426);
CLASS_BOSS.put(ClassId.ARBALESTER, 27428);
}
private static final EnumMap<ClassId, Integer> CLASS_ITEMS = new EnumMap<>(ClassId.class);
static
{
CLASS_ITEMS.put(ClassId.GLADIATOR, 17484);
CLASS_ITEMS.put(ClassId.WARLORD, 17485);
CLASS_ITEMS.put(ClassId.PALADIN, 17486);
CLASS_ITEMS.put(ClassId.DARK_AVENGER, 17487);
CLASS_ITEMS.put(ClassId.TREASURE_HUNTER, 17488);
CLASS_ITEMS.put(ClassId.HAWKEYE, 17489);
CLASS_ITEMS.put(ClassId.SORCERER, 17490);
CLASS_ITEMS.put(ClassId.NECROMANCER, 17491);
CLASS_ITEMS.put(ClassId.WARLOCK, 17492);
CLASS_ITEMS.put(ClassId.BISHOP, 17493);
CLASS_ITEMS.put(ClassId.PROPHET, 17494);
CLASS_ITEMS.put(ClassId.TEMPLE_KNIGHT, 17495);
CLASS_ITEMS.put(ClassId.SWORDSINGER, 17496);
CLASS_ITEMS.put(ClassId.PLAINS_WALKER, 17497);
CLASS_ITEMS.put(ClassId.SILVER_RANGER, 17498);
CLASS_ITEMS.put(ClassId.SPELLSINGER, 17499);
CLASS_ITEMS.put(ClassId.ELEMENTAL_SUMMONER, 17500);
CLASS_ITEMS.put(ClassId.ELDER, 17501);
CLASS_ITEMS.put(ClassId.SHILLIEN_KNIGHT, 17502);
CLASS_ITEMS.put(ClassId.BLADEDANCER, 17503);
CLASS_ITEMS.put(ClassId.ABYSS_WALKER, 17504);
CLASS_ITEMS.put(ClassId.PHANTOM_RANGER, 17505);
CLASS_ITEMS.put(ClassId.SPELLHOWLER, 17506);
CLASS_ITEMS.put(ClassId.PHANTOM_SUMMONER, 17507);
CLASS_ITEMS.put(ClassId.SHILLIEN_ELDER, 17508);
CLASS_ITEMS.put(ClassId.DESTROYER, 17509);
CLASS_ITEMS.put(ClassId.TYRANT, 17510);
CLASS_ITEMS.put(ClassId.OVERLORD, 17511);
CLASS_ITEMS.put(ClassId.WARCRYER, 17512);
CLASS_ITEMS.put(ClassId.BOUNTY_HUNTER, 17513);
CLASS_ITEMS.put(ClassId.WARSMITH, 17514);
CLASS_ITEMS.put(ClassId.BERSERKER, 17515);
CLASS_ITEMS.put(ClassId.MALE_SOULBREAKER, 17516);
CLASS_ITEMS.put(ClassId.FEMALE_SOULBREAKER, 17516);
CLASS_ITEMS.put(ClassId.ARBALESTER, 17517);
}
//@formatter:on
public EvilIncubator()
{
super(TEMPLATE_ID);
addStartNpc(VANGUARD_MEMBER);
addTalkId(VANGUARD_MEMBER, ADOLPH, ALICE, BARTON, HAYUK, ELIYAH);
addFirstTalkId(ADOLPH, ALICE, BARTON, HAYUK, ELIYAH);
setCreatureKillId(this::onCreatureKill, MONSTERS);
setCreatureKillId(this::onCreatureKill, BOSSES);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
final QuestState st = getQuestState(player);
if ((st == null) || !st.isStarted())
{
return super.onAdvEvent(event, npc, player);
}
String htmltext = null;
if (event.equals("enterInstance"))
{
enterInstance(player, npc, TEMPLATE_ID);
st.setCond(5);
htmltext = "33165-01.html";
}
else
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
switch (event)
{
case "33170-02.html":
{
if (st.isCond(5))
{
st.setCond(6, true);
htmltext = event;
}
break;
}
case "selectHelper":
{
int helperCount = world.getParameters().getInt("HELPER_COUNT", 0);
if ((helperCount < 2) && st.isCond(6))
{
helperCount++;
world.setParameter("HELPER_COUNT", helperCount);
npc.teleToLocation(helperCount == 1 ? BATTLE_LOC_2 : BATTLE_LOC_3);
if (helperCount == 2)
{
st.setCond(7, true);
L2World.getInstance().getVisibleObjects(world.getNpc(ADOLPH), FriendlyNpcInstance.class, 1000).forEach(c -> c.deleteMe());
}
}
break;
}
case "teamSelected":
{
int helperCount = world.getParameters().getInt("HELPER_COUNT", 0);
if ((helperCount == 2) && st.isCond(7))
{
helperCount++;
world.setParameter("HELPER_COUNT", helperCount);
st.setCond(8, true);
npc.teleToLocation(PLAYER_BATTLE_LOC);
player.teleToLocation(PLAYER_BATTLE_LOC);
getTimers().addTimer("SPAWN_WAVE", 5000, e -> manageWaveSpawn(world));
}
break;
}
case "giveItem":
{
if (st.isCond(9))
{
st.setCond(10, true);
final int classItemId = CLASS_ITEMS.getOrDefault(player.getClassId(), -1);
if ((classItemId != -1) && !hasQuestItems(player, classItemId))
{
giveItems(player, classItemId, 1);
}
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.THE_CRY_OF_DESTINY_PENDANT_WILL_BE_HELPFUL_TO_YOU_PLEASE_EQUIP_IT_AND_BRING_OUT_THE_POWER_OF_THE_PENDANT_TO_PREPARE_FOR_THE_NEXT_FIGHT);
htmltext = "33170-06.html";
}
break;
}
case "resumeFight":
{
if (st.isCond(10))
{
st.setCond(11, true);
manageWaveSpawn(world);
}
break;
}
}
}
}
return htmltext;
}
@Override
public String onTalk(L2Npc npc, L2PcInstance player, boolean isSimulated)
{
String htmltext = getNoQuestMsg(player);
final QuestState st = getQuestState(player);
if ((st == null) || !st.isStarted())
{
return super.onTalk(npc, player, isSimulated);
}
if (st.getState() == State.STARTED)
{
switch (npc.getId())
{
case ADOLPH:
{
switch (st.getCond())
{
case 5:
{
htmltext = npc.getId() + "-01.html";
break;
}
case 6:
{
htmltext = "33170-02.html";
break;
}
case 7:
{
htmltext = "33170-03.html";
break;
}
case 9:
{
htmltext = "33170-05.html";
break;
}
case 10:
{
htmltext = "33170-07.html";
break;
}
case 12:
{
st.setCond(13, true);
giveItems(player, SOE, 1);
}
case 13:
{
htmltext = "33170-08.html";
break;
}
}
break;
}
case ALICE:
case BARTON:
case ELIYAH:
case HAYUK:
{
switch (st.getCond())
{
case 5:
{
htmltext = npc.getId() + "-01.html";
break;
}
case 6:
{
htmltext = npc.getId() + "-02.html";
break;
}
}
break;
}
}
}
return htmltext;
}
@Override
public String onFirstTalk(L2Npc npc, L2PcInstance player)
{
String htmltext = null;
final QuestState st = getQuestState(player);
if ((st == null) || !st.isStarted())
{
return htmltext;
}
switch (npc.getId())
{
case ADOLPH:
{
switch (st.getCond())
{
case 5:
case 6:
case 7:
case 9:
case 10:
case 12:
case 13:
{
htmltext = "33170.html";
break;
}
default:
{
htmltext = "33170-04.html";
}
}
break;
}
case ALICE:
case BARTON:
case ELIYAH:
case HAYUK:
{
htmltext = st.getCond() > 7 ? (npc.getId() + "-03.html") : (npc.getId() + ".html");
break;
}
}
return htmltext;
}
public void onCreatureKill(OnCreatureDeath event)
{
final L2Npc npc = (L2Npc) event.getTarget();
final Instance world = npc.getInstanceWorld();
if (world != null)
{
final int waveId = world.getParameters().getInt("WORLD_WAVE", 1);
if (CommonUtil.contains(BOSSES, npc.getId()))
{
final QuestState st = getQuestState(world.getFirstPlayer());
if ((st == null) || !st.isStarted())
{
return;
}
st.setCond(12, true);
}
else if (waveId < 8)
{
if (world.getAliveNpcs(MONSTERS).isEmpty())
{
getTimers().addTimer("SPAWN_WAVE", 5000, e -> manageWaveSpawn(world));
}
}
getTimers().addTimer("WORLD_ATTACK", 1000, e -> managerWorldAttack(world, null));
}
}
@Override
public void onTimerEvent(String event, StatsSet params, L2Npc npc, L2PcInstance player)
{
}
private void manageWaveSpawn(Instance world)
{
final QuestState st = getQuestState(world.getFirstPlayer());
if ((st == null) || !st.isStarted())
{
return;
}
List<L2Npc> spawnedNpcs = null;
int waveId = world.getParameters().getInt("WORLD_WAVE", 1);
switch (waveId)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
{
spawnedNpcs = world.spawnGroup("wave_" + waveId);
waveId++;
world.setParameter("WORLD_WAVE", waveId);
showOnScreenMsg(world, NpcStringId.CREATURES_RESURRECTED_DEFEND_YOURSELF, ExShowScreenMessage.TOP_CENTER, 5000, true);
break;
}
case 7:
{
waveId++;
world.setParameter("WORLD_WAVE", waveId);
showOnScreenMsg(world, NpcStringId.CREATURES_HAVE_STOPPED_THEIR_ATTACK_REST_AND_THEN_SPEAK_WITH_ADOLPH, ExShowScreenMessage.TOP_CENTER, 5000, true);
st.setCond(9, true);
break;
}
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
{
spawnedNpcs = world.spawnGroup("wave_" + waveId);
waveId++;
world.setParameter("WORLD_WAVE", waveId);
showOnScreenMsg(world, NpcStringId.CREATURES_RESURRECTED_DEFEND_YOURSELF, ExShowScreenMessage.TOP_CENTER, 5000, true);
getTimers().addTimer("SPAWN_WAVE", 20000, e -> manageWaveSpawn(world));
break;
}
case 15:
{
spawnedNpcs = world.spawnGroup("wave_" + waveId);
waveId++;
world.setParameter("WORLD_WAVE", waveId);
showOnScreenMsg(world, NpcStringId.I_DEATH_WOUND_CHAMPION_OF_SHILEN_SHALL_END_YOUR_WORLD, ExShowScreenMessage.TOP_CENTER, 5000, true);
getTimers().addTimer("SPAWN_WAVE", 20000, e -> manageWaveSpawn(world));
final L2Npc boss = addSpawn(CLASS_BOSS.get(world.getFirstPlayer().getClassId()), BOSS_LOC, false, 0, false, world.getId());
world.broadcastPacket(new Earthquake(boss, 50, 10));
break;
}
}
managerWorldAttack(world, spawnedNpcs);
}
private void managerWorldAttack(Instance world, List<L2Npc> spawnedNpcs)
{
final List<FriendlyNpcInstance> helperList = L2World.getInstance().getVisibleObjects(world.getFirstPlayer(), FriendlyNpcInstance.class);
if ((spawnedNpcs != null) && !spawnedNpcs.isEmpty())
{
for (L2Npc npc : spawnedNpcs)
{
if (!helperList.isEmpty())
{
final FriendlyNpcInstance helper = helperList.get(getRandom(helperList.size()));
if (CommonUtil.contains(HELPERS, helper.getId()))
{
npc.reduceCurrentHp(1, helper, null);
helper.reduceCurrentHp(1, npc, null);
addAttackDesire(helper, npc);
helperList.remove(helper);
}
}
}
}
for (FriendlyNpcInstance helper : helperList)
{
for (L2Attackable monster : L2World.getInstance().getVisibleObjects(helper, L2Attackable.class))
{
if (!(monster instanceof FriendlyNpcInstance))
{
addAttackDesire(helper, monster);
}
}
}
}
private QuestState getQuestState(L2PcInstance player)
{
if (player == null)
{
return null;
}
return RACE_QUESTS.containsKey(player.getRace()) ? player.getQuestState(RACE_QUESTS.get(player.getRace())) : null;
}
public static void main(String[] args)
{
new EvilIncubator();
}
}

View File

@@ -0,0 +1,3 @@
<html><body>Magister Ayanthe:<br>
The monsters are coming. Concentrate and attack!
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Magister Ayanthe:<br>
Focus on the fight!<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest FaeronTrainingGrounds1 33944-03.html">"Create more monsters so I can train."</Button>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Magister Ayanthe:<br>
Good. It pleases me that you're taking your training so seriously.<br>
But bear in mind that this is training, and not the real world. We will limit the number of monsters you face in a single encounter.
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Magister Ayanthe:<br>
You're better than I expected. Feels different from training with a dummy, right?<br>
Here, take these <font color="LEVEL">Spiritshots</font>. Right now, your weapon has no grade, but as you become stronger, you'll be using D-grade, C-grade, and even R-grade weapons. They're proving to be very valuable in the Material Realm.<br>
Go ahead and <font color="LEVEL">activate the Spiritshots</font>.<br>
I'll go prepare more monsters for your next training session, so let me know when the Spiritshots have been activated.<br>
(You can conveniently automate your Spiritshots by creating a <font color="LEVEL">shortcut</font> and then <font color="LEVEL">right-clicking</font> on it.)
</body></html>

View File

@@ -0,0 +1,3 @@
<html><head><body>Magister Ayanthe:<br>
All right, now your training begins again!
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Mastery Ayanthe:<br>
I see you're a quick study. I'm impressed. I can't wait to see how much you'll have improved in the future. You have recovered some of your power that has been sealed while passing through the Dimensional Rift. These are called <font color="LEVEL">Skills</font>.<br>
The more abilities you recover, the stronger you'll grow.<br>
Now that you've recovered some of your power, shall we face tougher enemies? Are you ready?<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest FaeronTrainingGrounds1 33944-07.html">"My body is ready."</Button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Magister Ayanthe:<br>
If you don't use your <font color="LEVEL">skills</font>, this next battle will be difficult!
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Magister Ayanthe:<br>
Great work. You've made me very proud. You must be exhausted. Shall we step outside?<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest FaeronTrainingGrounds1 exit_instance">"I'd like to go outside."</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Magister Ayanthe:<br>
Don't underestimate the enemy you see before you.<br>
While in battle, you must never lose focus.<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h npc_%objectId%_Quest FaeronTrainingGrounds1">Quest</Button>
</body></html>

View File

@@ -0,0 +1,310 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.FaeronTrainingGrounds1;
import com.l2jmobius.gameserver.enums.QuestSound;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.NpcStringId;
import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
import com.l2jmobius.gameserver.network.serverpackets.TutorialShowHtml;
import instances.AbstractInstance;
import quests.Q10735_ASpecialPower.Q10735_ASpecialPower;
/**
* Fearon Training Grounds Instance Zone.
* @author Sdw, malyelfik
*/
public final class FaeronTrainingGrounds1 extends AbstractInstance
{
// NPCs
private static final int AYANTHE = 33942;
private static final int AYANTHE_2 = 33944;
// Monsters
private static final int FLOATO = 27526;
private static final int FLOATO2 = 27531;
private static final int RATEL = 27527;
// Items
private static final ItemHolder SPIRITSHOTS_TRAINING = new ItemHolder(2509, 150);
// Locations
private static final Location[] MOB_SPAWNS =
{
new Location(-74721, 240513, -3584),
new Location(-74760, 240773, -3560)
};
// Misc
private static final int TEMPLATE_ID = 251;
private static final double DAMAGE_BY_SKILL = 0.5d; // Percent
public FaeronTrainingGrounds1()
{
super(TEMPLATE_ID);
addStartNpc(AYANTHE, AYANTHE_2);
addFirstTalkId(AYANTHE_2);
addTalkId(AYANTHE, AYANTHE_2);
addKillId(FLOATO, FLOATO2, RATEL);
addSkillSeeId(RATEL);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
final QuestState qs = player.getQuestState(Q10735_ASpecialPower.class.getSimpleName());
String htmltext = null;
if (qs == null)
{
return htmltext;
}
switch (event)
{
case "enter_instance":
enterInstance(player, npc, TEMPLATE_ID);
break;
case "exit_instance":
finishInstance(player, 0);
break;
case "33944-03.html":
{
if (qs.isCond(6))
{
spawnMonsters(RATEL, player);
showOnScreenMsg(player, NpcStringId.FIGHT_USING_SKILLS, ExShowScreenMessage.TOP_CENTER, 10000);
}
else
{
final int npcId = (qs.isCond(4)) ? FLOATO2 : FLOATO;
spawnMonsters(npcId, player);
showOnScreenMsg(player, NpcStringId.ATTACK_THE_MONSTER, ExShowScreenMessage.TOP_CENTER, 10000);
}
htmltext = event;
break;
}
case "33944-07.html":
{
if (qs.isCond(5))
{
qs.setCond(6, true);
showOnScreenMsg(player, NpcStringId.FIGHT_USING_SKILLS, ExShowScreenMessage.TOP_CENTER, 10000);
spawnMonsters(RATEL, player);
htmltext = event;
}
break;
}
}
return htmltext;
}
@Override
public String onTalk(L2Npc npc, L2PcInstance player)
{
final QuestState qs = player.getQuestState(Q10735_ASpecialPower.class.getSimpleName());
String htmltext = getNoQuestMsg(player);
if (qs == null)
{
return htmltext;
}
if ((npc.getId() == AYANTHE_2) && qs.isStarted())
{
switch (qs.getCond())
{
case 1:
{
qs.setCond(2, true);
spawnMonsters(FLOATO, player);
showOnScreenMsg(player, NpcStringId.ATTACK_THE_MONSTER, ExShowScreenMessage.TOP_CENTER, 10000);
htmltext = "33944-01.html";
break;
}
case 2:
case 4:
case 6:
{
htmltext = "33944-02.html";
break;
}
case 3:
{
if (qs.getInt("ss") == 1)
{
spawnMonsters(FLOATO2, player);
showOnScreenMsg(player, NpcStringId.ATTACK_THE_MONSTER, ExShowScreenMessage.TOP_CENTER, 10000);
qs.setCond(4, true);
htmltext = "33944-05.html";
}
else
{
qs.set("ss", 1);
giveItems(player, SPIRITSHOTS_TRAINING);
showOnScreenMsg(player, NpcStringId.AUTOMATE_SPIRITSHOT_AS_SHOWN_IN_THE_TUTORIAL, ExShowScreenMessage.TOP_CENTER, 10000);
player.sendPacket(new TutorialShowHtml(npc.getObjectId(), "..\\L2Text\\QT_003_bullet_01.htm", TutorialShowHtml.LARGE_WINDOW));
htmltext = "33944-04.html";
}
break;
}
case 5:
{
player.sendPacket(new TutorialShowHtml(npc.getObjectId(), "..\\L2Text\\QT_004_skill_01.htm", TutorialShowHtml.LARGE_WINDOW));
htmltext = "33944-06.html";
break;
}
case 7:
{
htmltext = "33944-08.html";
break;
}
}
}
return htmltext;
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
// Check if monster is inside instance
final Instance world = npc.getInstanceWorld();
if (world == null)
{
return super.onKill(npc, killer, isSummon);
}
// Remove monster from instance spawn holder
world.setParameter("Mob" + npc.getScriptValue(), null);
// Handle quest state
final QuestState qs = killer.getQuestState(Q10735_ASpecialPower.class.getSimpleName());
if (qs != null)
{
switch (npc.getId())
{
case FLOATO:
case FLOATO2:
{
if ((qs.isCond(2) || qs.isCond(4)) && onKillQuestChange(killer, qs))
{
despawnMonsters(killer);
if (qs.isCond(5) && (killer.getLevel() < 5))
{
addExpAndSp(killer, 1716, 0);
}
}
break;
}
case RATEL:
{
if (qs.isCond(6) && onKillQuestChange(killer, qs))
{
despawnMonsters(killer);
showOnScreenMsg(killer, NpcStringId.TALK_TO_AYANTHE_TO_LEAVE_THE_TRAINING_GROUNDS, ExShowScreenMessage.TOP_CENTER, 10000);
}
break;
}
}
}
return super.onKill(npc, killer, isSummon);
}
@Override
public String onSkillSee(L2Npc npc, L2PcInstance player, Skill skill, com.l2jmobius.gameserver.model.L2Object[] targets, boolean isSummon)
{
if (!npc.isDead() && (player.getTarget() == npc))
{
final double dmg = npc.getMaxHp() * DAMAGE_BY_SKILL;
npc.reduceCurrentHp(dmg, player, null);
}
return super.onSkillSee(npc, player, skill, targets, isSummon);
}
/**
* Handle death of training monster. When all monsters are killed, quest cond is increased.
* @param killer player who killed monster
* @param qs quest state of killer
* @return {@code true} when all monsters are killed, otherwise {@code false}
*/
private boolean onKillQuestChange(L2PcInstance killer, QuestState qs)
{
final int value = qs.getMemoStateEx(Q10735_ASpecialPower.KILL_COUNT_VAR) + 1;
if (value >= 2)
{
qs.setCond(qs.getCond() + 1, true);
qs.setMemoStateEx(Q10735_ASpecialPower.KILL_COUNT_VAR, 0);
return true;
}
playSound(killer, QuestSound.ITEMSOUND_QUEST_ITEMGET);
qs.setMemoStateEx(Q10735_ASpecialPower.KILL_COUNT_VAR, value);
qs.getQuest().sendNpcLogList(killer);
return false;
}
/**
* Spawn training monsters inside instance
* @param npcId template id of training monster
* @param player player that owns instance
*/
private void spawnMonsters(int npcId, L2PcInstance player)
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
final StatsSet params = world.getParameters();
for (int i = 0; i < MOB_SPAWNS.length; i++)
{
if (params.getObject("Mob" + i, L2Npc.class) == null)
{
final L2Npc npc = addSpawn(npcId, MOB_SPAWNS[i], false, 0, false, world.getId());
npc.setScriptValue(i);
params.set("Mob" + i, npc);
}
}
}
}
/**
* Despawn training monsters inside instance
* @param player player that owns instance
*/
private void despawnMonsters(L2PcInstance player)
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
final StatsSet params = world.getParameters();
for (int i = 0; i < MOB_SPAWNS.length; i++)
{
final L2Npc mob = params.getObject("Mob" + i, L2Npc.class);
if (mob != null)
{
mob.deleteMe();
params.remove("Mob" + i);
}
}
}
}
public static void main(String[] args)
{
new FaeronTrainingGrounds1();
}
}

View File

@@ -0,0 +1,4 @@
<html><body>Master Katalin:<br>
The monsters are here.<br>
Stay focused in battle and take care not to get hurt.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Master Katalin:<br>
Focus on your battle!<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest FaeronTrainingGrounds2 33945-03.html">"More monsters please."</button>
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Master Katalin:<br>
It's good to see you train hard.<br>
But for your safety, there's a limit to the number of monsters at once.
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Master Katalin:<br>
Different from the Training Dummies?<br>
Take these <font color="LEVEL">Soulshots</font>. They have no grade, but as you get stronger and get better weapons, you can use Grade D, C, B, A, S, S-80, and eventually R. It's very important in the Material Realm.<br>
<font color="LEVEL">Activate Soulshots</font>.<br>
I will prepare the next set of monsters. Tell me when you have activated the Soulshots.<br>
(How to Enable Auto-Use Soulshot or Spiritshot: Open your <font color="LEVEL">Inventory</font>. <font color="LEVEL">Click and Drag</font> Soulshot/Spiritshot into your Shortcut Bar. Right Click the Soulshot/Spiritshot Shortcut Icon to use it automatically.)
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Master Katalin:<br>
Okay, here we go again.
</body></html>

View File

@@ -0,0 +1,7 @@
<html><body>Master Katalin:<br>
Do you know what just happened? You were able to use a lot of abilities when you were in the Wind Spirit Realm. But as you came through the Dimensional Rift, your abilities were locked.<br>
But you just unlocked one of those abilities. They call them <font color="LEVEL">Skills</font> here.<br>
As you train more, you will be able to get more skills back.<br>
Shall we fight a more powerful enemy with your new skill?<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest FaeronTrainingGrounds2 33945-07.html">"Okay."</button>
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Master Katalin:<br>
This battle won't be easy without your <font color="LEVEL">Skills</font>!
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Master Katalin:<br>
Good work! I am proud of you.<br>
We're done. Let's get out.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest FaeronTrainingGrounds2 exit_instance">"Okay."</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Master Katalin:<br>
You can't let your guard down with your enemy in front of you.<br>
You must always focus during battle.<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h npc_%objectId%_Quest FaeronTrainingGrounds2">Quest</Button>
</body></html>

View File

@@ -0,0 +1,311 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.FaeronTrainingGrounds2;
import com.l2jmobius.gameserver.enums.QuestSound;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.NpcStringId;
import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
import com.l2jmobius.gameserver.network.serverpackets.TutorialShowHtml;
import instances.AbstractInstance;
import quests.Q10736_ASpecialPower.Q10736_ASpecialPower;
/**
* Fearon Training Grounds Instance Zone.
* @author Sdw, malyelfik
*/
public final class FaeronTrainingGrounds2 extends AbstractInstance
{
// NPCs
private static final int KATALIN = 33943;
private static final int KATALIN_2 = 33945;
// Monsters
private static final int FLOATO = 27526;
private static final int FLOATO2 = 27531;
private static final int RATEL = 27527;
// Item
private static final ItemHolder SOULSHOTS_TRAINING = new ItemHolder(1835, 150);
// Locations
private static final Location[] MOB_SPAWNS =
{
new Location(-74760, 240773, -3560),
new Location(-74721, 240513, -3584)
};
// Misc
private static final int TEMPLATE_ID = 252;
private static final double DAMAGE_BY_SKILL = 0.5d; // Percent
public FaeronTrainingGrounds2()
{
super(TEMPLATE_ID);
addStartNpc(KATALIN, KATALIN_2);
addFirstTalkId(KATALIN_2);
addTalkId(KATALIN, KATALIN_2);
addKillId(FLOATO, FLOATO2, RATEL);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
final QuestState qs = player.getQuestState(Q10736_ASpecialPower.class.getSimpleName());
String htmltext = null;
if (qs == null)
{
return htmltext;
}
switch (event)
{
case "enter_instance":
{
enterInstance(player, npc, TEMPLATE_ID);
break;
}
case "exit_instance":
{
finishInstance(player, 0);
break;
}
case "33945-03.html":
{
if (qs.isCond(6))
{
spawnMonsters(RATEL, player);
showOnScreenMsg(player, NpcStringId.FIGHT_USING_SKILLS, ExShowScreenMessage.TOP_CENTER, 10000);
}
else
{
final int npcId = (qs.isCond(4)) ? FLOATO2 : FLOATO;
spawnMonsters(npcId, player);
showOnScreenMsg(player, NpcStringId.ATTACK_THE_MONSTER, ExShowScreenMessage.TOP_CENTER, 10000);
}
htmltext = event;
break;
}
case "33945-07.html":
{
if (qs.isCond(5))
{
qs.setCond(6, true);
showOnScreenMsg(player, NpcStringId.FIGHT_USING_SKILLS, ExShowScreenMessage.TOP_CENTER, 10000);
spawnMonsters(RATEL, player);
htmltext = event;
}
break;
}
}
return htmltext;
}
@Override
public String onTalk(L2Npc npc, L2PcInstance player)
{
final QuestState qs = player.getQuestState(Q10736_ASpecialPower.class.getSimpleName());
String htmltext = getNoQuestMsg(player);
if (qs == null)
{
return htmltext;
}
if ((npc.getId() == KATALIN_2) && qs.isStarted())
{
switch (qs.getCond())
{
case 1:
{
qs.setCond(2, true);
spawnMonsters(FLOATO, player);
htmltext = "33945-01.html";
break;
}
case 2:
case 4:
case 6:
{
htmltext = "33945-02.html";
break;
}
case 3:
{
if (qs.getInt("ss") == 1)
{
spawnMonsters(FLOATO2, player);
qs.setCond(4, true);
htmltext = "33945-05.html";
}
else
{
qs.set("ss", 1);
giveItems(player, SOULSHOTS_TRAINING);
showOnScreenMsg(player, NpcStringId.AUTOMATE_SOULSHOT_AS_SHOWN_IN_THE_TUTORIAL, ExShowScreenMessage.TOP_CENTER, 10000);
player.sendPacket(new TutorialShowHtml(npc.getObjectId(), "..\\L2Text\\QT_003_bullet_01.htm", TutorialShowHtml.LARGE_WINDOW));
htmltext = "33945-04.html";
}
break;
}
case 5:
{
player.sendPacket(new TutorialShowHtml(npc.getObjectId(), "..\\L2Text\\QT_004_skill_01.htm", TutorialShowHtml.LARGE_WINDOW));
htmltext = "33945-06.html";
break;
}
case 7:
{
htmltext = "33945-08.html";
break;
}
}
}
return htmltext;
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
// Check if monster is inside instance
final Instance world = npc.getInstanceWorld();
if (world == null)
{
return super.onKill(npc, killer, isSummon);
}
// Remove monster from instance spawn holder
world.setParameter("Mob" + npc.getScriptValue(), null);
// Handle quest state
final QuestState qs = killer.getQuestState(Q10736_ASpecialPower.class.getSimpleName());
if (qs != null)
{
switch (npc.getId())
{
case FLOATO:
case FLOATO2:
{
if ((qs.isCond(2) || qs.isCond(4)) && onKillQuestChange(killer, qs))
{
despawnMonsters(killer);
if (qs.isCond(5) && (killer.getLevel() < 5))
{
addExpAndSp(killer, 1716, 0);
}
}
break;
}
case RATEL:
{
if (qs.isCond(6) && onKillQuestChange(killer, qs))
{
despawnMonsters(killer);
showOnScreenMsg(killer, NpcStringId.TALK_TO_KATALIN_TO_LEAVE_THE_TRAINING_GROUNDS, ExShowScreenMessage.TOP_CENTER, 10000);
}
break;
}
}
}
return super.onKill(npc, killer, isSummon);
}
@Override
public String onSkillSee(L2Npc npc, L2PcInstance player, Skill skill, com.l2jmobius.gameserver.model.L2Object[] targets, boolean isSummon)
{
if (!npc.isDead() && (player.getTarget() == npc))
{
final double dmg = npc.getMaxHp() * DAMAGE_BY_SKILL;
npc.reduceCurrentHp(dmg, player, null);
}
return super.onSkillSee(npc, player, skill, targets, isSummon);
}
/**
* Handle death of training monster. When all monsters are killed, quest cond is increased.
* @param killer player who killed monster
* @param qs quest state of killer
* @return {@code true} when all monsters are killed, otherwise {@code false}
*/
private boolean onKillQuestChange(L2PcInstance killer, QuestState qs)
{
final int value = qs.getMemoStateEx(Q10736_ASpecialPower.KILL_COUNT_VAR) + 1;
if (value >= 2)
{
qs.setCond(qs.getCond() + 1, true);
qs.setMemoStateEx(Q10736_ASpecialPower.KILL_COUNT_VAR, 0);
return true;
}
playSound(killer, QuestSound.ITEMSOUND_QUEST_ITEMGET);
qs.setMemoStateEx(Q10736_ASpecialPower.KILL_COUNT_VAR, value);
qs.getQuest().sendNpcLogList(killer);
return false;
}
/**
* Spawn training monsters inside instance
* @param npcId template id of training monster
* @param player player that owns instance
*/
private void spawnMonsters(int npcId, L2PcInstance player)
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
final StatsSet params = world.getParameters();
for (int i = 0; i < MOB_SPAWNS.length; i++)
{
if (params.getObject("Mob" + i, L2Npc.class) == null)
{
final L2Npc npc = addSpawn(npcId, MOB_SPAWNS[i], false, 0, false, world.getId());
npc.setScriptValue(i);
params.set("Mob" + i, npc);
}
}
}
}
/**
* Despawn training monsters inside instance
* @param player player that owns instance
*/
private void despawnMonsters(L2PcInstance player)
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
final StatsSet params = world.getParameters();
for (int i = 0; i < MOB_SPAWNS.length; i++)
{
final L2Npc mob = params.getObject("Mob" + i, L2Npc.class);
if (mob != null)
{
mob.deleteMe();
params.remove("Mob" + i);
}
}
}
}
public static void main(String[] args)
{
new FaeronTrainingGrounds2();
}
}

View File

@@ -0,0 +1,260 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.FortressDungeon;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.gameserver.ThreadPoolManager;
import com.l2jmobius.gameserver.enums.QuestSound;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.entity.Fort;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.instancezone.InstanceTemplate;
import com.l2jmobius.gameserver.model.quest.QuestState;
import instances.AbstractInstance;
import quests.Q00511_AwlUnderFoot.Q00511_AwlUnderFoot;
/**
* <b>Fortress dungeon</b> instance for quest <b>Awl Under Foot (511)</b>
* @author malyelfik
*/
public final class FortressDungeon extends AbstractInstance
{
// NPCs
private static final Map<Integer, Integer> NPCS = new HashMap<>();
static
{
NPCS.put(35666, 22); // Shanty
NPCS.put(35698, 23); // Southern
NPCS.put(35735, 24); // Hive
NPCS.put(35767, 25); // Valley
NPCS.put(35804, 26); // Ivory
NPCS.put(35835, 27); // Narsell
NPCS.put(35867, 28); // Bayou
NPCS.put(35904, 29); // White Sands
NPCS.put(35936, 30); // Borderland
NPCS.put(35974, 31); // Swamp
NPCS.put(36011, 32); // Archaic
NPCS.put(36043, 33); // Floran
NPCS.put(36081, 34); // Cloud Mountain
NPCS.put(36118, 35); // Tanor
NPCS.put(36149, 36); // Dragonspine
NPCS.put(36181, 37); // Antharas
NPCS.put(36219, 38); // Western
NPCS.put(36257, 39); // Hunter
NPCS.put(36294, 40); // Aaru
NPCS.put(36326, 41); // Demon
NPCS.put(36364, 42); // Monastic
}
// Monsters
private static final int[] RAIDS1 =
{
25572,
25575,
25578
};
private static final int[] RAIDS2 =
{
25579,
25582,
25585,
25588
};
private static final int[] RAIDS3 =
{
25589,
25592,
25593
};
// Item
private static final int MARK = 9797;
// Locations
private static final Location SPAWN_LOC = new Location(53319, 245814, -6576);
// Misc
private static final int MARK_COUNT = 2520;
private static final long REENTER = 24 * 3600000; // 24 hours
private static final Map<Integer, Long> REENETER_HOLDER = new ConcurrentHashMap<>();
public FortressDungeon()
{
super(NPCS.values().stream().mapToInt(Integer::valueOf).toArray());
// NPCs
addStartNpc(NPCS.keySet());
addTalkId(NPCS.keySet());
// Monsters
addKillId(RAIDS1);
addKillId(RAIDS2);
addKillId(RAIDS3);
// Instance
addInstanceCreatedId(NPCS.values());
addInstanceDestroyId(NPCS.values());
}
@Override
public String onTalk(L2Npc npc, L2PcInstance player)
{
final int npcId = npc.getId();
if (NPCS.containsKey(npcId))
{
enterInstance(player, npc, NPCS.get(npcId));
}
return null;
}
@Override
public String onKill(L2Npc npc, L2PcInstance player, boolean isSummon)
{
final Instance world = npc.getInstanceWorld();
if (world != null)
{
if (CommonUtil.contains(RAIDS3, npc.getId()))
{
// Get players with active quest
final List<L2PcInstance> members = new ArrayList<>();
for (L2PcInstance member : world.getPlayers())
{
final QuestState qs = member.getQuestState(Q00511_AwlUnderFoot.class.getSimpleName());
if ((qs != null) && qs.isCond(1))
{
members.add(member);
}
}
// Distribute marks between them
if (!members.isEmpty())
{
final long itemCount = MARK_COUNT / members.size();
for (L2PcInstance member : members)
{
giveItems(member, MARK, itemCount);
playSound(member, QuestSound.ITEMSOUND_QUEST_ITEMGET);
}
}
world.finishInstance();
}
else
{
world.incStatus();
spawnRaid(world);
}
}
return super.onKill(npc, player, isSummon);
}
@Override
public void onInstanceCreated(Instance instance, L2PcInstance player)
{
// Put re-enter for instance
REENETER_HOLDER.put(instance.getTemplateId(), System.currentTimeMillis() + REENTER);
// Schedule spawn of first raid
spawnRaid(instance);
}
@Override
protected boolean validateConditions(List<L2PcInstance> group, L2Npc npc, InstanceTemplate template)
{
final L2PcInstance groupLeader = group.get(0);
final Fort fort = npc.getFort();
if (fort == null)
{
showHtmlFile(groupLeader, "noProperPledge.html");
return false;
}
else if (fort.getFortState() == 0)
{
showHtmlFile(groupLeader, "noContractYet.html");
return false;
}
else if (fort.getFortState() == 2)
{
showHtmlFile(groupLeader, "noCastleContract.html");
return false;
}
else if (REENETER_HOLDER.containsKey(template.getId()))
{
final long time = REENETER_HOLDER.get(template.getId());
if (time > System.currentTimeMillis())
{
showHtmlFile(groupLeader, "enterRestricted.html");
return false;
}
REENETER_HOLDER.remove(template.getId());
}
return true;
}
@Override
public void onInstanceDestroy(Instance instance)
{
// Stop running spawn task
final ScheduledFuture<?> task = instance.getParameters().getObject("spawnTask", ScheduledFuture.class);
if ((task != null) && !task.isDone())
{
task.cancel(true);
}
instance.setParameter("spawnTask", null);
}
/**
* Spawn raid boss according to instance status.
* @param instance instance world where instance should be spawned
*/
private void spawnRaid(Instance instance)
{
final ScheduledFuture<?> spawnTask = ThreadPoolManager.getInstance().scheduleGeneral(() ->
{
// Get template id of raid
final int npcId;
switch (instance.getStatus())
{
case 0:
npcId = RAIDS1[getRandom(RAIDS1.length)];
break;
case 1:
npcId = RAIDS2[getRandom(RAIDS2.length)];
break;
default:
npcId = RAIDS3[getRandom(RAIDS3.length)];
}
// Spawn raid
addSpawn(npcId, SPAWN_LOC, false, 0, false, instance.getId());
// Unset spawn task reference
instance.setParameter("spawnTask", null);
}, 2, TimeUnit.MINUTES);
// Save timer to instance world
instance.setParameter("spawnTask", spawnTask);
}
public static void main(String[] args)
{
new FortressDungeon();
}
}

View File

@@ -0,0 +1,4 @@
<html><body>Training is currently closed.<br>
You can undertake training once every 24 hours with your clan.<br>
If training is closed, check with your clan members to see when the last training session started.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>
Only a party may enter the camp. Once you begin the training session, you may not attend another one for at least 24 hours.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>Off limits! Lord's orders.<br>
(You cannot enter the camp with a contract related to the castle.)
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>
The fortress is currently in an unsettled state because our relationship with the castle lord must be clearly determined.<br>
(Entry into the camps is only possible after gaining independence from the main castle.)
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>
To enter the Detention Camp, your party must include at least 2 people.
</body></html>

View File

@@ -0,0 +1,3 @@
<html><body>
The clan does not possess this fortress.
</body></html>

View File

@@ -0,0 +1,4 @@
<html><body>Kain Van Halter:<br>
You're Ertheia. Child of the wind. Why would you try to kill Giselle all the way out here?<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-02.html">"I didn't know that it was Giselle. She attacked first!"</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Kain Van Halter:<br>
That's not a good reason. The fact that you came all the way here alone is suspicious.<br>
You were after Giselle's life in the first place!<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-03.html">"I have Queen Navari's Mark."</button>
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Kain Van Halter:<br>
The Queen of Ertheia sent you?<br>
All right, then. Since Giselle became very aggressive after becoming a vampire, I will take your word on your dispute.<br>
But what brings you all the way here? Why were you looking for Giselle? I can't imagine Ertheia having business with her.<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-04.html">"I was looking for you."</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Kain Van Halter:<br>
Me?<br>
What do you want with a wanderer like me? And I don't see why I should even help you. You tried to harm my sister! Good thing I stopped by to check on her.<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-05.html">"It's about the Prophecy Machine."</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Kain Van Halter:<br>
Prophecy Machine?<br>
What about the Prophecy Machine?<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-06.html">"Here, take a look."</button>
</body></html>

View File

@@ -0,0 +1,9 @@
<html><body>Kain Van Halter:<br>
<center><font color="LEVEL">A red river flows...hard frost upon the earth...<br1>
One of half-noble blood,<br1>
descending with the winds to deliver from...<br1>
When joined by one...<br1>
The path will open to the Grail.</font></center><br1>
So why are you showing me this?<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-07.html">"I know that you're interested in the Grail."</button>
</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Kain Van Halter:<br>
I see. Did Karla tell you? So she still remembers me mentioning the Grail that day.<br>
Check this out first.<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-08.html">"This?"</button>
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Kain Van Halter:<br>
Surprised? I found it while passing Goddard Territory.<br>
This looks similar to the Prophecy Machine that you showed me. It's probably a part of the whole.<br>
And about what's written on your machine... I think I know what it's saying.<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-09.html">"Do tell."</button>
</body></html>

View File

@@ -0,0 +1,6 @@
<html><body>Kain Van Halter:<br>
<font color="LEVEL">A red river flows...Hm. I think I can see where this is going.</font><br>
It's talking about this continent. A red river flows...hard frost upon the earth. You see, <font color="LEVEL">Elmore</font> rules over a cold part of the land, which it once shared with Aden as Elmoreden. Now the whole continent is swarming with Shilen's bloody works.<br>
But this part is surprising. When joined by one...the path will open to the Grail. Truly?<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead 33979-10.html">"Who is this one?"</button>
</body></html>

View File

@@ -0,0 +1,8 @@
<html><body>Kain Van Halter:<br>
<font color="LEVEL">One of half-noble blood...</font><br>
Ha! Even the prophecy of Ertheia describes me so, huh? Well, it's right. My mother was a commoner. A tragic farce, to be loved by the king! So here I am, a prince only in name. And descending with the winds to deliver from...yes, that is me. I happened to save Faeron once. <font color="LEVEL">Kain</font> is your man!<br>
The <font color="LEVEL">Grail</font>...if it's the same <font color="LEVEL">Grail</font>, then...anyway, you can read the rest of the prophecy with the fragment that I found, right?<br>
Take it. Your village needs it more than I.<br>
Hmm? Wait...<br>
<Button ALIGN=LEFT ICON="Normal" action="bypass -h Quest FortressOfTheDead spawnWizard">"What?"</button>
</body></html>

View File

@@ -0,0 +1,2 @@
<html><body>Kain Van Halter:<br>
Who is that person standing there?</body></html>

View File

@@ -0,0 +1,5 @@
<html><body>Kain Van Halter:<br>
You trespass on the land of death. Who are you?<br1>
No matter. You threatened Giselle with harm, and you will answer to me!<br>
<Button ALIGN=LEFT ICON="QUEST" action="bypass -h Quest FortressOfTheDead 33979-01.html">Quest</button>
</body></html>

View File

@@ -0,0 +1,165 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.FortressOfTheDead;
import com.l2jmobius.gameserver.enums.Movie;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import com.l2jmobius.gameserver.network.NpcStringId;
import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
import instances.AbstractInstance;
import quests.Q10752_WindsOfFateAPromise.Q10752_WindsOfFateAPromise;
/**
* Fortress of the Dead instance zone.
* @author Gladicek
*/
public final class FortressOfTheDead extends AbstractInstance
{
// NPCs
private static final int BROKEN_BOOKSHELF = 31526;
private static final int VAMPIRIC_SOLDIER = 19567;
private static final int VON_HELLMAN = 19566;
private static final int MYSTERIOUS_WIZARD = 33980;
private static final int KAIN_VAN_HALTER = 33979;
// Items
private static final int KAIN_PROPHECY_MACHINE_FRAGMENT = 39538;
// Location
private static final Location VON_HELLMAN_LOC = new Location(57963, -28676, 568, 49980);
private static final Location MYSTERIOUS_WIZARD_LOC = new Location(57982, -28645, 568);
private static final Location KAIN_VAN_HALTER_LOC = new Location(57963, -28676, 568, 49980);
// Misc
private static final int TEMPLATE_ID = 254;
public FortressOfTheDead()
{
super(TEMPLATE_ID);
addStartNpc(BROKEN_BOOKSHELF);
addFirstTalkId(KAIN_VAN_HALTER);
addTalkId(BROKEN_BOOKSHELF, KAIN_VAN_HALTER, MYSTERIOUS_WIZARD);
addKillId(VAMPIRIC_SOLDIER, VON_HELLMAN);
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
String htmltext = null;
if (event.equals("enterInstance"))
{
enterInstance(player, npc, TEMPLATE_ID);
}
else
{
final Instance world = npc.getInstanceWorld();
if (isInInstance(world))
{
switch (event)
{
case "33979-01.html":
case "33979-02.html":
case "33979-03.html":
case "33979-04.html":
case "33979-05.html":
case "33979-06.html":
case "33979-07.html":
case "33979-08.html":
case "33979-09.html":
case "33979-10.html":
{
htmltext = event;
break;
}
case "exitInstance":
{
world.finishInstance(0);
break;
}
case "vampire_dead":
{
addSpawn(VON_HELLMAN, VON_HELLMAN_LOC, false, 0, false, world.getId());
break;
}
case "hellman_dead":
{
addSpawn(KAIN_VAN_HALTER, KAIN_VAN_HALTER_LOC, false, 0, false, world.getId());
break;
}
case "spawnWizard":
{
showOnScreenMsg(player, NpcStringId.TALK_TO_THE_MYSTERIOUS_WIZARD, ExShowScreenMessage.TOP_CENTER, 5000);
final L2Npc wizzard = addSpawn(MYSTERIOUS_WIZARD, MYSTERIOUS_WIZARD_LOC, true, 0, false, world.getId());
wizzard.setSummoner(player);
wizzard.setTitle(player.getAppearance().getVisibleName());
wizzard.broadcastInfo();
htmltext = "33979-11.html";
break;
}
case "endCinematic":
{
final QuestState qs = player.getQuestState(Q10752_WindsOfFateAPromise.class.getSimpleName());
if ((qs != null) && qs.isCond(8))
{
qs.setCond(9, true);
giveItems(player, KAIN_PROPHECY_MACHINE_FRAGMENT, 1);
}
world.getNpc(KAIN_VAN_HALTER).deleteMe();
world.getNpc(MYSTERIOUS_WIZARD).deleteMe();
playMovie(player, Movie.ERT_QUEST_B);
startQuestTimer("exitInstance", 25000, npc, player);
break;
}
}
}
}
return htmltext;
}
@Override
public String onKill(L2Npc npc, L2PcInstance player, boolean isSummon)
{
final Instance world = npc.getInstanceWorld();
if (isInInstance(world))
{
if (npc.getId() == VAMPIRIC_SOLDIER)
{
if (world.getAliveNpcs(VAMPIRIC_SOLDIER).isEmpty())
{
startQuestTimer("vampire_dead", 3000, npc, player);
}
}
else if (npc.getId() == VON_HELLMAN)
{
npc.deleteMe();
playMovie(player, Movie.ERT_QUEST_A);
startQuestTimer("hellman_dead", 6000, npc, player);
}
}
return super.onKill(npc, player, isSummon);
}
public static void main(String[] args)
{
new FortressOfTheDead();
}
}

View File

@@ -0,0 +1,4 @@
<html><body>Seal Control Device:<br>
This device controls the seals imprisoning the soul of Hermuncus.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest HarnakUndergroundRuins activate_seal">Release the seal.</Button>
</body></html>

View File

@@ -0,0 +1,658 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.HarnakUndergroundRuins;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.enums.CategoryType;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.enums.Movie;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.variables.NpcVariables;
import com.l2jmobius.gameserver.model.zone.L2ZoneType;
import com.l2jmobius.gameserver.network.NpcStringId;
import com.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
import instances.AbstractInstance;
/**
* Harnak Underground Ruins Instance Zone.
* @author Sdw
*/
public final class HarnakUndergroundRuins extends AbstractInstance
{
// NPCs
private static final int HADEL = 33344;
private static final int KRAKIA_BATHUS = 27437;
private static final int KRAKIA_CARCASS = 27438;
private static final int KRAKIA_LOTUS = 27439;
private static final int RAKZAN = 27440;
private static final int WEISS_KHAN = 27441;
private static final int BAMONTI = 27442;
private static final int SEKNUS = 27443;
private static final int WEISS_ELE = 27454;
private static final int HARNAKS_WRAITH = 27445;
private static final int SEAL_CONTROL_DEVICE = 33548;
private static final int POWER_SOURCE = 33501;
private static final int[] POWER_SOURCES =
{
33501,
33556,
33557
};
// Locations
private static final Location NPC_ROOM1_LOC = new Location(-107930, 206328, -10872);
// Skills
private static final SkillHolder RELEASE_OF_POWER = new SkillHolder(14625, 1);
private static final SkillHolder MAXIMUM_DEFENSE = new SkillHolder(14700, 1);
private static final SkillHolder LIGHT_HEAL = new SkillHolder(14736, 1);
private static final SkillHolder ULTIMATE_BUFF = new SkillHolder(4318, 1);
// Misc
private static final int TEMPLATE_ID = 195;
private static final int ZONE_ROOM_2 = 200032;
private static final int ZONE_ROOM_3 = 200033;
private static final int DOOR_ONE = 16240100;
private static final int DOOR_TWO = 16240102;
private static final Map<CategoryType, Integer> MOB_CATEGORY = new HashMap<>();
static
{
MOB_CATEGORY.put(CategoryType.SIGEL_CANDIDATE, RAKZAN);
MOB_CATEGORY.put(CategoryType.TYRR_CANDIDATE, KRAKIA_BATHUS);
MOB_CATEGORY.put(CategoryType.OTHELL_CANDIDATE, BAMONTI);
MOB_CATEGORY.put(CategoryType.YUL_CANDIDATE, KRAKIA_CARCASS);
MOB_CATEGORY.put(CategoryType.FEOH_CANDIDATE, WEISS_KHAN);
MOB_CATEGORY.put(CategoryType.ISS_CANDIDATE, SEKNUS);
MOB_CATEGORY.put(CategoryType.WYNN_CANDIDATE, KRAKIA_LOTUS);
MOB_CATEGORY.put(CategoryType.AEORE_CANDIDATE, WEISS_ELE);
}
public HarnakUndergroundRuins()
{
super(TEMPLATE_ID);
registerMobs(KRAKIA_BATHUS, KRAKIA_CARCASS, KRAKIA_LOTUS, RAKZAN, WEISS_KHAN, BAMONTI, SEKNUS, WEISS_ELE, HARNAKS_WRAITH);
addSeeCreatureId(POWER_SOURCES);
addEnterZoneId(ZONE_ROOM_2, ZONE_ROOM_3);
addFirstTalkId(SEAL_CONTROL_DEVICE);
addTalkId(HADEL);
addStartNpc(HADEL);
}
@Override
protected void onEnter(L2PcInstance player, Instance instance, boolean firstEnter)
{
super.onEnter(player, instance, firstEnter);
if (firstEnter)
{
startQuestTimer("fail_instance", 1260000, null, player);
startQuestTimer("message1", 2500, null, player);
startQuestTimer("message2", 5000, null, player);
startQuestTimer("message3", 8500, null, player);
startQuestTimer("spawn_npc1", 10000, null, player);
}
}
@Override
public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "enter_instance":
enterInstance(player, npc, TEMPLATE_ID);
break;
case "message1":
showOnScreenMsg(player, NpcStringId.AN_INTRUDER_INTERESTING, ExShowScreenMessage.TOP_CENTER, 5000);
break;
case "message2":
showOnScreenMsg(player, NpcStringId.PROVE_YOUR_WORTH, ExShowScreenMessage.TOP_CENTER, 5000);
break;
case "message3":
showOnScreenMsg(player, NpcStringId.ONLY_THOSE_STRONG_ENOUGH_SHALL_PROCEED, ExShowScreenMessage.TOP_CENTER, 5000);
break;
case "message4":
showOnScreenMsg(player, NpcStringId.THOUGH_SMALL_THIS_POWER_WILL_HELP_YOU_GREATLY, ExShowScreenMessage.TOP_CENTER, 5000);
break;
case "message5":
showOnScreenMsg(player, NpcStringId.ARE_YOU_STRONG_OR_WEAK_OF_THE_LIGHT_OR_DARKNESS, ExShowScreenMessage.TOP_CENTER, 5000);
break;
case "message6":
showOnScreenMsg(player, NpcStringId.ONLY_THOSE_OF_LIGHT_MAY_PASS_OTHERS_MUST_PROVE_THEIR_STRENGTH, ExShowScreenMessage.TOP_CENTER, 5000);
break;
case "razkan_say":
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.COME_ATTACK_ME_IF_YOU_DARE);
break;
case "bathus_say":
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.IT_S_THE_END_FOR_YOU_TRAITOR);
break;
case "bamonti_say":
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.I_WANT_TO_HEAR_YOU_CRY);
break;
case "carcass_say":
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.I_WANT_TO_HEAR_YOU_CRY);
break;
case "khan_say":
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.YOU_LL_HAVE_TO_KILL_US_FIRST);
break;
case "seknus_say":
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.LETS_SEE_WHAT_YOU_ARE_MADE_OF);
break;
case "lotus_say":
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.REPENT_AND_YOUR_DEATH_WILL_BE_QUICK);
break;
case "ele_say":
npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.DIE_TRAITOR);
break;
case "spawn_npc1":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
world.setStatus(1);
world.spawnGroup("first_room");
moveNpcRoom1(RAKZAN, NpcStringId.ARE_YOU_AGAINST_THE_WILL_OF_LIGHT, "razkan_say", world);
}
break;
}
case "spawn_npc2":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
world.openCloseDoor(DOOR_ONE, true);
world.spawnGroup("power_sources");
}
break;
}
case "spawn_npc3":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
world.incStatus();
final List<L2Npc> npcs = world.spawnGroup("third_room");
final L2Npc powerSource = npcs.stream().filter(n -> n.getId() == POWER_SOURCE).findFirst().orElse(null);
if (powerSource != null)
{
powerSource.setTarget(player);
startQuestTimer("cast_light_heal", 3000, powerSource, player);
}
}
break;
}
case "spawn_wave1":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
for (Entry<CategoryType, Integer> entry : MOB_CATEGORY.entrySet())
{
if (player.isInCategory(entry.getKey()))
{
world.setStatus(2);
world.setParameter("wave", 1);
world.setParameter("waveNpcId", entry.getValue());
final List<L2Npc> npcs = world.spawnGroup("second_room_wave_1_" + entry.getValue());
for (L2Npc n : npcs)
{
addAttackPlayerDesire(n, player);
}
break;
}
}
}
break;
}
case "spawn_wave2":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
final int waveNpcId = world.getParameters().getInt("waveNpcId");
final List<L2Npc> npcs = world.spawnGroup("second_room_wave_2_" + waveNpcId);
for (L2Npc n : npcs)
{
addAttackPlayerDesire(n, player);
}
world.setParameter("wave", 2);
}
break;
}
case "spawn_wave3":
{
showOnScreenMsg(player, NpcStringId.I_MUST_GO_HELP_SOME_MORE, ExShowScreenMessage.TOP_CENTER, 5000);
final Instance world = player.getInstanceWorld();
if (world != null)
{
final int waveNpcId = world.getParameters().getInt("waveNpcId");
List<L2Npc> npcs = world.spawnGroup("second_room_wave_3_" + waveNpcId);
for (L2Npc n : npcs)
{
addAttackPlayerDesire(n, player);
}
npcs = world.spawnGroup("power_source");
for (L2Npc n : npcs)
{
n.setTarget(player);
startQuestTimer("cast_defense_maximum", 1, n, player);
}
world.setParameter("wave", 3);
}
break;
}
case "cast_defense_maximum":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
if (npc.calculateDistance(player, true, false) < MAXIMUM_DEFENSE.getSkill().getCastRange())
{
npc.doCast(MAXIMUM_DEFENSE.getSkill());
final int defenseCounter = world.getParameters().getInt("defenseCounter", 0) + 1;
world.setParameter("defenseCounter", defenseCounter);
if (defenseCounter < 3)
{
startQuestTimer("cast_defense_maximum", 60000, npc, player);
}
else
{
npc.deleteMe();
}
}
else
{
startQuestTimer("cast_defense_maximum", 1, npc, player);
}
}
break;
}
case "cast_light_heal":
{
if (npc != null)
{
final Instance world = player.getInstanceWorld();
if ((world != null) && (world.isStatus(3) || world.isStatus(4)))
{
if (npc.calculateDistance(player, true, false) < LIGHT_HEAL.getSkill().getCastRange())
{
npc.doCast(LIGHT_HEAL.getSkill());
}
startQuestTimer("cast_light_heal", 3000, npc, player);
}
}
break;
}
case "fail_instance":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
world.removeNpcs();
playMovie(player, Movie.SC_AWAKENING_BOSS_ENDING_B);
startQuestTimer("exit", 13500, npc, player);
}
break;
}
case "exit":
finishInstance(player, 0);
break;
case "spawn_npc4":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
final int waveNpcId = world.getParameters().getInt("waveNpcId");
List<L2Npc> npcs = world.spawnGroup("third_room_" + waveNpcId);
for (L2Npc n : npcs)
{
addAttackPlayerDesire(n, player);
}
npcs = world.spawnGroup("seal");
for (L2Npc n : npcs)
{
n.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.DISABLE_DEVICE_WILL_GO_OUT_OF_CONTROL_IN_1_MINUTE);
startQuestTimer("seal_say", 10000, n, player);
}
}
break;
}
case "activate_seal":
{
final Instance world = player.getInstanceWorld();
if ((world != null) && npc.isScriptValue(0))
{
npc.setScriptValue(1);
final int enabledSeal = world.getParameters().getInt("enabledSeal", 0) + 1;
world.setParameter("enabledSeal", enabledSeal);
if (enabledSeal == 2)
{
cancelQuestTimer("fail_instance", null, player);
world.removeNpcs();
playMovie(player, Movie.SC_AWAKENING_BOSS_ENDING_A);
startQuestTimer("spawn_hermuncus", 25050, npc, player);
}
}
break;
}
case "seal_say":
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
final int timerCount = world.getParameters().getInt("timerCount", 0);
switch (timerCount)
{
case 0:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS_ARE_REMAINING41);
break;
case 1:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS_ARE_REMAINING42);
break;
case 2:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS_ARE_REMAINING43);
break;
case 3:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS_ARE_REMAINING44);
break;
case 4:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS_ARE_REMAINING45);
break;
case 5:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS);
break;
case 6:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS2);
break;
case 7:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS3);
break;
case 8:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECONDS4);
break;
case 9:
npc.broadcastSay(ChatType.NPC_SHOUT, NpcStringId.SECOND);
break;
}
if (timerCount <= 4)
{
startQuestTimer("seal_say", 10000, npc, player);
}
else if ((timerCount > 4) && (timerCount <= 9))
{
startQuestTimer("seal_say", 1000, npc, player);
}
world.setParameter("timerCount", timerCount);
}
break;
}
case "spawn_hermuncus":
{
final Instance inst = player.getInstanceWorld();
if (inst != null)
{
inst.spawnGroup("hermuncus");
}
break;
}
case "cast_release_power":
{
npc.setTarget(player);
npc.doCast(RELEASE_OF_POWER.getSkill());
break;
}
case "whisper_to_player":
{
showOnScreenMsg(player, NpcStringId.I_HERMUNCUS_GIVE_MY_POWER_TO_THOSE_WHO_FIGHT_FOR_ME, ExShowScreenMessage.TOP_CENTER, 5000);
npc.broadcastSay(ChatType.WHISPER, NpcStringId.RECEIVE_THIS_POWER_FORM_THE_ANCIENT_GIANT);
npc.broadcastSay(ChatType.WHISPER, NpcStringId.USE_THIS_NEW_POWER_WHEN_THE_TIME_IS_RIGHT);
startQuestTimer("message4", 3000, npc, player);
}
}
return null;
}
@Override
public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
{
final Instance world = killer.getInstanceWorld();
if (world != null)
{
switch (world.getStatus())
{
case 0:
{
if (world.getAliveNpcs().isEmpty())
{
startQuestTimer("spawn_npc2", 100, npc, killer);
world.setStatus(1);
}
break;
}
case 1:
{
switch (npc.getId())
{
case RAKZAN:
moveNpcRoom1(KRAKIA_BATHUS, NpcStringId.ARE_YOU_PLANNING_TO_BETRAY_THE_GODS_AND_FOLLOW_A_GIANT, "bathus_say", world);
break;
case KRAKIA_BATHUS:
moveNpcRoom1(BAMONTI, NpcStringId.HAHA, "bamonti_say", world);
break;
case BAMONTI:
moveNpcRoom1(KRAKIA_CARCASS, NpcStringId.HAHA, "carcass_say", world);
break;
case KRAKIA_CARCASS:
moveNpcRoom1(WEISS_KHAN, NpcStringId.YOU_WILL_NOT_FREE_HERMUNCUS, "khan_say", world);
break;
case WEISS_KHAN:
moveNpcRoom1(SEKNUS, NpcStringId.MORTAL, "seknus_say", world);
break;
case SEKNUS:
moveNpcRoom1(KRAKIA_LOTUS, NpcStringId.TRYING_TO_FREE_HERMUNCUS, "lotus_say", world);
break;
case KRAKIA_LOTUS:
moveNpcRoom1(WEISS_ELE, NpcStringId.YOU_WILL_NEVER_BREAK_THE_SEAL, "ele_say", world);
break;
case WEISS_ELE:
startQuestTimer("spawn_npc2", 100, npc, killer);
break;
}
break;
}
case 2:
{
final StatsSet params = world.getParameters();
final int waveNpc = params.getInt("waveNpcId");
if (world.getAliveNpcs(waveNpc).isEmpty())
{
switch (params.getInt("wave"))
{
case 1:
startQuestTimer("spawn_wave2", 100, npc, killer);
break;
case 2:
startQuestTimer("spawn_wave3", 100, npc, killer);
break;
case 3:
world.openCloseDoor(DOOR_TWO, true);
break;
}
}
break;
}
default:
{
if (npc.getId() == HARNAKS_WRAITH)
{
cancelQuestTimer("fail_instance", null, killer);
world.removeNpcs();
playMovie(killer, Movie.SC_AWAKENING_BOSS_ENDING_A);
startQuestTimer("spawn_hermuncus", 25050, npc, killer);
}
}
}
}
return super.onKill(npc, killer, isSummon);
}
private void moveNpcRoom1(int npcId, NpcStringId message, String timer, Instance world)
{
final L2Npc npc = world.getNpc(npcId);
if (npc != null)
{
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, NPC_ROOM1_LOC);
npc.broadcastSay(ChatType.NPC_GENERAL, message);
startQuestTimer("timer", 2600, npc, null);
world.setParameter("currentNpc", npcId);
}
}
@Override
public String onAttack(L2Npc npc, L2PcInstance player, int damage, boolean isSummon)
{
final Instance world = player.getInstanceWorld();
if (world != null)
{
if (world.isStatus(1))
{
final int currentNpc = world.getParameters().getInt("currentNpc");
if (npc.getId() != currentNpc)
{
world.setStatus(0);
for (L2Npc n : world.getAliveNpcs())
{
addAttackPlayerDesire(n, player);
}
}
}
else if (world.isStatus(2))
{
if (((npc.getCurrentHp() / npc.getMaxHp()) * 100) < 80)
{
npc.doCast(ULTIMATE_BUFF.getSkill());
}
}
else if (world.isStatus(3) && (npc.getId() == HARNAKS_WRAITH))
{
final NpcVariables vars = npc.getVariables();
if (!vars.getBoolean("message1", false) && (((npc.getCurrentHp() / npc.getMaxHp()) * 100) > 80))
{
showOnScreenMsg(player, NpcStringId.FREE_ME_FROM_THIS_BINDING_OF_LIGHT, ExShowScreenMessage.TOP_CENTER, 5000);
vars.set("message1", true);
}
else if (!vars.getBoolean("message2", false) && (((npc.getCurrentHp() / npc.getMaxHp()) * 100) <= 80))
{
showOnScreenMsg(player, NpcStringId.DESTROY_THE_GHOST_OF_HARNAK_THIS_CORRUPTED_CREATURE, ExShowScreenMessage.TOP_CENTER, 5000);
vars.set("message2", true);
}
else if (!vars.getBoolean("message3", false) && (((npc.getCurrentHp() / npc.getMaxHp()) * 100) <= 60))
{
showOnScreenMsg(player, NpcStringId.FREE_ME_AND_I_PROMISE_YOU_THE_POWER_OF_GIANTS, ExShowScreenMessage.TOP_CENTER, 5000);
vars.set("message3", true);
}
else if (((npc.getCurrentHp() / npc.getMaxHp()) * 100) <= 50)
{
world.incStatus();
player.sendPacket(new ExSendUIEvent(player, false, false, 60, 0, NpcStringId.REMAINING_TIME));
showOnScreenMsg(player, NpcStringId.NO_THE_SEAL_CONTROLS_HAVE_BEEN_EXPOSED_GUARDS_PROTECT_THE_SEAL_CONTROLS, ExShowScreenMessage.TOP_CENTER, 10000);
startQuestTimer("spawn_npc4", 1, npc, player);
cancelQuestTimer("fail_instance", null, player);
startQuestTimer("fail_instance", 60000, null, player);
}
}
}
return super.onAttack(npc, player, damage, isSummon);
}
@Override
public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
{
if (creature.isPlayer())
{
startQuestTimer("cast_release_power", 2000, npc, creature.getActingPlayer());
if (npc.getId() == POWER_SOURCE)
{
startQuestTimer("whisper_to_player", 2000, npc, creature.getActingPlayer());
}
}
return super.onSeeCreature(npc, creature, isSummon);
}
@Override
public String onEnterZone(L2Character character, L2ZoneType zone)
{
if (!character.isPlayer())
{
return null;
}
final L2PcInstance player = character.getActingPlayer();
final Instance world = player.getInstanceWorld();
if (world != null)
{
switch (zone.getId())
{
case ZONE_ROOM_2:
{
if (world.isStatus(1))
{
world.incStatus();
startQuestTimer("message2", 100, null, player);
startQuestTimer("message5", 2600, null, player);
startQuestTimer("message6", 5100, null, player);
startQuestTimer("spawn_wave1", 5100, null, player);
}
break;
}
case ZONE_ROOM_3:
{
if (!world.getParameters().getBoolean("openingPlayed", false))
{
world.setParameter("openingPlayed", true);
startQuestTimer("spawn_npc3", 29950, null, player);
playMovie(player, Movie.SC_AWAKENING_BOSS_OPENING);
}
break;
}
}
}
return super.onEnterZone(character, zone);
}
public static void main(String[] args)
{
new HarnakUndergroundRuins();
}
}

View File

@@ -0,0 +1,70 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.JiniaGuildHideout1;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import instances.AbstractInstance;
import quests.Q10284_AcquisitionOfDivineSword.Q10284_AcquisitionOfDivineSword;
/**
* Jinia Guild Hideout instance zone.
* @author Adry_85
*/
public final class JiniaGuildHideout1 extends AbstractInstance
{
// NPC
private static final int RAFFORTY = 32020;
// Misc
private static final int TEMPLATE_ID = 140;
public JiniaGuildHideout1()
{
super(TEMPLATE_ID);
addStartNpc(RAFFORTY);
addTalkId(RAFFORTY);
}
@Override
protected void onEnter(L2PcInstance player, Instance instance, boolean firstEnter)
{
super.onEnter(player, instance, firstEnter);
if (firstEnter)
{
final QuestState qs = player.getQuestState(Q10284_AcquisitionOfDivineSword.class.getSimpleName());
if (qs != null)
{
qs.setCond(2, true);
}
}
}
@Override
public String onTalk(L2Npc npc, L2PcInstance talker)
{
enterInstance(talker, npc, TEMPLATE_ID);
return super.onTalk(npc, talker);
}
public static void main(String[] args)
{
new JiniaGuildHideout1();
}
}

View File

@@ -0,0 +1,70 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.JiniaGuildHideout2;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import instances.AbstractInstance;
import quests.Q10285_MeetingSirra.Q10285_MeetingSirra;
/**
* Jinia Guild Hideout instance zone.
* @author Adry_85
*/
public final class JiniaGuildHideout2 extends AbstractInstance
{
// NPC
private static final int RAFFORTY = 32020;
// Misc
private static final int TEMPLATE_ID = 141;
public JiniaGuildHideout2()
{
super(TEMPLATE_ID);
addStartNpc(RAFFORTY);
addTalkId(RAFFORTY);
}
@Override
protected void onEnter(L2PcInstance player, Instance instance, boolean firstEnter)
{
super.onEnter(player, instance, firstEnter);
if (firstEnter)
{
final QuestState qs = player.getQuestState(Q10285_MeetingSirra.class.getSimpleName());
if (qs != null)
{
qs.setCond(2, true);
}
}
}
@Override
public String onTalk(L2Npc npc, L2PcInstance talker)
{
enterInstance(talker, npc, TEMPLATE_ID);
return super.onTalk(npc, talker);
}
public static void main(String[] args)
{
new JiniaGuildHideout2();
}
}

View File

@@ -0,0 +1,70 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package instances.JiniaGuildHideout3;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.quest.QuestState;
import instances.AbstractInstance;
import quests.Q10286_ReunionWithSirra.Q10286_ReunionWithSirra;
/**
* Jinia Guild Hideout instance zone.
* @author Adry_85
*/
public final class JiniaGuildHideout3 extends AbstractInstance
{
// NPC
private static final int RAFFORTY = 32020;
// Misc
private static final int TEMPLATE_ID = 145;
public JiniaGuildHideout3()
{
super(TEMPLATE_ID);
addStartNpc(RAFFORTY);
addTalkId(RAFFORTY);
}
@Override
protected void onEnter(L2PcInstance player, Instance instance, boolean firstEnter)
{
super.onEnter(player, instance, firstEnter);
if (firstEnter)
{
final QuestState qs = player.getQuestState(Q10286_ReunionWithSirra.class.getSimpleName());
if (qs != null)
{
qs.setCond(2, true);
}
}
}
@Override
public String onTalk(L2Npc npc, L2PcInstance talker)
{
enterInstance(talker, npc, TEMPLATE_ID);
return super.onTalk(npc, talker);
}
public static void main(String[] args)
{
new JiniaGuildHideout3();
}
}

Some files were not shown because too many files have changed in this diff Show More