diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/Routes.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/Routes.xml
index 21d278f283..ad4baaf622 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/Routes.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/Routes.xml
@@ -2476,6 +2476,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsCoralGarden.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsCoralGarden.xml
new file mode 100644
index 0000000000..00ecfbdb7d
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsCoralGarden.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
new file mode 100644
index 0000000000..57900c0c1e
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
@@ -0,0 +1,2511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsSteamCorridor.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
new file mode 100644
index 0000000000..2f12da9071
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
@@ -0,0 +1,694 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
new file mode 100644
index 0000000000..35880d0254
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
@@ -0,0 +1,215 @@
+/*
+ * 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 .
+ */
+package ai.areas.Parnassus.EntrancePortalToCrystalCaverns;
+
+import java.util.Calendar;
+
+import org.l2jmobius.gameserver.instancemanager.QuestManager;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.quest.Quest;
+import org.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+
+import ai.AbstractNpcAI;
+import instances.CrystalCaverns.CrystalCavernsCoralGarden;
+import instances.CrystalCaverns.CrystalCavernsEmeraldSquare;
+import instances.CrystalCaverns.CrystalCavernsSteamCorridor;
+
+/**
+ * Entrance Portal to Crystal Caverns AI.
+ * @author St3eT
+ */
+public class EntrancePortalToCrystalCaverns extends AbstractNpcAI
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ // Misc
+ private static final int EMERALD_SQUARE_TEMPLATE_ID = 163;
+ private static final int STEAM_CORRIDOR_TEMPLATE_ID = 164;
+ private static final int CORAL_GARDEN_TEMPLATE_ID = 165;
+ private static final int PRISON_ENTRACE_TRIGGER_1 = 24230010;
+ private static final int PRISON_ENTRACE_TRIGGER_2 = 24230012;
+ private static final int CAVERNS_ENTRACE_TRIGGER_1 = 24230014;
+ private static final int CAVERNS_ENTRACE_TRIGGER_2 = 24230016;
+ private static final int CAVERNS_ENTRACE_TRIGGER_3 = 24230018;
+
+ private EntrancePortalToCrystalCaverns()
+ {
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(CAVERNS_ENTRACE);
+ addSpawnId(CAVERNS_ENTRACE);
+ addSeeCreatureId(CAVERNS_ENTRACE);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ Quest instanceScript = null;
+
+ switch (getCurrentInstanceTemplateId())
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsEmeraldSquare.class.getSimpleName());
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsSteamCorridor.class.getSimpleName());
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsCoralGarden.class.getSimpleName());
+ break;
+ }
+ }
+
+ if (instanceScript != null)
+ {
+ instanceScript.notifyEvent(event, npc, player);
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ return "EntrancePortal_" + getCurrentInstanceTemplateId() + ".html";
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ getTimers().addRepeatingTimer("LOOP_TIMER", 10000, npc, null);
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("LOOP_TIMER"))
+ {
+ final int currentTemplateId = getCurrentInstanceTemplateId();
+
+ World.getInstance().forEachVisibleObjectInRange(npc, PlayerInstance.class, 500, p ->
+ {
+ updateTriggersForPlayer(player, currentTemplateId);
+ });
+ }
+ }
+
+ @Override
+ public String onSeeCreature(Npc npc, Creature creature, boolean isSummon)
+ {
+ if (creature.isPlayer())
+ {
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_1, true));
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_2, true));
+ updateTriggersForPlayer(creature.getActingPlayer(), getCurrentInstanceTemplateId());
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ public void updateTriggersForPlayer(PlayerInstance player, int currentTemplateId)
+ {
+ if (player != null)
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, false));
+
+ switch (currentTemplateId)
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, true));
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, true));
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, true));
+ break;
+ }
+ }
+ }
+ }
+
+ public int getCurrentInstanceTemplateId()
+ {
+ final int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
+ final int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ int templateId = -1;
+
+ switch (day)
+ {
+ case Calendar.MONDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.TUESDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.WEDNESDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.THURSDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.FRIDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SATURDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SUNDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ }
+ return templateId;
+ }
+
+ public static void main(String[] args)
+ {
+ new EntrancePortalToCrystalCaverns();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
new file mode 100644
index 0000000000..6de9f6e4b9
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Emerald Square.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
new file mode 100644
index 0000000000..b0d8310c68
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Steam Corridor.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
new file mode 100644
index 0000000000..ea7efec129
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Coral Garden.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
new file mode 100644
index 0000000000..21d8f0d1b3
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
@@ -0,0 +1,253 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.instancemanager.WalkingManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.Spawn;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Coral Garden instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsCoralGarden extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int MICHAELA_NORMAL = 25799;
+ private static final int MICHAELA_WISE = 26116;
+ private static final int MICHAELA_WEALTHY = 26115;
+ private static final int MICHAELA_ARMED = 26114;
+ private static final int GOLEM_1 = 19013; // Crystalline Golem
+ private static final int GOLEM_2 = 19014; // Crystalline Golem
+ // Location
+ private static final Location BOSS_LOC = new Location(144307, 220032, -11824);
+ // Misc
+ private static final int TEMPLATE_ID = 165;
+ private static final int BOSS_DOOR_ID = 24240026;
+ private static final int PLAYER_MAX_DISTANCE = 250;
+
+ public CrystalCavernsCoralGarden()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(GOLEM_1, GOLEM_2);
+ addKillId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addAttackId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addRouteFinishedId(GOLEM_1, GOLEM_2);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "SUCCESS_TIMER":
+ {
+ showOnScreenMsg(instance, NpcStringId.GOLEM_LOCATION_SUCCESSFUL_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ break;
+ }
+ case "LOOP_TIMER":
+ {
+ player = npcVars.getObject("PLAYER_OBJECT", PlayerInstance.class);
+
+ if ((player != null) && (npc.calculateDistance3D(player) > PLAYER_MAX_DISTANCE) && npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ addMoveToDesire(npc, new Location(npc.getX() + getRandom(-100, 100), npc.getY() + getRandom(-150, 150), npc.getZ()), 23);
+ npc.setRunning();
+ npcVars.set("NPC_FOLLOWING", false);
+ getTimers().cancelTimer("LOOP_TIMER", npc, null);
+ getTimers().addTimer("FAIL_TIMER", 5000, npc, null);
+ }
+ break;
+ }
+ case "FAIL_TIMER":
+ {
+ final Spawn spawn = npc.getSpawn();
+
+ if (!npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ npc.setWalking();
+ npc.teleToLocation(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ());
+ npc.setScriptValue(0);
+ npc.setNameString(null);
+ npc.setTitleString(null);
+ npc.setTitle(null);
+ npc.broadcastInfo();
+ }
+ npcVars.set("CAN_CALL_MONSTERS", ((spawn.getX() - ((npc.getX() * spawn.getX()) - npc.getX())) + (spawn.getY() - (npc.getY() * spawn.getY()) - npc.getY())) > (200 * 200));
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.getVariables().set("PLAYER_OBJECT", player);
+ npc.setNameString(NpcStringId.TRAITOR_CRYSTALLINE_GOLEM);
+ npc.setTitleString(NpcStringId.GIVEN_TO_S1);
+ npc.setTitle(player.getName());
+ npc.broadcastInfo();
+ WalkingManager.getInstance().startMoving(npc, npc.getId() == GOLEM_1 ? "gd_golem_1" : "gd_golem_2");
+ getTimers().addRepeatingTimer("LOOP_TIMER", 500, npc, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onRouteFinished(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (instance != null)
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ showOnScreenMsg(instance, NpcStringId.GOLEM_ENTERED_THE_REQUIRED_ZONE, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ npc.deleteMe();
+
+ if (instance.getAliveNpcs(GOLEM_1, GOLEM_2).isEmpty())
+ {
+ instance.openCloseDoor(BOSS_DOOR_ID, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = MICHAELA_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = MICHAELA_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = MICHAELA_WEALTHY;
+ }
+ else
+ {
+ bossId = MICHAELA_ARMED;
+ }
+
+ final Npc boss = addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ getTimers().addTimer("SUCCESS_TIMER", 5000, boss, null);
+ }
+ }
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ instance.openCloseDoor(BOSS_DOOR_ID, false);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsCoralGarden();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
new file mode 100644
index 0000000000..26bb4ad5c8
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
@@ -0,0 +1,424 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.MonsterInstance;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.skills.Skill;
+import org.l2jmobius.gameserver.model.stats.Stats;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Emerald Square instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsEmeraldSquare extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int VERIDAN_NORMAL = 25796;
+ private static final int VERIDAN_WISE = 26107;
+ private static final int VERIDAN_WEALTHY = 26106;
+ private static final int VERIDAN_ARMED = 26105;
+ private static final int WATER_CANNON = 19008;
+ private static final int WATER_CANNON_SKILL = 19009;
+ private static final int STRONGHOLD_PROTECTOR = 23012;
+ private static final int SQUARE_INTRUDER = 23010;
+ private static final int SQUARE_ATTACKER = 23011;
+ // Skills
+ private static final SkillHolder DESTROY_SKILL = new SkillHolder(12003, 1);
+ private static final SkillHolder WATER_CANNON_SKILL_ATTACK = new SkillHolder(14179, 1);
+ // Locations
+ private static final Location[] BOSS_SPAWNS =
+ {
+ new Location(152745, 145957, -12584, 16446),
+ new Location(152816, 145968, -12633, 16446),
+ };
+ // Misc
+ private static final int TEMPLATE_ID = 163;
+ private static final int RAID_DOOR_1 = 24220005;
+ private static final int RAID_DOOR_2 = 24220006;
+
+ public CrystalCavernsEmeraldSquare()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addSpawnId(WATER_CANNON);
+ addKillId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addAttackId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addSpellFinishedId(WATER_CANNON_SKILL);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, WATER_CANNON);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final StatsSet npcVars = npc.getVariables();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (event)
+ {
+ case "HP_REGEN_TIMER":
+ {
+ int value = ((baseId == 5) || (baseId == 6)) ? 5 : baseId;
+ npc.getStat().addFixedValue(Stats.REGENERATE_HP_RATE, Double.valueOf(value * 1000));
+ break;
+ }
+ case "SUPPORT_SPAWN_TIMER":
+ {
+ int supportVal = npcVars.getInt("SUPPORT_VALUE", 0);
+
+ if (supportVal > 3)
+ {
+ return;
+ }
+
+ if ((supportVal == 0) || (supportVal == 1) || (supportVal == 2))
+ {
+ final String spawnName = npcParams.getString("SupportMaker" + (supportVal + 1), null);
+ if (spawnName != null)
+ {
+ instance.spawnGroup(spawnName);
+ }
+ npcVars.increaseInt("SUPPORT_VALUE", 1);
+ }
+
+ if (!npcVars.getBoolean("PREVIOUS_BASE_DESTROYED", false))
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", 60000, npc, null);
+ }
+ break;
+ }
+ case "CANNON_LOOP_ATTACK":
+ {
+ if (npc.getCurrentHpPercent() > 30)
+ {
+ if (npcVars.getBoolean("IS_DESTROY_ACTIVATED", false) || (getRandom(10) < 2))
+ {
+ final Npc cannonSkill = addSpawn(WATER_CANNON_SKILL, npc, true, 0, false, instance.getId());
+ addSkillCastDesire(cannonSkill, cannonSkill, WATER_CANNON_SKILL_ATTACK, 23);
+ }
+ }
+ break;
+ }
+ case "SUICIDE_TIMER":
+ {
+ npc.doDie(null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (npc.getId())
+ {
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (!npcVars.getBoolean("CLOSED_DOORS", false))
+ {
+ npcVars.set("CLOSED_DOORS", true);
+ instance.openCloseDoor(RAID_DOOR_2, false);
+ }
+ }
+ case WATER_CANNON:
+ {
+ if ((skill != null) && (skill.getId() == DESTROY_SKILL.getSkillId()) && !npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npcVars.set("IS_DESTROY_ACTIVATED", true);
+ npc.setDisplayEffect(2);
+ getTimers().addTimer("SUICIDE_TIMER", 60000, npc, null);
+ }
+
+ if (npc.getCurrentHpPercent() < 30)
+ {
+ if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(3);
+
+ }
+ }
+ else if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (npc.getId())
+ {
+ case VERIDAN_NORMAL:
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (instance.getAliveNpcs(VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED).isEmpty())
+ {
+ instance.finishInstance();
+ }
+ else
+ {
+ instance.setReenterTime();
+ }
+ break;
+ }
+ case WATER_CANNON:
+ {
+ npc.setDisplayEffect(4);
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_S1, ExShowScreenMessage.MIDDLE_CENTER, 4000, String.valueOf(npc.getParameters().getInt("base_id", -1)));
+
+ World.getInstance().forEachVisibleObjectInRange(npc, MonsterInstance.class, 400, monster ->
+ {
+ if ((monster.getId() == STRONGHOLD_PROTECTOR) || (monster.getId() == SQUARE_INTRUDER) || (monster.getId() == SQUARE_ATTACKER))
+ {
+ monster.doDie(null);
+ }
+ });
+
+ instance.getAliveNpcs(WATER_CANNON).forEach(cannon ->
+ {
+ final int cannonBaseId = cannon.getParameters().getInt("base_id", -1);
+
+ switch (baseId)
+ {
+ case 1:
+ {
+ if (cannonBaseId == 2)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (cannonBaseId == 3)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 3:
+ {
+ if (cannonBaseId == 4)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 4:
+ {
+ if ((cannonBaseId == 5) || (cannonBaseId == 6))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 5:
+ case 6:
+ {
+ if (cannonBaseId == 7)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 7:
+ {
+ if ((cannonBaseId == 8) || (cannonBaseId == 9))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ }
+ });
+
+ if ((baseId == 8) || (baseId == 9))
+ {
+ instance.getParameters().increaseInt("MAIN_TARGETS_KILLED", 0, 1);
+
+ if (instance.getParameters().getInt("MAIN_TARGETS_KILLED", 0) == 2)
+ {
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.openCloseDoor(RAID_DOOR_1, true);
+ instance.openCloseDoor(RAID_DOOR_2, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = VERIDAN_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = VERIDAN_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = VERIDAN_WEALTHY;
+ }
+ else
+ {
+ bossId = VERIDAN_ARMED;
+ }
+
+ for (Location loc : BOSS_SPAWNS)
+ {
+ addSpawn(bossId, loc, false, 0, false, instance.getId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onSpellFinished(Npc npc, PlayerInstance player, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance) && (npc.getId() == WATER_CANNON_SKILL) && (skill.getId() == WATER_CANNON_SKILL_ATTACK.getSkillId()))
+ {
+ npc.deleteMe();
+ }
+ return super.onSpellFinished(npc, player, skill);
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case WATER_CANNON:
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ if (baseId != 1)
+ {
+ npc.setTargetable(false);
+ }
+
+ getTimers().addTimer("HP_REGEN_TIMER", 10000, npc, null);
+
+ if (baseId > 0)
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", (baseId * 60) * 1000, npc, null);
+ }
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ public void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance world = npc.getInstanceWorld();
+
+ if ((world != null) && creature.isPlayer() && npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.setDisplayEffect(1);
+ getTimers().addRepeatingTimer("CANNON_LOOP_ATTACK", 1000, npc, null);
+ }
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsEmeraldSquare();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
new file mode 100644
index 0000000000..23f045e3fd
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
@@ -0,0 +1,408 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.enums.ChatType;
+import org.l2jmobius.gameserver.instancemanager.ZoneManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.zone.ZoneType;
+import org.l2jmobius.gameserver.model.zone.type.TeleportZone;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Steam Corridor instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsSteamCorridor extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int KECHI_NORMAL = 25797;
+ private static final int KECHI_WISE = 26113;
+ private static final int KECHI_WEALTHY = 26112;
+ private static final int KECHI_ARMED = 26111;
+ private static final int VICIOUS_DUELER = 23014;
+ private static final int VICIOUS_WARRIOR = 23016;
+ private static final int VICIOUS_SWORDSMAN = 23015;
+ private static final int SPIRIT_PROTECTOR = 23013;
+ private static final int FIRE_REGION = 19161;
+ private static final int PLAYER_DETECTOR = 19075;
+ private static final int TRAP_1 = 19011;
+ private static final int TRAP_2 = 19012;
+ // Skills
+ private static final SkillHolder FIRE_SKILL_1 = new SkillHolder(14373, 1);
+ private static final SkillHolder FIRE_SKILL_2 = new SkillHolder(14373, 2);
+ private static final SkillHolder FIRE_SKILL_3 = new SkillHolder(14197, 1);
+ private static final SkillHolder TRAP_SKILL_1 = new SkillHolder(14180, 1);
+ private static final SkillHolder TRAP_SKILL_2 = new SkillHolder(14181, 1);
+ private static final SkillHolder TRAP_SKILL_3 = new SkillHolder(14372, 1);
+ // Location
+ private static final Location BOSS_LOC = new Location(154078, 215125, -12140);
+ // Misc
+ private static final int TEMPLATE_ID = 164;
+
+ public CrystalCavernsSteamCorridor()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addAttackId(TRAP_1, TRAP_2);
+ addKillId(VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, KECHI_NORMAL, KECHI_WISE, KECHI_WEALTHY, KECHI_ARMED);
+ addSpawnId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, FIRE_REGION, PLAYER_DETECTOR);
+ addEventReceivedId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN);
+ addInstanceCreatedId(TEMPLATE_ID);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, PLAYER_DETECTOR);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "FIRE_REGION_TIMER_1":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_1, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_2", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_2":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_2, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_3":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_3, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 1000, npc, null);
+ break;
+ }
+ case "TRAP_REACT_TIMER":
+ {
+ final int timer = npcVars.increaseInt("TIMER_VAL", -1);
+ if (timer > 0)
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, " " + timer);
+ }
+ else
+ {
+ if (npc.getId() == TRAP_1)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_1 : TRAP_SKILL_3), 23);
+ }
+ else if (npc.getId() == TRAP_2)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_2 : TRAP_SKILL_3), 23);
+ }
+ }
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceCreated(Instance instance, PlayerInstance player)
+ {
+ instance.setStatus(1);
+ for (int i = 0; i < 6; i++)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName("24_24_fire_telezone_0" + i, TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(false, instance.getId());
+ }
+ }
+ super.onInstanceCreated(instance, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case SPIRIT_PROTECTOR:
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ npc.setTargetable(false);
+ npc.disableCoreAI(true);
+ npc.setInvisible(true);
+ break;
+ }
+ case FIRE_REGION:
+ {
+ final int timeLimit = npcParams.getInt("Limit_Time", 0);
+ if (timeLimit > 0)
+ {
+ getTimers().addTimer("FIRE_REGION_TIMER_1", ((timeLimit * 30) * 100), npc, null);
+ }
+ npc.setTargetable(false);
+ npc.setIsInvul(true);
+ npc.setRandomAnimation(false);
+ npc.setRandomWalking(false);
+ npc.disableCoreAI(true);
+ break;
+ }
+ case PLAYER_DETECTOR:
+ {
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onEventReceived(String eventName, Npc sender, Npc receiver, WorldObject reference)
+ {
+ final Instance instance = receiver.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = receiver.getParameters();
+
+ if (eventName.equals(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0))))
+ {
+ receiver.setTargetable(true);
+ receiver.disableCoreAI(false);
+ receiver.setInvisible(false);
+ }
+ }
+ return super.onEventReceived(eventName, sender, receiver, reference);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case TRAP_1:
+ case TRAP_2:
+ {
+ if (npc.isScriptValue(0))
+ {
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ npc.setScriptValue(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int killTarget = instance.getParameters().getInt("KILL_TARGET", 5);
+ int currentKillCount = instance.getParameters().getInt("KILL_COUNT", 0);
+
+ switch (npc.getId())
+ {
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ if (npcParams.getInt("last_checker", 0) == 1)
+ {
+ currentKillCount = instance.getParameters().increaseInt("KILL_COUNT", 0, 1);
+
+ if (currentKillCount >= killTarget)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName(npc.getParameters().getString("AreaTeleName"), TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(true, instance.getId());
+ showOnScreenMsg(instance, NpcStringId.THE_PORTAL_TO_THE_NEXT_ROOM_IS_NOW_OPEN, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.spawnGroup("innadril23_mb2422_pt" + instance.getStatus() + "m1");
+ instance.getParameters().set("KILL_COUNT", 0);
+
+ switch (instance.getStatus())
+ {
+ case 1:
+ {
+ instance.getParameters().set("KILL_TARGET", 12);
+ instance.setStatus(2);
+ break;
+ }
+ case 2:
+ {
+ instance.getParameters().set("KILL_TARGET", 3);
+ instance.setStatus(3);
+ break;
+ }
+ case 3:
+ {
+ instance.getParameters().set("KILL_TARGET", 18);
+ instance.setStatus(4);
+ break;
+ }
+ case 4:
+ {
+ instance.getParameters().set("KILL_TARGET", 5);
+ instance.setStatus(5);
+ break;
+ }
+ case 5:
+ {
+ instance.getParameters().set("KILL_TARGET", 20);
+ instance.setStatus(6);
+ break;
+ }
+ case 6:
+ {
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = KECHI_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = KECHI_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = KECHI_WEALTHY;
+ }
+ else
+ {
+ bossId = KECHI_ARMED;
+ }
+
+ addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOGGER.warning("Cannot find teleport zone for Crystal Cavern: Steam Corridor instance!!!");
+ }
+ }
+ }
+ break;
+ }
+ case KECHI_NORMAL:
+ case KECHI_WISE:
+ case KECHI_WEALTHY:
+ case KECHI_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ private void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance instance = npc.getInstanceWorld();
+
+ if (isInInstance(instance) && creature.isPlayer())
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case PLAYER_DETECTOR:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.broadcastEvent(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0)), 2000, null);
+
+ for (int i = 0; i < getRandom(5); i++)
+ {
+ final Npc trap = addSpawn(((npcParams.getInt("MobType", 0) == 0) ? TRAP_1 : TRAP_2), npc, true, 0, false, instance.getId());
+ trap.getVariables().set("TIMER_VAL", 4);
+ }
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsSteamCorridor();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
new file mode 100644
index 0000000000..efc886f117
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+You must be in a party to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
new file mode 100644
index 0000000000..dfa8583852
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+The leader of the party should try to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/19000-19099.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/19000-19099.xml
index 84e0a9ee1e..925f033325 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/19000-19099.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/19000-19099.xml
@@ -366,7 +366,7 @@
-
+
@@ -403,7 +403,7 @@
-
+
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/zones/teleportzones.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/zones/teleportzones.xml
index d68c0bd43b..ea67b08743 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/zones/teleportzones.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/zones/teleportzones.xml
@@ -154,6 +154,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/Routes.xml b/L2J_Mobius_2.5_Underground/dist/game/data/Routes.xml
index 21d278f283..ad4baaf622 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/Routes.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/Routes.xml
@@ -2476,6 +2476,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsCoralGarden.xml b/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsCoralGarden.xml
new file mode 100644
index 0000000000..00ecfbdb7d
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsCoralGarden.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml b/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
new file mode 100644
index 0000000000..57900c0c1e
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
@@ -0,0 +1,2511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsSteamCorridor.xml b/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
new file mode 100644
index 0000000000..2f12da9071
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
@@ -0,0 +1,694 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
new file mode 100644
index 0000000000..35880d0254
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
@@ -0,0 +1,215 @@
+/*
+ * 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 .
+ */
+package ai.areas.Parnassus.EntrancePortalToCrystalCaverns;
+
+import java.util.Calendar;
+
+import org.l2jmobius.gameserver.instancemanager.QuestManager;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.quest.Quest;
+import org.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+
+import ai.AbstractNpcAI;
+import instances.CrystalCaverns.CrystalCavernsCoralGarden;
+import instances.CrystalCaverns.CrystalCavernsEmeraldSquare;
+import instances.CrystalCaverns.CrystalCavernsSteamCorridor;
+
+/**
+ * Entrance Portal to Crystal Caverns AI.
+ * @author St3eT
+ */
+public class EntrancePortalToCrystalCaverns extends AbstractNpcAI
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ // Misc
+ private static final int EMERALD_SQUARE_TEMPLATE_ID = 163;
+ private static final int STEAM_CORRIDOR_TEMPLATE_ID = 164;
+ private static final int CORAL_GARDEN_TEMPLATE_ID = 165;
+ private static final int PRISON_ENTRACE_TRIGGER_1 = 24230010;
+ private static final int PRISON_ENTRACE_TRIGGER_2 = 24230012;
+ private static final int CAVERNS_ENTRACE_TRIGGER_1 = 24230014;
+ private static final int CAVERNS_ENTRACE_TRIGGER_2 = 24230016;
+ private static final int CAVERNS_ENTRACE_TRIGGER_3 = 24230018;
+
+ private EntrancePortalToCrystalCaverns()
+ {
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(CAVERNS_ENTRACE);
+ addSpawnId(CAVERNS_ENTRACE);
+ addSeeCreatureId(CAVERNS_ENTRACE);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ Quest instanceScript = null;
+
+ switch (getCurrentInstanceTemplateId())
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsEmeraldSquare.class.getSimpleName());
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsSteamCorridor.class.getSimpleName());
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsCoralGarden.class.getSimpleName());
+ break;
+ }
+ }
+
+ if (instanceScript != null)
+ {
+ instanceScript.notifyEvent(event, npc, player);
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ return "EntrancePortal_" + getCurrentInstanceTemplateId() + ".html";
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ getTimers().addRepeatingTimer("LOOP_TIMER", 10000, npc, null);
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("LOOP_TIMER"))
+ {
+ final int currentTemplateId = getCurrentInstanceTemplateId();
+
+ World.getInstance().forEachVisibleObjectInRange(npc, PlayerInstance.class, 500, p ->
+ {
+ updateTriggersForPlayer(player, currentTemplateId);
+ });
+ }
+ }
+
+ @Override
+ public String onSeeCreature(Npc npc, Creature creature, boolean isSummon)
+ {
+ if (creature.isPlayer())
+ {
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_1, true));
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_2, true));
+ updateTriggersForPlayer(creature.getActingPlayer(), getCurrentInstanceTemplateId());
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ public void updateTriggersForPlayer(PlayerInstance player, int currentTemplateId)
+ {
+ if (player != null)
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, false));
+
+ switch (currentTemplateId)
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, true));
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, true));
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, true));
+ break;
+ }
+ }
+ }
+ }
+
+ public int getCurrentInstanceTemplateId()
+ {
+ final int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
+ final int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ int templateId = -1;
+
+ switch (day)
+ {
+ case Calendar.MONDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.TUESDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.WEDNESDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.THURSDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.FRIDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SATURDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SUNDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ }
+ return templateId;
+ }
+
+ public static void main(String[] args)
+ {
+ new EntrancePortalToCrystalCaverns();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
new file mode 100644
index 0000000000..6de9f6e4b9
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Emerald Square.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
new file mode 100644
index 0000000000..b0d8310c68
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Steam Corridor.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
new file mode 100644
index 0000000000..ea7efec129
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Coral Garden.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
new file mode 100644
index 0000000000..21d8f0d1b3
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
@@ -0,0 +1,253 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.instancemanager.WalkingManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.Spawn;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Coral Garden instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsCoralGarden extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int MICHAELA_NORMAL = 25799;
+ private static final int MICHAELA_WISE = 26116;
+ private static final int MICHAELA_WEALTHY = 26115;
+ private static final int MICHAELA_ARMED = 26114;
+ private static final int GOLEM_1 = 19013; // Crystalline Golem
+ private static final int GOLEM_2 = 19014; // Crystalline Golem
+ // Location
+ private static final Location BOSS_LOC = new Location(144307, 220032, -11824);
+ // Misc
+ private static final int TEMPLATE_ID = 165;
+ private static final int BOSS_DOOR_ID = 24240026;
+ private static final int PLAYER_MAX_DISTANCE = 250;
+
+ public CrystalCavernsCoralGarden()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(GOLEM_1, GOLEM_2);
+ addKillId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addAttackId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addRouteFinishedId(GOLEM_1, GOLEM_2);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "SUCCESS_TIMER":
+ {
+ showOnScreenMsg(instance, NpcStringId.GOLEM_LOCATION_SUCCESSFUL_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ break;
+ }
+ case "LOOP_TIMER":
+ {
+ player = npcVars.getObject("PLAYER_OBJECT", PlayerInstance.class);
+
+ if ((player != null) && (npc.calculateDistance3D(player) > PLAYER_MAX_DISTANCE) && npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ addMoveToDesire(npc, new Location(npc.getX() + getRandom(-100, 100), npc.getY() + getRandom(-150, 150), npc.getZ()), 23);
+ npc.setRunning();
+ npcVars.set("NPC_FOLLOWING", false);
+ getTimers().cancelTimer("LOOP_TIMER", npc, null);
+ getTimers().addTimer("FAIL_TIMER", 5000, npc, null);
+ }
+ break;
+ }
+ case "FAIL_TIMER":
+ {
+ final Spawn spawn = npc.getSpawn();
+
+ if (!npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ npc.setWalking();
+ npc.teleToLocation(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ());
+ npc.setScriptValue(0);
+ npc.setNameString(null);
+ npc.setTitleString(null);
+ npc.setTitle(null);
+ npc.broadcastInfo();
+ }
+ npcVars.set("CAN_CALL_MONSTERS", ((spawn.getX() - ((npc.getX() * spawn.getX()) - npc.getX())) + (spawn.getY() - (npc.getY() * spawn.getY()) - npc.getY())) > (200 * 200));
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.getVariables().set("PLAYER_OBJECT", player);
+ npc.setNameString(NpcStringId.TRAITOR_CRYSTALLINE_GOLEM);
+ npc.setTitleString(NpcStringId.GIVEN_TO_S1);
+ npc.setTitle(player.getName());
+ npc.broadcastInfo();
+ WalkingManager.getInstance().startMoving(npc, npc.getId() == GOLEM_1 ? "gd_golem_1" : "gd_golem_2");
+ getTimers().addRepeatingTimer("LOOP_TIMER", 500, npc, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onRouteFinished(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (instance != null)
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ showOnScreenMsg(instance, NpcStringId.GOLEM_ENTERED_THE_REQUIRED_ZONE, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ npc.deleteMe();
+
+ if (instance.getAliveNpcs(GOLEM_1, GOLEM_2).isEmpty())
+ {
+ instance.openCloseDoor(BOSS_DOOR_ID, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = MICHAELA_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = MICHAELA_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = MICHAELA_WEALTHY;
+ }
+ else
+ {
+ bossId = MICHAELA_ARMED;
+ }
+
+ final Npc boss = addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ getTimers().addTimer("SUCCESS_TIMER", 5000, boss, null);
+ }
+ }
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ instance.openCloseDoor(BOSS_DOOR_ID, false);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsCoralGarden();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
new file mode 100644
index 0000000000..26bb4ad5c8
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
@@ -0,0 +1,424 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.MonsterInstance;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.skills.Skill;
+import org.l2jmobius.gameserver.model.stats.Stats;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Emerald Square instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsEmeraldSquare extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int VERIDAN_NORMAL = 25796;
+ private static final int VERIDAN_WISE = 26107;
+ private static final int VERIDAN_WEALTHY = 26106;
+ private static final int VERIDAN_ARMED = 26105;
+ private static final int WATER_CANNON = 19008;
+ private static final int WATER_CANNON_SKILL = 19009;
+ private static final int STRONGHOLD_PROTECTOR = 23012;
+ private static final int SQUARE_INTRUDER = 23010;
+ private static final int SQUARE_ATTACKER = 23011;
+ // Skills
+ private static final SkillHolder DESTROY_SKILL = new SkillHolder(12003, 1);
+ private static final SkillHolder WATER_CANNON_SKILL_ATTACK = new SkillHolder(14179, 1);
+ // Locations
+ private static final Location[] BOSS_SPAWNS =
+ {
+ new Location(152745, 145957, -12584, 16446),
+ new Location(152816, 145968, -12633, 16446),
+ };
+ // Misc
+ private static final int TEMPLATE_ID = 163;
+ private static final int RAID_DOOR_1 = 24220005;
+ private static final int RAID_DOOR_2 = 24220006;
+
+ public CrystalCavernsEmeraldSquare()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addSpawnId(WATER_CANNON);
+ addKillId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addAttackId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addSpellFinishedId(WATER_CANNON_SKILL);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, WATER_CANNON);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final StatsSet npcVars = npc.getVariables();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (event)
+ {
+ case "HP_REGEN_TIMER":
+ {
+ int value = ((baseId == 5) || (baseId == 6)) ? 5 : baseId;
+ npc.getStat().addFixedValue(Stats.REGENERATE_HP_RATE, Double.valueOf(value * 1000));
+ break;
+ }
+ case "SUPPORT_SPAWN_TIMER":
+ {
+ int supportVal = npcVars.getInt("SUPPORT_VALUE", 0);
+
+ if (supportVal > 3)
+ {
+ return;
+ }
+
+ if ((supportVal == 0) || (supportVal == 1) || (supportVal == 2))
+ {
+ final String spawnName = npcParams.getString("SupportMaker" + (supportVal + 1), null);
+ if (spawnName != null)
+ {
+ instance.spawnGroup(spawnName);
+ }
+ npcVars.increaseInt("SUPPORT_VALUE", 1);
+ }
+
+ if (!npcVars.getBoolean("PREVIOUS_BASE_DESTROYED", false))
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", 60000, npc, null);
+ }
+ break;
+ }
+ case "CANNON_LOOP_ATTACK":
+ {
+ if (npc.getCurrentHpPercent() > 30)
+ {
+ if (npcVars.getBoolean("IS_DESTROY_ACTIVATED", false) || (getRandom(10) < 2))
+ {
+ final Npc cannonSkill = addSpawn(WATER_CANNON_SKILL, npc, true, 0, false, instance.getId());
+ addSkillCastDesire(cannonSkill, cannonSkill, WATER_CANNON_SKILL_ATTACK, 23);
+ }
+ }
+ break;
+ }
+ case "SUICIDE_TIMER":
+ {
+ npc.doDie(null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (npc.getId())
+ {
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (!npcVars.getBoolean("CLOSED_DOORS", false))
+ {
+ npcVars.set("CLOSED_DOORS", true);
+ instance.openCloseDoor(RAID_DOOR_2, false);
+ }
+ }
+ case WATER_CANNON:
+ {
+ if ((skill != null) && (skill.getId() == DESTROY_SKILL.getSkillId()) && !npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npcVars.set("IS_DESTROY_ACTIVATED", true);
+ npc.setDisplayEffect(2);
+ getTimers().addTimer("SUICIDE_TIMER", 60000, npc, null);
+ }
+
+ if (npc.getCurrentHpPercent() < 30)
+ {
+ if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(3);
+
+ }
+ }
+ else if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (npc.getId())
+ {
+ case VERIDAN_NORMAL:
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (instance.getAliveNpcs(VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED).isEmpty())
+ {
+ instance.finishInstance();
+ }
+ else
+ {
+ instance.setReenterTime();
+ }
+ break;
+ }
+ case WATER_CANNON:
+ {
+ npc.setDisplayEffect(4);
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_S1, ExShowScreenMessage.MIDDLE_CENTER, 4000, String.valueOf(npc.getParameters().getInt("base_id", -1)));
+
+ World.getInstance().forEachVisibleObjectInRange(npc, MonsterInstance.class, 400, monster ->
+ {
+ if ((monster.getId() == STRONGHOLD_PROTECTOR) || (monster.getId() == SQUARE_INTRUDER) || (monster.getId() == SQUARE_ATTACKER))
+ {
+ monster.doDie(null);
+ }
+ });
+
+ instance.getAliveNpcs(WATER_CANNON).forEach(cannon ->
+ {
+ final int cannonBaseId = cannon.getParameters().getInt("base_id", -1);
+
+ switch (baseId)
+ {
+ case 1:
+ {
+ if (cannonBaseId == 2)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (cannonBaseId == 3)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 3:
+ {
+ if (cannonBaseId == 4)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 4:
+ {
+ if ((cannonBaseId == 5) || (cannonBaseId == 6))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 5:
+ case 6:
+ {
+ if (cannonBaseId == 7)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 7:
+ {
+ if ((cannonBaseId == 8) || (cannonBaseId == 9))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ }
+ });
+
+ if ((baseId == 8) || (baseId == 9))
+ {
+ instance.getParameters().increaseInt("MAIN_TARGETS_KILLED", 0, 1);
+
+ if (instance.getParameters().getInt("MAIN_TARGETS_KILLED", 0) == 2)
+ {
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.openCloseDoor(RAID_DOOR_1, true);
+ instance.openCloseDoor(RAID_DOOR_2, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = VERIDAN_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = VERIDAN_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = VERIDAN_WEALTHY;
+ }
+ else
+ {
+ bossId = VERIDAN_ARMED;
+ }
+
+ for (Location loc : BOSS_SPAWNS)
+ {
+ addSpawn(bossId, loc, false, 0, false, instance.getId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onSpellFinished(Npc npc, PlayerInstance player, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance) && (npc.getId() == WATER_CANNON_SKILL) && (skill.getId() == WATER_CANNON_SKILL_ATTACK.getSkillId()))
+ {
+ npc.deleteMe();
+ }
+ return super.onSpellFinished(npc, player, skill);
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case WATER_CANNON:
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ if (baseId != 1)
+ {
+ npc.setTargetable(false);
+ }
+
+ getTimers().addTimer("HP_REGEN_TIMER", 10000, npc, null);
+
+ if (baseId > 0)
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", (baseId * 60) * 1000, npc, null);
+ }
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ public void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance world = npc.getInstanceWorld();
+
+ if ((world != null) && creature.isPlayer() && npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.setDisplayEffect(1);
+ getTimers().addRepeatingTimer("CANNON_LOOP_ATTACK", 1000, npc, null);
+ }
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsEmeraldSquare();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
new file mode 100644
index 0000000000..23f045e3fd
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
@@ -0,0 +1,408 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.enums.ChatType;
+import org.l2jmobius.gameserver.instancemanager.ZoneManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.zone.ZoneType;
+import org.l2jmobius.gameserver.model.zone.type.TeleportZone;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Steam Corridor instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsSteamCorridor extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int KECHI_NORMAL = 25797;
+ private static final int KECHI_WISE = 26113;
+ private static final int KECHI_WEALTHY = 26112;
+ private static final int KECHI_ARMED = 26111;
+ private static final int VICIOUS_DUELER = 23014;
+ private static final int VICIOUS_WARRIOR = 23016;
+ private static final int VICIOUS_SWORDSMAN = 23015;
+ private static final int SPIRIT_PROTECTOR = 23013;
+ private static final int FIRE_REGION = 19161;
+ private static final int PLAYER_DETECTOR = 19075;
+ private static final int TRAP_1 = 19011;
+ private static final int TRAP_2 = 19012;
+ // Skills
+ private static final SkillHolder FIRE_SKILL_1 = new SkillHolder(14373, 1);
+ private static final SkillHolder FIRE_SKILL_2 = new SkillHolder(14373, 2);
+ private static final SkillHolder FIRE_SKILL_3 = new SkillHolder(14197, 1);
+ private static final SkillHolder TRAP_SKILL_1 = new SkillHolder(14180, 1);
+ private static final SkillHolder TRAP_SKILL_2 = new SkillHolder(14181, 1);
+ private static final SkillHolder TRAP_SKILL_3 = new SkillHolder(14372, 1);
+ // Location
+ private static final Location BOSS_LOC = new Location(154078, 215125, -12140);
+ // Misc
+ private static final int TEMPLATE_ID = 164;
+
+ public CrystalCavernsSteamCorridor()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addAttackId(TRAP_1, TRAP_2);
+ addKillId(VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, KECHI_NORMAL, KECHI_WISE, KECHI_WEALTHY, KECHI_ARMED);
+ addSpawnId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, FIRE_REGION, PLAYER_DETECTOR);
+ addEventReceivedId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN);
+ addInstanceCreatedId(TEMPLATE_ID);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, PLAYER_DETECTOR);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "FIRE_REGION_TIMER_1":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_1, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_2", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_2":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_2, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_3":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_3, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 1000, npc, null);
+ break;
+ }
+ case "TRAP_REACT_TIMER":
+ {
+ final int timer = npcVars.increaseInt("TIMER_VAL", -1);
+ if (timer > 0)
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, " " + timer);
+ }
+ else
+ {
+ if (npc.getId() == TRAP_1)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_1 : TRAP_SKILL_3), 23);
+ }
+ else if (npc.getId() == TRAP_2)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_2 : TRAP_SKILL_3), 23);
+ }
+ }
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceCreated(Instance instance, PlayerInstance player)
+ {
+ instance.setStatus(1);
+ for (int i = 0; i < 6; i++)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName("24_24_fire_telezone_0" + i, TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(false, instance.getId());
+ }
+ }
+ super.onInstanceCreated(instance, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case SPIRIT_PROTECTOR:
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ npc.setTargetable(false);
+ npc.disableCoreAI(true);
+ npc.setInvisible(true);
+ break;
+ }
+ case FIRE_REGION:
+ {
+ final int timeLimit = npcParams.getInt("Limit_Time", 0);
+ if (timeLimit > 0)
+ {
+ getTimers().addTimer("FIRE_REGION_TIMER_1", ((timeLimit * 30) * 100), npc, null);
+ }
+ npc.setTargetable(false);
+ npc.setIsInvul(true);
+ npc.setRandomAnimation(false);
+ npc.setRandomWalking(false);
+ npc.disableCoreAI(true);
+ break;
+ }
+ case PLAYER_DETECTOR:
+ {
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onEventReceived(String eventName, Npc sender, Npc receiver, WorldObject reference)
+ {
+ final Instance instance = receiver.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = receiver.getParameters();
+
+ if (eventName.equals(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0))))
+ {
+ receiver.setTargetable(true);
+ receiver.disableCoreAI(false);
+ receiver.setInvisible(false);
+ }
+ }
+ return super.onEventReceived(eventName, sender, receiver, reference);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case TRAP_1:
+ case TRAP_2:
+ {
+ if (npc.isScriptValue(0))
+ {
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ npc.setScriptValue(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int killTarget = instance.getParameters().getInt("KILL_TARGET", 5);
+ int currentKillCount = instance.getParameters().getInt("KILL_COUNT", 0);
+
+ switch (npc.getId())
+ {
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ if (npcParams.getInt("last_checker", 0) == 1)
+ {
+ currentKillCount = instance.getParameters().increaseInt("KILL_COUNT", 0, 1);
+
+ if (currentKillCount >= killTarget)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName(npc.getParameters().getString("AreaTeleName"), TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(true, instance.getId());
+ showOnScreenMsg(instance, NpcStringId.THE_PORTAL_TO_THE_NEXT_ROOM_IS_NOW_OPEN, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.spawnGroup("innadril23_mb2422_pt" + instance.getStatus() + "m1");
+ instance.getParameters().set("KILL_COUNT", 0);
+
+ switch (instance.getStatus())
+ {
+ case 1:
+ {
+ instance.getParameters().set("KILL_TARGET", 12);
+ instance.setStatus(2);
+ break;
+ }
+ case 2:
+ {
+ instance.getParameters().set("KILL_TARGET", 3);
+ instance.setStatus(3);
+ break;
+ }
+ case 3:
+ {
+ instance.getParameters().set("KILL_TARGET", 18);
+ instance.setStatus(4);
+ break;
+ }
+ case 4:
+ {
+ instance.getParameters().set("KILL_TARGET", 5);
+ instance.setStatus(5);
+ break;
+ }
+ case 5:
+ {
+ instance.getParameters().set("KILL_TARGET", 20);
+ instance.setStatus(6);
+ break;
+ }
+ case 6:
+ {
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = KECHI_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = KECHI_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = KECHI_WEALTHY;
+ }
+ else
+ {
+ bossId = KECHI_ARMED;
+ }
+
+ addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOGGER.warning("Cannot find teleport zone for Crystal Cavern: Steam Corridor instance!!!");
+ }
+ }
+ }
+ break;
+ }
+ case KECHI_NORMAL:
+ case KECHI_WISE:
+ case KECHI_WEALTHY:
+ case KECHI_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ private void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance instance = npc.getInstanceWorld();
+
+ if (isInInstance(instance) && creature.isPlayer())
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case PLAYER_DETECTOR:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.broadcastEvent(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0)), 2000, null);
+
+ for (int i = 0; i < getRandom(5); i++)
+ {
+ final Npc trap = addSpawn(((npcParams.getInt("MobType", 0) == 0) ? TRAP_1 : TRAP_2), npc, true, 0, false, instance.getId());
+ trap.getVariables().set("TIMER_VAL", 4);
+ }
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsSteamCorridor();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
new file mode 100644
index 0000000000..efc886f117
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+You must be in a party to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
new file mode 100644
index 0000000000..dfa8583852
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+The leader of the party should try to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/19000-19099.xml b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/19000-19099.xml
index e684733153..43ec23bd28 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/19000-19099.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/19000-19099.xml
@@ -366,7 +366,7 @@
-
+
@@ -403,7 +403,7 @@
-
+
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/zones/teleportzones.xml b/L2J_Mobius_2.5_Underground/dist/game/data/zones/teleportzones.xml
index 148fab164c..7c3f8b0806 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/zones/teleportzones.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/zones/teleportzones.xml
@@ -154,6 +154,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/Routes.xml b/L2J_Mobius_3.0_Helios/dist/game/data/Routes.xml
index 9ca4f105cb..97221d49ac 100644
--- a/L2J_Mobius_3.0_Helios/dist/game/data/Routes.xml
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/Routes.xml
@@ -2476,6 +2476,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsCoralGarden.xml b/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsCoralGarden.xml
new file mode 100644
index 0000000000..00ecfbdb7d
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsCoralGarden.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml b/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
new file mode 100644
index 0000000000..57900c0c1e
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
@@ -0,0 +1,2511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsSteamCorridor.xml b/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
new file mode 100644
index 0000000000..2f12da9071
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
@@ -0,0 +1,694 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
new file mode 100644
index 0000000000..35880d0254
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
@@ -0,0 +1,215 @@
+/*
+ * 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 .
+ */
+package ai.areas.Parnassus.EntrancePortalToCrystalCaverns;
+
+import java.util.Calendar;
+
+import org.l2jmobius.gameserver.instancemanager.QuestManager;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.quest.Quest;
+import org.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+
+import ai.AbstractNpcAI;
+import instances.CrystalCaverns.CrystalCavernsCoralGarden;
+import instances.CrystalCaverns.CrystalCavernsEmeraldSquare;
+import instances.CrystalCaverns.CrystalCavernsSteamCorridor;
+
+/**
+ * Entrance Portal to Crystal Caverns AI.
+ * @author St3eT
+ */
+public class EntrancePortalToCrystalCaverns extends AbstractNpcAI
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ // Misc
+ private static final int EMERALD_SQUARE_TEMPLATE_ID = 163;
+ private static final int STEAM_CORRIDOR_TEMPLATE_ID = 164;
+ private static final int CORAL_GARDEN_TEMPLATE_ID = 165;
+ private static final int PRISON_ENTRACE_TRIGGER_1 = 24230010;
+ private static final int PRISON_ENTRACE_TRIGGER_2 = 24230012;
+ private static final int CAVERNS_ENTRACE_TRIGGER_1 = 24230014;
+ private static final int CAVERNS_ENTRACE_TRIGGER_2 = 24230016;
+ private static final int CAVERNS_ENTRACE_TRIGGER_3 = 24230018;
+
+ private EntrancePortalToCrystalCaverns()
+ {
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(CAVERNS_ENTRACE);
+ addSpawnId(CAVERNS_ENTRACE);
+ addSeeCreatureId(CAVERNS_ENTRACE);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ Quest instanceScript = null;
+
+ switch (getCurrentInstanceTemplateId())
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsEmeraldSquare.class.getSimpleName());
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsSteamCorridor.class.getSimpleName());
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsCoralGarden.class.getSimpleName());
+ break;
+ }
+ }
+
+ if (instanceScript != null)
+ {
+ instanceScript.notifyEvent(event, npc, player);
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ return "EntrancePortal_" + getCurrentInstanceTemplateId() + ".html";
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ getTimers().addRepeatingTimer("LOOP_TIMER", 10000, npc, null);
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("LOOP_TIMER"))
+ {
+ final int currentTemplateId = getCurrentInstanceTemplateId();
+
+ World.getInstance().forEachVisibleObjectInRange(npc, PlayerInstance.class, 500, p ->
+ {
+ updateTriggersForPlayer(player, currentTemplateId);
+ });
+ }
+ }
+
+ @Override
+ public String onSeeCreature(Npc npc, Creature creature, boolean isSummon)
+ {
+ if (creature.isPlayer())
+ {
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_1, true));
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_2, true));
+ updateTriggersForPlayer(creature.getActingPlayer(), getCurrentInstanceTemplateId());
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ public void updateTriggersForPlayer(PlayerInstance player, int currentTemplateId)
+ {
+ if (player != null)
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, false));
+
+ switch (currentTemplateId)
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, true));
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, true));
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, true));
+ break;
+ }
+ }
+ }
+ }
+
+ public int getCurrentInstanceTemplateId()
+ {
+ final int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
+ final int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ int templateId = -1;
+
+ switch (day)
+ {
+ case Calendar.MONDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.TUESDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.WEDNESDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.THURSDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.FRIDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SATURDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SUNDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ }
+ return templateId;
+ }
+
+ public static void main(String[] args)
+ {
+ new EntrancePortalToCrystalCaverns();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
new file mode 100644
index 0000000000..6de9f6e4b9
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Emerald Square.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
new file mode 100644
index 0000000000..b0d8310c68
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Steam Corridor.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
new file mode 100644
index 0000000000..ea7efec129
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Coral Garden.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
new file mode 100644
index 0000000000..21d8f0d1b3
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
@@ -0,0 +1,253 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.instancemanager.WalkingManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.Spawn;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Coral Garden instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsCoralGarden extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int MICHAELA_NORMAL = 25799;
+ private static final int MICHAELA_WISE = 26116;
+ private static final int MICHAELA_WEALTHY = 26115;
+ private static final int MICHAELA_ARMED = 26114;
+ private static final int GOLEM_1 = 19013; // Crystalline Golem
+ private static final int GOLEM_2 = 19014; // Crystalline Golem
+ // Location
+ private static final Location BOSS_LOC = new Location(144307, 220032, -11824);
+ // Misc
+ private static final int TEMPLATE_ID = 165;
+ private static final int BOSS_DOOR_ID = 24240026;
+ private static final int PLAYER_MAX_DISTANCE = 250;
+
+ public CrystalCavernsCoralGarden()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(GOLEM_1, GOLEM_2);
+ addKillId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addAttackId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addRouteFinishedId(GOLEM_1, GOLEM_2);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "SUCCESS_TIMER":
+ {
+ showOnScreenMsg(instance, NpcStringId.GOLEM_LOCATION_SUCCESSFUL_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ break;
+ }
+ case "LOOP_TIMER":
+ {
+ player = npcVars.getObject("PLAYER_OBJECT", PlayerInstance.class);
+
+ if ((player != null) && (npc.calculateDistance3D(player) > PLAYER_MAX_DISTANCE) && npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ addMoveToDesire(npc, new Location(npc.getX() + getRandom(-100, 100), npc.getY() + getRandom(-150, 150), npc.getZ()), 23);
+ npc.setRunning();
+ npcVars.set("NPC_FOLLOWING", false);
+ getTimers().cancelTimer("LOOP_TIMER", npc, null);
+ getTimers().addTimer("FAIL_TIMER", 5000, npc, null);
+ }
+ break;
+ }
+ case "FAIL_TIMER":
+ {
+ final Spawn spawn = npc.getSpawn();
+
+ if (!npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ npc.setWalking();
+ npc.teleToLocation(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ());
+ npc.setScriptValue(0);
+ npc.setNameString(null);
+ npc.setTitleString(null);
+ npc.setTitle(null);
+ npc.broadcastInfo();
+ }
+ npcVars.set("CAN_CALL_MONSTERS", ((spawn.getX() - ((npc.getX() * spawn.getX()) - npc.getX())) + (spawn.getY() - (npc.getY() * spawn.getY()) - npc.getY())) > (200 * 200));
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.getVariables().set("PLAYER_OBJECT", player);
+ npc.setNameString(NpcStringId.TRAITOR_CRYSTALLINE_GOLEM);
+ npc.setTitleString(NpcStringId.GIVEN_TO_S1);
+ npc.setTitle(player.getName());
+ npc.broadcastInfo();
+ WalkingManager.getInstance().startMoving(npc, npc.getId() == GOLEM_1 ? "gd_golem_1" : "gd_golem_2");
+ getTimers().addRepeatingTimer("LOOP_TIMER", 500, npc, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onRouteFinished(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (instance != null)
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ showOnScreenMsg(instance, NpcStringId.GOLEM_ENTERED_THE_REQUIRED_ZONE, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ npc.deleteMe();
+
+ if (instance.getAliveNpcs(GOLEM_1, GOLEM_2).isEmpty())
+ {
+ instance.openCloseDoor(BOSS_DOOR_ID, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = MICHAELA_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = MICHAELA_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = MICHAELA_WEALTHY;
+ }
+ else
+ {
+ bossId = MICHAELA_ARMED;
+ }
+
+ final Npc boss = addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ getTimers().addTimer("SUCCESS_TIMER", 5000, boss, null);
+ }
+ }
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ instance.openCloseDoor(BOSS_DOOR_ID, false);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsCoralGarden();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
new file mode 100644
index 0000000000..26bb4ad5c8
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
@@ -0,0 +1,424 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.MonsterInstance;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.skills.Skill;
+import org.l2jmobius.gameserver.model.stats.Stats;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Emerald Square instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsEmeraldSquare extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int VERIDAN_NORMAL = 25796;
+ private static final int VERIDAN_WISE = 26107;
+ private static final int VERIDAN_WEALTHY = 26106;
+ private static final int VERIDAN_ARMED = 26105;
+ private static final int WATER_CANNON = 19008;
+ private static final int WATER_CANNON_SKILL = 19009;
+ private static final int STRONGHOLD_PROTECTOR = 23012;
+ private static final int SQUARE_INTRUDER = 23010;
+ private static final int SQUARE_ATTACKER = 23011;
+ // Skills
+ private static final SkillHolder DESTROY_SKILL = new SkillHolder(12003, 1);
+ private static final SkillHolder WATER_CANNON_SKILL_ATTACK = new SkillHolder(14179, 1);
+ // Locations
+ private static final Location[] BOSS_SPAWNS =
+ {
+ new Location(152745, 145957, -12584, 16446),
+ new Location(152816, 145968, -12633, 16446),
+ };
+ // Misc
+ private static final int TEMPLATE_ID = 163;
+ private static final int RAID_DOOR_1 = 24220005;
+ private static final int RAID_DOOR_2 = 24220006;
+
+ public CrystalCavernsEmeraldSquare()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addSpawnId(WATER_CANNON);
+ addKillId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addAttackId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addSpellFinishedId(WATER_CANNON_SKILL);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, WATER_CANNON);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final StatsSet npcVars = npc.getVariables();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (event)
+ {
+ case "HP_REGEN_TIMER":
+ {
+ int value = ((baseId == 5) || (baseId == 6)) ? 5 : baseId;
+ npc.getStat().addFixedValue(Stats.REGENERATE_HP_RATE, Double.valueOf(value * 1000));
+ break;
+ }
+ case "SUPPORT_SPAWN_TIMER":
+ {
+ int supportVal = npcVars.getInt("SUPPORT_VALUE", 0);
+
+ if (supportVal > 3)
+ {
+ return;
+ }
+
+ if ((supportVal == 0) || (supportVal == 1) || (supportVal == 2))
+ {
+ final String spawnName = npcParams.getString("SupportMaker" + (supportVal + 1), null);
+ if (spawnName != null)
+ {
+ instance.spawnGroup(spawnName);
+ }
+ npcVars.increaseInt("SUPPORT_VALUE", 1);
+ }
+
+ if (!npcVars.getBoolean("PREVIOUS_BASE_DESTROYED", false))
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", 60000, npc, null);
+ }
+ break;
+ }
+ case "CANNON_LOOP_ATTACK":
+ {
+ if (npc.getCurrentHpPercent() > 30)
+ {
+ if (npcVars.getBoolean("IS_DESTROY_ACTIVATED", false) || (getRandom(10) < 2))
+ {
+ final Npc cannonSkill = addSpawn(WATER_CANNON_SKILL, npc, true, 0, false, instance.getId());
+ addSkillCastDesire(cannonSkill, cannonSkill, WATER_CANNON_SKILL_ATTACK, 23);
+ }
+ }
+ break;
+ }
+ case "SUICIDE_TIMER":
+ {
+ npc.doDie(null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (npc.getId())
+ {
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (!npcVars.getBoolean("CLOSED_DOORS", false))
+ {
+ npcVars.set("CLOSED_DOORS", true);
+ instance.openCloseDoor(RAID_DOOR_2, false);
+ }
+ }
+ case WATER_CANNON:
+ {
+ if ((skill != null) && (skill.getId() == DESTROY_SKILL.getSkillId()) && !npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npcVars.set("IS_DESTROY_ACTIVATED", true);
+ npc.setDisplayEffect(2);
+ getTimers().addTimer("SUICIDE_TIMER", 60000, npc, null);
+ }
+
+ if (npc.getCurrentHpPercent() < 30)
+ {
+ if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(3);
+
+ }
+ }
+ else if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (npc.getId())
+ {
+ case VERIDAN_NORMAL:
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (instance.getAliveNpcs(VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED).isEmpty())
+ {
+ instance.finishInstance();
+ }
+ else
+ {
+ instance.setReenterTime();
+ }
+ break;
+ }
+ case WATER_CANNON:
+ {
+ npc.setDisplayEffect(4);
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_S1, ExShowScreenMessage.MIDDLE_CENTER, 4000, String.valueOf(npc.getParameters().getInt("base_id", -1)));
+
+ World.getInstance().forEachVisibleObjectInRange(npc, MonsterInstance.class, 400, monster ->
+ {
+ if ((monster.getId() == STRONGHOLD_PROTECTOR) || (monster.getId() == SQUARE_INTRUDER) || (monster.getId() == SQUARE_ATTACKER))
+ {
+ monster.doDie(null);
+ }
+ });
+
+ instance.getAliveNpcs(WATER_CANNON).forEach(cannon ->
+ {
+ final int cannonBaseId = cannon.getParameters().getInt("base_id", -1);
+
+ switch (baseId)
+ {
+ case 1:
+ {
+ if (cannonBaseId == 2)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (cannonBaseId == 3)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 3:
+ {
+ if (cannonBaseId == 4)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 4:
+ {
+ if ((cannonBaseId == 5) || (cannonBaseId == 6))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 5:
+ case 6:
+ {
+ if (cannonBaseId == 7)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 7:
+ {
+ if ((cannonBaseId == 8) || (cannonBaseId == 9))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ }
+ });
+
+ if ((baseId == 8) || (baseId == 9))
+ {
+ instance.getParameters().increaseInt("MAIN_TARGETS_KILLED", 0, 1);
+
+ if (instance.getParameters().getInt("MAIN_TARGETS_KILLED", 0) == 2)
+ {
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.openCloseDoor(RAID_DOOR_1, true);
+ instance.openCloseDoor(RAID_DOOR_2, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = VERIDAN_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = VERIDAN_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = VERIDAN_WEALTHY;
+ }
+ else
+ {
+ bossId = VERIDAN_ARMED;
+ }
+
+ for (Location loc : BOSS_SPAWNS)
+ {
+ addSpawn(bossId, loc, false, 0, false, instance.getId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onSpellFinished(Npc npc, PlayerInstance player, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance) && (npc.getId() == WATER_CANNON_SKILL) && (skill.getId() == WATER_CANNON_SKILL_ATTACK.getSkillId()))
+ {
+ npc.deleteMe();
+ }
+ return super.onSpellFinished(npc, player, skill);
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case WATER_CANNON:
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ if (baseId != 1)
+ {
+ npc.setTargetable(false);
+ }
+
+ getTimers().addTimer("HP_REGEN_TIMER", 10000, npc, null);
+
+ if (baseId > 0)
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", (baseId * 60) * 1000, npc, null);
+ }
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ public void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance world = npc.getInstanceWorld();
+
+ if ((world != null) && creature.isPlayer() && npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.setDisplayEffect(1);
+ getTimers().addRepeatingTimer("CANNON_LOOP_ATTACK", 1000, npc, null);
+ }
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsEmeraldSquare();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
new file mode 100644
index 0000000000..23f045e3fd
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
@@ -0,0 +1,408 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.enums.ChatType;
+import org.l2jmobius.gameserver.instancemanager.ZoneManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.zone.ZoneType;
+import org.l2jmobius.gameserver.model.zone.type.TeleportZone;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Steam Corridor instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsSteamCorridor extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int KECHI_NORMAL = 25797;
+ private static final int KECHI_WISE = 26113;
+ private static final int KECHI_WEALTHY = 26112;
+ private static final int KECHI_ARMED = 26111;
+ private static final int VICIOUS_DUELER = 23014;
+ private static final int VICIOUS_WARRIOR = 23016;
+ private static final int VICIOUS_SWORDSMAN = 23015;
+ private static final int SPIRIT_PROTECTOR = 23013;
+ private static final int FIRE_REGION = 19161;
+ private static final int PLAYER_DETECTOR = 19075;
+ private static final int TRAP_1 = 19011;
+ private static final int TRAP_2 = 19012;
+ // Skills
+ private static final SkillHolder FIRE_SKILL_1 = new SkillHolder(14373, 1);
+ private static final SkillHolder FIRE_SKILL_2 = new SkillHolder(14373, 2);
+ private static final SkillHolder FIRE_SKILL_3 = new SkillHolder(14197, 1);
+ private static final SkillHolder TRAP_SKILL_1 = new SkillHolder(14180, 1);
+ private static final SkillHolder TRAP_SKILL_2 = new SkillHolder(14181, 1);
+ private static final SkillHolder TRAP_SKILL_3 = new SkillHolder(14372, 1);
+ // Location
+ private static final Location BOSS_LOC = new Location(154078, 215125, -12140);
+ // Misc
+ private static final int TEMPLATE_ID = 164;
+
+ public CrystalCavernsSteamCorridor()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addAttackId(TRAP_1, TRAP_2);
+ addKillId(VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, KECHI_NORMAL, KECHI_WISE, KECHI_WEALTHY, KECHI_ARMED);
+ addSpawnId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, FIRE_REGION, PLAYER_DETECTOR);
+ addEventReceivedId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN);
+ addInstanceCreatedId(TEMPLATE_ID);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, PLAYER_DETECTOR);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "FIRE_REGION_TIMER_1":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_1, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_2", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_2":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_2, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_3":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_3, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 1000, npc, null);
+ break;
+ }
+ case "TRAP_REACT_TIMER":
+ {
+ final int timer = npcVars.increaseInt("TIMER_VAL", -1);
+ if (timer > 0)
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, " " + timer);
+ }
+ else
+ {
+ if (npc.getId() == TRAP_1)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_1 : TRAP_SKILL_3), 23);
+ }
+ else if (npc.getId() == TRAP_2)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_2 : TRAP_SKILL_3), 23);
+ }
+ }
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceCreated(Instance instance, PlayerInstance player)
+ {
+ instance.setStatus(1);
+ for (int i = 0; i < 6; i++)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName("24_24_fire_telezone_0" + i, TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(false, instance.getId());
+ }
+ }
+ super.onInstanceCreated(instance, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case SPIRIT_PROTECTOR:
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ npc.setTargetable(false);
+ npc.disableCoreAI(true);
+ npc.setInvisible(true);
+ break;
+ }
+ case FIRE_REGION:
+ {
+ final int timeLimit = npcParams.getInt("Limit_Time", 0);
+ if (timeLimit > 0)
+ {
+ getTimers().addTimer("FIRE_REGION_TIMER_1", ((timeLimit * 30) * 100), npc, null);
+ }
+ npc.setTargetable(false);
+ npc.setIsInvul(true);
+ npc.setRandomAnimation(false);
+ npc.setRandomWalking(false);
+ npc.disableCoreAI(true);
+ break;
+ }
+ case PLAYER_DETECTOR:
+ {
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onEventReceived(String eventName, Npc sender, Npc receiver, WorldObject reference)
+ {
+ final Instance instance = receiver.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = receiver.getParameters();
+
+ if (eventName.equals(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0))))
+ {
+ receiver.setTargetable(true);
+ receiver.disableCoreAI(false);
+ receiver.setInvisible(false);
+ }
+ }
+ return super.onEventReceived(eventName, sender, receiver, reference);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case TRAP_1:
+ case TRAP_2:
+ {
+ if (npc.isScriptValue(0))
+ {
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ npc.setScriptValue(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int killTarget = instance.getParameters().getInt("KILL_TARGET", 5);
+ int currentKillCount = instance.getParameters().getInt("KILL_COUNT", 0);
+
+ switch (npc.getId())
+ {
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ if (npcParams.getInt("last_checker", 0) == 1)
+ {
+ currentKillCount = instance.getParameters().increaseInt("KILL_COUNT", 0, 1);
+
+ if (currentKillCount >= killTarget)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName(npc.getParameters().getString("AreaTeleName"), TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(true, instance.getId());
+ showOnScreenMsg(instance, NpcStringId.THE_PORTAL_TO_THE_NEXT_ROOM_IS_NOW_OPEN, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.spawnGroup("innadril23_mb2422_pt" + instance.getStatus() + "m1");
+ instance.getParameters().set("KILL_COUNT", 0);
+
+ switch (instance.getStatus())
+ {
+ case 1:
+ {
+ instance.getParameters().set("KILL_TARGET", 12);
+ instance.setStatus(2);
+ break;
+ }
+ case 2:
+ {
+ instance.getParameters().set("KILL_TARGET", 3);
+ instance.setStatus(3);
+ break;
+ }
+ case 3:
+ {
+ instance.getParameters().set("KILL_TARGET", 18);
+ instance.setStatus(4);
+ break;
+ }
+ case 4:
+ {
+ instance.getParameters().set("KILL_TARGET", 5);
+ instance.setStatus(5);
+ break;
+ }
+ case 5:
+ {
+ instance.getParameters().set("KILL_TARGET", 20);
+ instance.setStatus(6);
+ break;
+ }
+ case 6:
+ {
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = KECHI_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = KECHI_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = KECHI_WEALTHY;
+ }
+ else
+ {
+ bossId = KECHI_ARMED;
+ }
+
+ addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOGGER.warning("Cannot find teleport zone for Crystal Cavern: Steam Corridor instance!!!");
+ }
+ }
+ }
+ break;
+ }
+ case KECHI_NORMAL:
+ case KECHI_WISE:
+ case KECHI_WEALTHY:
+ case KECHI_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ private void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance instance = npc.getInstanceWorld();
+
+ if (isInInstance(instance) && creature.isPlayer())
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case PLAYER_DETECTOR:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.broadcastEvent(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0)), 2000, null);
+
+ for (int i = 0; i < getRandom(5); i++)
+ {
+ final Npc trap = addSpawn(((npcParams.getInt("MobType", 0) == 0) ? TRAP_1 : TRAP_2), npc, true, 0, false, instance.getId());
+ trap.getVariables().set("TIMER_VAL", 4);
+ }
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsSteamCorridor();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
new file mode 100644
index 0000000000..efc886f117
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+You must be in a party to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
new file mode 100644
index 0000000000..dfa8583852
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+The leader of the party should try to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/19000-19099.xml b/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/19000-19099.xml
index e684733153..43ec23bd28 100644
--- a/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/19000-19099.xml
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/19000-19099.xml
@@ -366,7 +366,7 @@
-
+
@@ -403,7 +403,7 @@
-
+
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/zones/teleportzones.xml b/L2J_Mobius_3.0_Helios/dist/game/data/zones/teleportzones.xml
index bbf62a120d..3d4ff454c9 100644
--- a/L2J_Mobius_3.0_Helios/dist/game/data/zones/teleportzones.xml
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/zones/teleportzones.xml
@@ -154,6 +154,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/Routes.xml b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/Routes.xml
index 8727b0389a..b971a893a6 100644
--- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/Routes.xml
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/Routes.xml
@@ -2476,6 +2476,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsCoralGarden.xml b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsCoralGarden.xml
new file mode 100644
index 0000000000..00ecfbdb7d
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsCoralGarden.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
new file mode 100644
index 0000000000..57900c0c1e
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
@@ -0,0 +1,2511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsSteamCorridor.xml b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
new file mode 100644
index 0000000000..2f12da9071
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
@@ -0,0 +1,694 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
new file mode 100644
index 0000000000..35880d0254
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
@@ -0,0 +1,215 @@
+/*
+ * 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 .
+ */
+package ai.areas.Parnassus.EntrancePortalToCrystalCaverns;
+
+import java.util.Calendar;
+
+import org.l2jmobius.gameserver.instancemanager.QuestManager;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.quest.Quest;
+import org.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+
+import ai.AbstractNpcAI;
+import instances.CrystalCaverns.CrystalCavernsCoralGarden;
+import instances.CrystalCaverns.CrystalCavernsEmeraldSquare;
+import instances.CrystalCaverns.CrystalCavernsSteamCorridor;
+
+/**
+ * Entrance Portal to Crystal Caverns AI.
+ * @author St3eT
+ */
+public class EntrancePortalToCrystalCaverns extends AbstractNpcAI
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ // Misc
+ private static final int EMERALD_SQUARE_TEMPLATE_ID = 163;
+ private static final int STEAM_CORRIDOR_TEMPLATE_ID = 164;
+ private static final int CORAL_GARDEN_TEMPLATE_ID = 165;
+ private static final int PRISON_ENTRACE_TRIGGER_1 = 24230010;
+ private static final int PRISON_ENTRACE_TRIGGER_2 = 24230012;
+ private static final int CAVERNS_ENTRACE_TRIGGER_1 = 24230014;
+ private static final int CAVERNS_ENTRACE_TRIGGER_2 = 24230016;
+ private static final int CAVERNS_ENTRACE_TRIGGER_3 = 24230018;
+
+ private EntrancePortalToCrystalCaverns()
+ {
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(CAVERNS_ENTRACE);
+ addSpawnId(CAVERNS_ENTRACE);
+ addSeeCreatureId(CAVERNS_ENTRACE);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ Quest instanceScript = null;
+
+ switch (getCurrentInstanceTemplateId())
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsEmeraldSquare.class.getSimpleName());
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsSteamCorridor.class.getSimpleName());
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsCoralGarden.class.getSimpleName());
+ break;
+ }
+ }
+
+ if (instanceScript != null)
+ {
+ instanceScript.notifyEvent(event, npc, player);
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ return "EntrancePortal_" + getCurrentInstanceTemplateId() + ".html";
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ getTimers().addRepeatingTimer("LOOP_TIMER", 10000, npc, null);
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("LOOP_TIMER"))
+ {
+ final int currentTemplateId = getCurrentInstanceTemplateId();
+
+ World.getInstance().forEachVisibleObjectInRange(npc, PlayerInstance.class, 500, p ->
+ {
+ updateTriggersForPlayer(player, currentTemplateId);
+ });
+ }
+ }
+
+ @Override
+ public String onSeeCreature(Npc npc, Creature creature, boolean isSummon)
+ {
+ if (creature.isPlayer())
+ {
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_1, true));
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_2, true));
+ updateTriggersForPlayer(creature.getActingPlayer(), getCurrentInstanceTemplateId());
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ public void updateTriggersForPlayer(PlayerInstance player, int currentTemplateId)
+ {
+ if (player != null)
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, false));
+
+ switch (currentTemplateId)
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, true));
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, true));
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, true));
+ break;
+ }
+ }
+ }
+ }
+
+ public int getCurrentInstanceTemplateId()
+ {
+ final int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
+ final int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ int templateId = -1;
+
+ switch (day)
+ {
+ case Calendar.MONDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.TUESDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.WEDNESDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.THURSDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.FRIDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SATURDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SUNDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ }
+ return templateId;
+ }
+
+ public static void main(String[] args)
+ {
+ new EntrancePortalToCrystalCaverns();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
new file mode 100644
index 0000000000..6de9f6e4b9
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Emerald Square.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
new file mode 100644
index 0000000000..b0d8310c68
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Steam Corridor.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
new file mode 100644
index 0000000000..ea7efec129
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Coral Garden.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
new file mode 100644
index 0000000000..21d8f0d1b3
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
@@ -0,0 +1,253 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.instancemanager.WalkingManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.Spawn;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Coral Garden instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsCoralGarden extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int MICHAELA_NORMAL = 25799;
+ private static final int MICHAELA_WISE = 26116;
+ private static final int MICHAELA_WEALTHY = 26115;
+ private static final int MICHAELA_ARMED = 26114;
+ private static final int GOLEM_1 = 19013; // Crystalline Golem
+ private static final int GOLEM_2 = 19014; // Crystalline Golem
+ // Location
+ private static final Location BOSS_LOC = new Location(144307, 220032, -11824);
+ // Misc
+ private static final int TEMPLATE_ID = 165;
+ private static final int BOSS_DOOR_ID = 24240026;
+ private static final int PLAYER_MAX_DISTANCE = 250;
+
+ public CrystalCavernsCoralGarden()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(GOLEM_1, GOLEM_2);
+ addKillId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addAttackId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addRouteFinishedId(GOLEM_1, GOLEM_2);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "SUCCESS_TIMER":
+ {
+ showOnScreenMsg(instance, NpcStringId.GOLEM_LOCATION_SUCCESSFUL_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ break;
+ }
+ case "LOOP_TIMER":
+ {
+ player = npcVars.getObject("PLAYER_OBJECT", PlayerInstance.class);
+
+ if ((player != null) && (npc.calculateDistance3D(player) > PLAYER_MAX_DISTANCE) && npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ addMoveToDesire(npc, new Location(npc.getX() + getRandom(-100, 100), npc.getY() + getRandom(-150, 150), npc.getZ()), 23);
+ npc.setRunning();
+ npcVars.set("NPC_FOLLOWING", false);
+ getTimers().cancelTimer("LOOP_TIMER", npc, null);
+ getTimers().addTimer("FAIL_TIMER", 5000, npc, null);
+ }
+ break;
+ }
+ case "FAIL_TIMER":
+ {
+ final Spawn spawn = npc.getSpawn();
+
+ if (!npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ npc.setWalking();
+ npc.teleToLocation(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ());
+ npc.setScriptValue(0);
+ npc.setNameString(null);
+ npc.setTitleString(null);
+ npc.setTitle(null);
+ npc.broadcastInfo();
+ }
+ npcVars.set("CAN_CALL_MONSTERS", ((spawn.getX() - ((npc.getX() * spawn.getX()) - npc.getX())) + (spawn.getY() - (npc.getY() * spawn.getY()) - npc.getY())) > (200 * 200));
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.getVariables().set("PLAYER_OBJECT", player);
+ npc.setNameString(NpcStringId.TRAITOR_CRYSTALLINE_GOLEM);
+ npc.setTitleString(NpcStringId.GIVEN_TO_S1);
+ npc.setTitle(player.getName());
+ npc.broadcastInfo();
+ WalkingManager.getInstance().startMoving(npc, npc.getId() == GOLEM_1 ? "gd_golem_1" : "gd_golem_2");
+ getTimers().addRepeatingTimer("LOOP_TIMER", 500, npc, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onRouteFinished(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (instance != null)
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ showOnScreenMsg(instance, NpcStringId.GOLEM_ENTERED_THE_REQUIRED_ZONE, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ npc.deleteMe();
+
+ if (instance.getAliveNpcs(GOLEM_1, GOLEM_2).isEmpty())
+ {
+ instance.openCloseDoor(BOSS_DOOR_ID, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = MICHAELA_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = MICHAELA_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = MICHAELA_WEALTHY;
+ }
+ else
+ {
+ bossId = MICHAELA_ARMED;
+ }
+
+ final Npc boss = addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ getTimers().addTimer("SUCCESS_TIMER", 5000, boss, null);
+ }
+ }
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ instance.openCloseDoor(BOSS_DOOR_ID, false);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsCoralGarden();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
new file mode 100644
index 0000000000..26bb4ad5c8
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
@@ -0,0 +1,424 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.MonsterInstance;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.skills.Skill;
+import org.l2jmobius.gameserver.model.stats.Stats;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Emerald Square instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsEmeraldSquare extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int VERIDAN_NORMAL = 25796;
+ private static final int VERIDAN_WISE = 26107;
+ private static final int VERIDAN_WEALTHY = 26106;
+ private static final int VERIDAN_ARMED = 26105;
+ private static final int WATER_CANNON = 19008;
+ private static final int WATER_CANNON_SKILL = 19009;
+ private static final int STRONGHOLD_PROTECTOR = 23012;
+ private static final int SQUARE_INTRUDER = 23010;
+ private static final int SQUARE_ATTACKER = 23011;
+ // Skills
+ private static final SkillHolder DESTROY_SKILL = new SkillHolder(12003, 1);
+ private static final SkillHolder WATER_CANNON_SKILL_ATTACK = new SkillHolder(14179, 1);
+ // Locations
+ private static final Location[] BOSS_SPAWNS =
+ {
+ new Location(152745, 145957, -12584, 16446),
+ new Location(152816, 145968, -12633, 16446),
+ };
+ // Misc
+ private static final int TEMPLATE_ID = 163;
+ private static final int RAID_DOOR_1 = 24220005;
+ private static final int RAID_DOOR_2 = 24220006;
+
+ public CrystalCavernsEmeraldSquare()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addSpawnId(WATER_CANNON);
+ addKillId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addAttackId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addSpellFinishedId(WATER_CANNON_SKILL);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, WATER_CANNON);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final StatsSet npcVars = npc.getVariables();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (event)
+ {
+ case "HP_REGEN_TIMER":
+ {
+ int value = ((baseId == 5) || (baseId == 6)) ? 5 : baseId;
+ npc.getStat().addFixedValue(Stats.REGENERATE_HP_RATE, Double.valueOf(value * 1000));
+ break;
+ }
+ case "SUPPORT_SPAWN_TIMER":
+ {
+ int supportVal = npcVars.getInt("SUPPORT_VALUE", 0);
+
+ if (supportVal > 3)
+ {
+ return;
+ }
+
+ if ((supportVal == 0) || (supportVal == 1) || (supportVal == 2))
+ {
+ final String spawnName = npcParams.getString("SupportMaker" + (supportVal + 1), null);
+ if (spawnName != null)
+ {
+ instance.spawnGroup(spawnName);
+ }
+ npcVars.increaseInt("SUPPORT_VALUE", 1);
+ }
+
+ if (!npcVars.getBoolean("PREVIOUS_BASE_DESTROYED", false))
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", 60000, npc, null);
+ }
+ break;
+ }
+ case "CANNON_LOOP_ATTACK":
+ {
+ if (npc.getCurrentHpPercent() > 30)
+ {
+ if (npcVars.getBoolean("IS_DESTROY_ACTIVATED", false) || (getRandom(10) < 2))
+ {
+ final Npc cannonSkill = addSpawn(WATER_CANNON_SKILL, npc, true, 0, false, instance.getId());
+ addSkillCastDesire(cannonSkill, cannonSkill, WATER_CANNON_SKILL_ATTACK, 23);
+ }
+ }
+ break;
+ }
+ case "SUICIDE_TIMER":
+ {
+ npc.doDie(null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (npc.getId())
+ {
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (!npcVars.getBoolean("CLOSED_DOORS", false))
+ {
+ npcVars.set("CLOSED_DOORS", true);
+ instance.openCloseDoor(RAID_DOOR_2, false);
+ }
+ }
+ case WATER_CANNON:
+ {
+ if ((skill != null) && (skill.getId() == DESTROY_SKILL.getSkillId()) && !npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npcVars.set("IS_DESTROY_ACTIVATED", true);
+ npc.setDisplayEffect(2);
+ getTimers().addTimer("SUICIDE_TIMER", 60000, npc, null);
+ }
+
+ if (npc.getCurrentHpPercent() < 30)
+ {
+ if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(3);
+
+ }
+ }
+ else if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (npc.getId())
+ {
+ case VERIDAN_NORMAL:
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (instance.getAliveNpcs(VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED).isEmpty())
+ {
+ instance.finishInstance();
+ }
+ else
+ {
+ instance.setReenterTime();
+ }
+ break;
+ }
+ case WATER_CANNON:
+ {
+ npc.setDisplayEffect(4);
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_S1, ExShowScreenMessage.MIDDLE_CENTER, 4000, String.valueOf(npc.getParameters().getInt("base_id", -1)));
+
+ World.getInstance().forEachVisibleObjectInRange(npc, MonsterInstance.class, 400, monster ->
+ {
+ if ((monster.getId() == STRONGHOLD_PROTECTOR) || (monster.getId() == SQUARE_INTRUDER) || (monster.getId() == SQUARE_ATTACKER))
+ {
+ monster.doDie(null);
+ }
+ });
+
+ instance.getAliveNpcs(WATER_CANNON).forEach(cannon ->
+ {
+ final int cannonBaseId = cannon.getParameters().getInt("base_id", -1);
+
+ switch (baseId)
+ {
+ case 1:
+ {
+ if (cannonBaseId == 2)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (cannonBaseId == 3)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 3:
+ {
+ if (cannonBaseId == 4)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 4:
+ {
+ if ((cannonBaseId == 5) || (cannonBaseId == 6))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 5:
+ case 6:
+ {
+ if (cannonBaseId == 7)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 7:
+ {
+ if ((cannonBaseId == 8) || (cannonBaseId == 9))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ }
+ });
+
+ if ((baseId == 8) || (baseId == 9))
+ {
+ instance.getParameters().increaseInt("MAIN_TARGETS_KILLED", 0, 1);
+
+ if (instance.getParameters().getInt("MAIN_TARGETS_KILLED", 0) == 2)
+ {
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.openCloseDoor(RAID_DOOR_1, true);
+ instance.openCloseDoor(RAID_DOOR_2, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = VERIDAN_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = VERIDAN_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = VERIDAN_WEALTHY;
+ }
+ else
+ {
+ bossId = VERIDAN_ARMED;
+ }
+
+ for (Location loc : BOSS_SPAWNS)
+ {
+ addSpawn(bossId, loc, false, 0, false, instance.getId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onSpellFinished(Npc npc, PlayerInstance player, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance) && (npc.getId() == WATER_CANNON_SKILL) && (skill.getId() == WATER_CANNON_SKILL_ATTACK.getSkillId()))
+ {
+ npc.deleteMe();
+ }
+ return super.onSpellFinished(npc, player, skill);
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case WATER_CANNON:
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ if (baseId != 1)
+ {
+ npc.setTargetable(false);
+ }
+
+ getTimers().addTimer("HP_REGEN_TIMER", 10000, npc, null);
+
+ if (baseId > 0)
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", (baseId * 60) * 1000, npc, null);
+ }
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ public void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance world = npc.getInstanceWorld();
+
+ if ((world != null) && creature.isPlayer() && npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.setDisplayEffect(1);
+ getTimers().addRepeatingTimer("CANNON_LOOP_ATTACK", 1000, npc, null);
+ }
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsEmeraldSquare();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
new file mode 100644
index 0000000000..23f045e3fd
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
@@ -0,0 +1,408 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.enums.ChatType;
+import org.l2jmobius.gameserver.instancemanager.ZoneManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.zone.ZoneType;
+import org.l2jmobius.gameserver.model.zone.type.TeleportZone;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Steam Corridor instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsSteamCorridor extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int KECHI_NORMAL = 25797;
+ private static final int KECHI_WISE = 26113;
+ private static final int KECHI_WEALTHY = 26112;
+ private static final int KECHI_ARMED = 26111;
+ private static final int VICIOUS_DUELER = 23014;
+ private static final int VICIOUS_WARRIOR = 23016;
+ private static final int VICIOUS_SWORDSMAN = 23015;
+ private static final int SPIRIT_PROTECTOR = 23013;
+ private static final int FIRE_REGION = 19161;
+ private static final int PLAYER_DETECTOR = 19075;
+ private static final int TRAP_1 = 19011;
+ private static final int TRAP_2 = 19012;
+ // Skills
+ private static final SkillHolder FIRE_SKILL_1 = new SkillHolder(14373, 1);
+ private static final SkillHolder FIRE_SKILL_2 = new SkillHolder(14373, 2);
+ private static final SkillHolder FIRE_SKILL_3 = new SkillHolder(14197, 1);
+ private static final SkillHolder TRAP_SKILL_1 = new SkillHolder(14180, 1);
+ private static final SkillHolder TRAP_SKILL_2 = new SkillHolder(14181, 1);
+ private static final SkillHolder TRAP_SKILL_3 = new SkillHolder(14372, 1);
+ // Location
+ private static final Location BOSS_LOC = new Location(154078, 215125, -12140);
+ // Misc
+ private static final int TEMPLATE_ID = 164;
+
+ public CrystalCavernsSteamCorridor()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addAttackId(TRAP_1, TRAP_2);
+ addKillId(VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, KECHI_NORMAL, KECHI_WISE, KECHI_WEALTHY, KECHI_ARMED);
+ addSpawnId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, FIRE_REGION, PLAYER_DETECTOR);
+ addEventReceivedId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN);
+ addInstanceCreatedId(TEMPLATE_ID);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, PLAYER_DETECTOR);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "FIRE_REGION_TIMER_1":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_1, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_2", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_2":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_2, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_3":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_3, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 1000, npc, null);
+ break;
+ }
+ case "TRAP_REACT_TIMER":
+ {
+ final int timer = npcVars.increaseInt("TIMER_VAL", -1);
+ if (timer > 0)
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, " " + timer);
+ }
+ else
+ {
+ if (npc.getId() == TRAP_1)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_1 : TRAP_SKILL_3), 23);
+ }
+ else if (npc.getId() == TRAP_2)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_2 : TRAP_SKILL_3), 23);
+ }
+ }
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceCreated(Instance instance, PlayerInstance player)
+ {
+ instance.setStatus(1);
+ for (int i = 0; i < 6; i++)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName("24_24_fire_telezone_0" + i, TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(false, instance.getId());
+ }
+ }
+ super.onInstanceCreated(instance, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case SPIRIT_PROTECTOR:
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ npc.setTargetable(false);
+ npc.disableCoreAI(true);
+ npc.setInvisible(true);
+ break;
+ }
+ case FIRE_REGION:
+ {
+ final int timeLimit = npcParams.getInt("Limit_Time", 0);
+ if (timeLimit > 0)
+ {
+ getTimers().addTimer("FIRE_REGION_TIMER_1", ((timeLimit * 30) * 100), npc, null);
+ }
+ npc.setTargetable(false);
+ npc.setIsInvul(true);
+ npc.setRandomAnimation(false);
+ npc.setRandomWalking(false);
+ npc.disableCoreAI(true);
+ break;
+ }
+ case PLAYER_DETECTOR:
+ {
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onEventReceived(String eventName, Npc sender, Npc receiver, WorldObject reference)
+ {
+ final Instance instance = receiver.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = receiver.getParameters();
+
+ if (eventName.equals(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0))))
+ {
+ receiver.setTargetable(true);
+ receiver.disableCoreAI(false);
+ receiver.setInvisible(false);
+ }
+ }
+ return super.onEventReceived(eventName, sender, receiver, reference);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case TRAP_1:
+ case TRAP_2:
+ {
+ if (npc.isScriptValue(0))
+ {
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ npc.setScriptValue(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int killTarget = instance.getParameters().getInt("KILL_TARGET", 5);
+ int currentKillCount = instance.getParameters().getInt("KILL_COUNT", 0);
+
+ switch (npc.getId())
+ {
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ if (npcParams.getInt("last_checker", 0) == 1)
+ {
+ currentKillCount = instance.getParameters().increaseInt("KILL_COUNT", 0, 1);
+
+ if (currentKillCount >= killTarget)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName(npc.getParameters().getString("AreaTeleName"), TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(true, instance.getId());
+ showOnScreenMsg(instance, NpcStringId.THE_PORTAL_TO_THE_NEXT_ROOM_IS_NOW_OPEN, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.spawnGroup("innadril23_mb2422_pt" + instance.getStatus() + "m1");
+ instance.getParameters().set("KILL_COUNT", 0);
+
+ switch (instance.getStatus())
+ {
+ case 1:
+ {
+ instance.getParameters().set("KILL_TARGET", 12);
+ instance.setStatus(2);
+ break;
+ }
+ case 2:
+ {
+ instance.getParameters().set("KILL_TARGET", 3);
+ instance.setStatus(3);
+ break;
+ }
+ case 3:
+ {
+ instance.getParameters().set("KILL_TARGET", 18);
+ instance.setStatus(4);
+ break;
+ }
+ case 4:
+ {
+ instance.getParameters().set("KILL_TARGET", 5);
+ instance.setStatus(5);
+ break;
+ }
+ case 5:
+ {
+ instance.getParameters().set("KILL_TARGET", 20);
+ instance.setStatus(6);
+ break;
+ }
+ case 6:
+ {
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = KECHI_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = KECHI_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = KECHI_WEALTHY;
+ }
+ else
+ {
+ bossId = KECHI_ARMED;
+ }
+
+ addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOGGER.warning("Cannot find teleport zone for Crystal Cavern: Steam Corridor instance!!!");
+ }
+ }
+ }
+ break;
+ }
+ case KECHI_NORMAL:
+ case KECHI_WISE:
+ case KECHI_WEALTHY:
+ case KECHI_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ private void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance instance = npc.getInstanceWorld();
+
+ if (isInInstance(instance) && creature.isPlayer())
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case PLAYER_DETECTOR:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.broadcastEvent(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0)), 2000, null);
+
+ for (int i = 0; i < getRandom(5); i++)
+ {
+ final Npc trap = addSpawn(((npcParams.getInt("MobType", 0) == 0) ? TRAP_1 : TRAP_2), npc, true, 0, false, instance.getId());
+ trap.getVariables().set("TIMER_VAL", 4);
+ }
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsSteamCorridor();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
new file mode 100644
index 0000000000..efc886f117
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+You must be in a party to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
new file mode 100644
index 0000000000..dfa8583852
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+The leader of the party should try to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/stats/npcs/19000-19099.xml b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/stats/npcs/19000-19099.xml
index e684733153..43ec23bd28 100644
--- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/stats/npcs/19000-19099.xml
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/stats/npcs/19000-19099.xml
@@ -366,7 +366,7 @@
-
+
@@ -403,7 +403,7 @@
-
+
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/zones/teleportzones.xml b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/zones/teleportzones.xml
index bbf62a120d..3d4ff454c9 100644
--- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/zones/teleportzones.xml
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/zones/teleportzones.xml
@@ -154,6 +154,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/Routes.xml b/L2J_Mobius_5.0_Salvation/dist/game/data/Routes.xml
index 8727b0389a..b971a893a6 100644
--- a/L2J_Mobius_5.0_Salvation/dist/game/data/Routes.xml
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/Routes.xml
@@ -2476,6 +2476,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsCoralGarden.xml b/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsCoralGarden.xml
new file mode 100644
index 0000000000..00ecfbdb7d
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsCoralGarden.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml b/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
new file mode 100644
index 0000000000..57900c0c1e
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
@@ -0,0 +1,2511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsSteamCorridor.xml b/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
new file mode 100644
index 0000000000..2f12da9071
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
@@ -0,0 +1,694 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
new file mode 100644
index 0000000000..35880d0254
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
@@ -0,0 +1,215 @@
+/*
+ * 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 .
+ */
+package ai.areas.Parnassus.EntrancePortalToCrystalCaverns;
+
+import java.util.Calendar;
+
+import org.l2jmobius.gameserver.instancemanager.QuestManager;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.quest.Quest;
+import org.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+
+import ai.AbstractNpcAI;
+import instances.CrystalCaverns.CrystalCavernsCoralGarden;
+import instances.CrystalCaverns.CrystalCavernsEmeraldSquare;
+import instances.CrystalCaverns.CrystalCavernsSteamCorridor;
+
+/**
+ * Entrance Portal to Crystal Caverns AI.
+ * @author St3eT
+ */
+public class EntrancePortalToCrystalCaverns extends AbstractNpcAI
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ // Misc
+ private static final int EMERALD_SQUARE_TEMPLATE_ID = 163;
+ private static final int STEAM_CORRIDOR_TEMPLATE_ID = 164;
+ private static final int CORAL_GARDEN_TEMPLATE_ID = 165;
+ private static final int PRISON_ENTRACE_TRIGGER_1 = 24230010;
+ private static final int PRISON_ENTRACE_TRIGGER_2 = 24230012;
+ private static final int CAVERNS_ENTRACE_TRIGGER_1 = 24230014;
+ private static final int CAVERNS_ENTRACE_TRIGGER_2 = 24230016;
+ private static final int CAVERNS_ENTRACE_TRIGGER_3 = 24230018;
+
+ private EntrancePortalToCrystalCaverns()
+ {
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(CAVERNS_ENTRACE);
+ addSpawnId(CAVERNS_ENTRACE);
+ addSeeCreatureId(CAVERNS_ENTRACE);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ Quest instanceScript = null;
+
+ switch (getCurrentInstanceTemplateId())
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsEmeraldSquare.class.getSimpleName());
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsSteamCorridor.class.getSimpleName());
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsCoralGarden.class.getSimpleName());
+ break;
+ }
+ }
+
+ if (instanceScript != null)
+ {
+ instanceScript.notifyEvent(event, npc, player);
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ return "EntrancePortal_" + getCurrentInstanceTemplateId() + ".html";
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ getTimers().addRepeatingTimer("LOOP_TIMER", 10000, npc, null);
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("LOOP_TIMER"))
+ {
+ final int currentTemplateId = getCurrentInstanceTemplateId();
+
+ World.getInstance().forEachVisibleObjectInRange(npc, PlayerInstance.class, 500, p ->
+ {
+ updateTriggersForPlayer(player, currentTemplateId);
+ });
+ }
+ }
+
+ @Override
+ public String onSeeCreature(Npc npc, Creature creature, boolean isSummon)
+ {
+ if (creature.isPlayer())
+ {
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_1, true));
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_2, true));
+ updateTriggersForPlayer(creature.getActingPlayer(), getCurrentInstanceTemplateId());
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ public void updateTriggersForPlayer(PlayerInstance player, int currentTemplateId)
+ {
+ if (player != null)
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, false));
+
+ switch (currentTemplateId)
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, true));
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, true));
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, true));
+ break;
+ }
+ }
+ }
+ }
+
+ public int getCurrentInstanceTemplateId()
+ {
+ final int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
+ final int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ int templateId = -1;
+
+ switch (day)
+ {
+ case Calendar.MONDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.TUESDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.WEDNESDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.THURSDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.FRIDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SATURDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SUNDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ }
+ return templateId;
+ }
+
+ public static void main(String[] args)
+ {
+ new EntrancePortalToCrystalCaverns();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
new file mode 100644
index 0000000000..6de9f6e4b9
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Emerald Square.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
new file mode 100644
index 0000000000..b0d8310c68
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Steam Corridor.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
new file mode 100644
index 0000000000..ea7efec129
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Coral Garden.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
new file mode 100644
index 0000000000..21d8f0d1b3
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
@@ -0,0 +1,253 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.instancemanager.WalkingManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.Spawn;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Coral Garden instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsCoralGarden extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int MICHAELA_NORMAL = 25799;
+ private static final int MICHAELA_WISE = 26116;
+ private static final int MICHAELA_WEALTHY = 26115;
+ private static final int MICHAELA_ARMED = 26114;
+ private static final int GOLEM_1 = 19013; // Crystalline Golem
+ private static final int GOLEM_2 = 19014; // Crystalline Golem
+ // Location
+ private static final Location BOSS_LOC = new Location(144307, 220032, -11824);
+ // Misc
+ private static final int TEMPLATE_ID = 165;
+ private static final int BOSS_DOOR_ID = 24240026;
+ private static final int PLAYER_MAX_DISTANCE = 250;
+
+ public CrystalCavernsCoralGarden()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(GOLEM_1, GOLEM_2);
+ addKillId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addAttackId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addRouteFinishedId(GOLEM_1, GOLEM_2);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "SUCCESS_TIMER":
+ {
+ showOnScreenMsg(instance, NpcStringId.GOLEM_LOCATION_SUCCESSFUL_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ break;
+ }
+ case "LOOP_TIMER":
+ {
+ player = npcVars.getObject("PLAYER_OBJECT", PlayerInstance.class);
+
+ if ((player != null) && (npc.calculateDistance3D(player) > PLAYER_MAX_DISTANCE) && npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ addMoveToDesire(npc, new Location(npc.getX() + getRandom(-100, 100), npc.getY() + getRandom(-150, 150), npc.getZ()), 23);
+ npc.setRunning();
+ npcVars.set("NPC_FOLLOWING", false);
+ getTimers().cancelTimer("LOOP_TIMER", npc, null);
+ getTimers().addTimer("FAIL_TIMER", 5000, npc, null);
+ }
+ break;
+ }
+ case "FAIL_TIMER":
+ {
+ final Spawn spawn = npc.getSpawn();
+
+ if (!npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ npc.setWalking();
+ npc.teleToLocation(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ());
+ npc.setScriptValue(0);
+ npc.setNameString(null);
+ npc.setTitleString(null);
+ npc.setTitle(null);
+ npc.broadcastInfo();
+ }
+ npcVars.set("CAN_CALL_MONSTERS", ((spawn.getX() - ((npc.getX() * spawn.getX()) - npc.getX())) + (spawn.getY() - (npc.getY() * spawn.getY()) - npc.getY())) > (200 * 200));
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.getVariables().set("PLAYER_OBJECT", player);
+ npc.setNameString(NpcStringId.TRAITOR_CRYSTALLINE_GOLEM);
+ npc.setTitleString(NpcStringId.GIVEN_TO_S1);
+ npc.setTitle(player.getName());
+ npc.broadcastInfo();
+ WalkingManager.getInstance().startMoving(npc, npc.getId() == GOLEM_1 ? "gd_golem_1" : "gd_golem_2");
+ getTimers().addRepeatingTimer("LOOP_TIMER", 500, npc, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onRouteFinished(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (instance != null)
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ showOnScreenMsg(instance, NpcStringId.GOLEM_ENTERED_THE_REQUIRED_ZONE, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ npc.deleteMe();
+
+ if (instance.getAliveNpcs(GOLEM_1, GOLEM_2).isEmpty())
+ {
+ instance.openCloseDoor(BOSS_DOOR_ID, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = MICHAELA_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = MICHAELA_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = MICHAELA_WEALTHY;
+ }
+ else
+ {
+ bossId = MICHAELA_ARMED;
+ }
+
+ final Npc boss = addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ getTimers().addTimer("SUCCESS_TIMER", 5000, boss, null);
+ }
+ }
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ instance.openCloseDoor(BOSS_DOOR_ID, false);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsCoralGarden();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
new file mode 100644
index 0000000000..26bb4ad5c8
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
@@ -0,0 +1,424 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.MonsterInstance;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.skills.Skill;
+import org.l2jmobius.gameserver.model.stats.Stats;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Emerald Square instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsEmeraldSquare extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int VERIDAN_NORMAL = 25796;
+ private static final int VERIDAN_WISE = 26107;
+ private static final int VERIDAN_WEALTHY = 26106;
+ private static final int VERIDAN_ARMED = 26105;
+ private static final int WATER_CANNON = 19008;
+ private static final int WATER_CANNON_SKILL = 19009;
+ private static final int STRONGHOLD_PROTECTOR = 23012;
+ private static final int SQUARE_INTRUDER = 23010;
+ private static final int SQUARE_ATTACKER = 23011;
+ // Skills
+ private static final SkillHolder DESTROY_SKILL = new SkillHolder(12003, 1);
+ private static final SkillHolder WATER_CANNON_SKILL_ATTACK = new SkillHolder(14179, 1);
+ // Locations
+ private static final Location[] BOSS_SPAWNS =
+ {
+ new Location(152745, 145957, -12584, 16446),
+ new Location(152816, 145968, -12633, 16446),
+ };
+ // Misc
+ private static final int TEMPLATE_ID = 163;
+ private static final int RAID_DOOR_1 = 24220005;
+ private static final int RAID_DOOR_2 = 24220006;
+
+ public CrystalCavernsEmeraldSquare()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addSpawnId(WATER_CANNON);
+ addKillId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addAttackId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addSpellFinishedId(WATER_CANNON_SKILL);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, WATER_CANNON);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final StatsSet npcVars = npc.getVariables();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (event)
+ {
+ case "HP_REGEN_TIMER":
+ {
+ int value = ((baseId == 5) || (baseId == 6)) ? 5 : baseId;
+ npc.getStat().addFixedValue(Stats.REGENERATE_HP_RATE, Double.valueOf(value * 1000));
+ break;
+ }
+ case "SUPPORT_SPAWN_TIMER":
+ {
+ int supportVal = npcVars.getInt("SUPPORT_VALUE", 0);
+
+ if (supportVal > 3)
+ {
+ return;
+ }
+
+ if ((supportVal == 0) || (supportVal == 1) || (supportVal == 2))
+ {
+ final String spawnName = npcParams.getString("SupportMaker" + (supportVal + 1), null);
+ if (spawnName != null)
+ {
+ instance.spawnGroup(spawnName);
+ }
+ npcVars.increaseInt("SUPPORT_VALUE", 1);
+ }
+
+ if (!npcVars.getBoolean("PREVIOUS_BASE_DESTROYED", false))
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", 60000, npc, null);
+ }
+ break;
+ }
+ case "CANNON_LOOP_ATTACK":
+ {
+ if (npc.getCurrentHpPercent() > 30)
+ {
+ if (npcVars.getBoolean("IS_DESTROY_ACTIVATED", false) || (getRandom(10) < 2))
+ {
+ final Npc cannonSkill = addSpawn(WATER_CANNON_SKILL, npc, true, 0, false, instance.getId());
+ addSkillCastDesire(cannonSkill, cannonSkill, WATER_CANNON_SKILL_ATTACK, 23);
+ }
+ }
+ break;
+ }
+ case "SUICIDE_TIMER":
+ {
+ npc.doDie(null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (npc.getId())
+ {
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (!npcVars.getBoolean("CLOSED_DOORS", false))
+ {
+ npcVars.set("CLOSED_DOORS", true);
+ instance.openCloseDoor(RAID_DOOR_2, false);
+ }
+ }
+ case WATER_CANNON:
+ {
+ if ((skill != null) && (skill.getId() == DESTROY_SKILL.getSkillId()) && !npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npcVars.set("IS_DESTROY_ACTIVATED", true);
+ npc.setDisplayEffect(2);
+ getTimers().addTimer("SUICIDE_TIMER", 60000, npc, null);
+ }
+
+ if (npc.getCurrentHpPercent() < 30)
+ {
+ if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(3);
+
+ }
+ }
+ else if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (npc.getId())
+ {
+ case VERIDAN_NORMAL:
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (instance.getAliveNpcs(VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED).isEmpty())
+ {
+ instance.finishInstance();
+ }
+ else
+ {
+ instance.setReenterTime();
+ }
+ break;
+ }
+ case WATER_CANNON:
+ {
+ npc.setDisplayEffect(4);
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_S1, ExShowScreenMessage.MIDDLE_CENTER, 4000, String.valueOf(npc.getParameters().getInt("base_id", -1)));
+
+ World.getInstance().forEachVisibleObjectInRange(npc, MonsterInstance.class, 400, monster ->
+ {
+ if ((monster.getId() == STRONGHOLD_PROTECTOR) || (monster.getId() == SQUARE_INTRUDER) || (monster.getId() == SQUARE_ATTACKER))
+ {
+ monster.doDie(null);
+ }
+ });
+
+ instance.getAliveNpcs(WATER_CANNON).forEach(cannon ->
+ {
+ final int cannonBaseId = cannon.getParameters().getInt("base_id", -1);
+
+ switch (baseId)
+ {
+ case 1:
+ {
+ if (cannonBaseId == 2)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (cannonBaseId == 3)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 3:
+ {
+ if (cannonBaseId == 4)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 4:
+ {
+ if ((cannonBaseId == 5) || (cannonBaseId == 6))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 5:
+ case 6:
+ {
+ if (cannonBaseId == 7)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 7:
+ {
+ if ((cannonBaseId == 8) || (cannonBaseId == 9))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ }
+ });
+
+ if ((baseId == 8) || (baseId == 9))
+ {
+ instance.getParameters().increaseInt("MAIN_TARGETS_KILLED", 0, 1);
+
+ if (instance.getParameters().getInt("MAIN_TARGETS_KILLED", 0) == 2)
+ {
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.openCloseDoor(RAID_DOOR_1, true);
+ instance.openCloseDoor(RAID_DOOR_2, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = VERIDAN_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = VERIDAN_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = VERIDAN_WEALTHY;
+ }
+ else
+ {
+ bossId = VERIDAN_ARMED;
+ }
+
+ for (Location loc : BOSS_SPAWNS)
+ {
+ addSpawn(bossId, loc, false, 0, false, instance.getId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onSpellFinished(Npc npc, PlayerInstance player, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance) && (npc.getId() == WATER_CANNON_SKILL) && (skill.getId() == WATER_CANNON_SKILL_ATTACK.getSkillId()))
+ {
+ npc.deleteMe();
+ }
+ return super.onSpellFinished(npc, player, skill);
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case WATER_CANNON:
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ if (baseId != 1)
+ {
+ npc.setTargetable(false);
+ }
+
+ getTimers().addTimer("HP_REGEN_TIMER", 10000, npc, null);
+
+ if (baseId > 0)
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", (baseId * 60) * 1000, npc, null);
+ }
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ public void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance world = npc.getInstanceWorld();
+
+ if ((world != null) && creature.isPlayer() && npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.setDisplayEffect(1);
+ getTimers().addRepeatingTimer("CANNON_LOOP_ATTACK", 1000, npc, null);
+ }
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsEmeraldSquare();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
new file mode 100644
index 0000000000..23f045e3fd
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
@@ -0,0 +1,408 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.enums.ChatType;
+import org.l2jmobius.gameserver.instancemanager.ZoneManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.zone.ZoneType;
+import org.l2jmobius.gameserver.model.zone.type.TeleportZone;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Steam Corridor instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsSteamCorridor extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int KECHI_NORMAL = 25797;
+ private static final int KECHI_WISE = 26113;
+ private static final int KECHI_WEALTHY = 26112;
+ private static final int KECHI_ARMED = 26111;
+ private static final int VICIOUS_DUELER = 23014;
+ private static final int VICIOUS_WARRIOR = 23016;
+ private static final int VICIOUS_SWORDSMAN = 23015;
+ private static final int SPIRIT_PROTECTOR = 23013;
+ private static final int FIRE_REGION = 19161;
+ private static final int PLAYER_DETECTOR = 19075;
+ private static final int TRAP_1 = 19011;
+ private static final int TRAP_2 = 19012;
+ // Skills
+ private static final SkillHolder FIRE_SKILL_1 = new SkillHolder(14373, 1);
+ private static final SkillHolder FIRE_SKILL_2 = new SkillHolder(14373, 2);
+ private static final SkillHolder FIRE_SKILL_3 = new SkillHolder(14197, 1);
+ private static final SkillHolder TRAP_SKILL_1 = new SkillHolder(14180, 1);
+ private static final SkillHolder TRAP_SKILL_2 = new SkillHolder(14181, 1);
+ private static final SkillHolder TRAP_SKILL_3 = new SkillHolder(14372, 1);
+ // Location
+ private static final Location BOSS_LOC = new Location(154078, 215125, -12140);
+ // Misc
+ private static final int TEMPLATE_ID = 164;
+
+ public CrystalCavernsSteamCorridor()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addAttackId(TRAP_1, TRAP_2);
+ addKillId(VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, KECHI_NORMAL, KECHI_WISE, KECHI_WEALTHY, KECHI_ARMED);
+ addSpawnId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, FIRE_REGION, PLAYER_DETECTOR);
+ addEventReceivedId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN);
+ addInstanceCreatedId(TEMPLATE_ID);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, PLAYER_DETECTOR);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "FIRE_REGION_TIMER_1":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_1, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_2", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_2":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_2, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_3":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_3, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 1000, npc, null);
+ break;
+ }
+ case "TRAP_REACT_TIMER":
+ {
+ final int timer = npcVars.increaseInt("TIMER_VAL", -1);
+ if (timer > 0)
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, " " + timer);
+ }
+ else
+ {
+ if (npc.getId() == TRAP_1)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_1 : TRAP_SKILL_3), 23);
+ }
+ else if (npc.getId() == TRAP_2)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_2 : TRAP_SKILL_3), 23);
+ }
+ }
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceCreated(Instance instance, PlayerInstance player)
+ {
+ instance.setStatus(1);
+ for (int i = 0; i < 6; i++)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName("24_24_fire_telezone_0" + i, TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(false, instance.getId());
+ }
+ }
+ super.onInstanceCreated(instance, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case SPIRIT_PROTECTOR:
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ npc.setTargetable(false);
+ npc.disableCoreAI(true);
+ npc.setInvisible(true);
+ break;
+ }
+ case FIRE_REGION:
+ {
+ final int timeLimit = npcParams.getInt("Limit_Time", 0);
+ if (timeLimit > 0)
+ {
+ getTimers().addTimer("FIRE_REGION_TIMER_1", ((timeLimit * 30) * 100), npc, null);
+ }
+ npc.setTargetable(false);
+ npc.setIsInvul(true);
+ npc.setRandomAnimation(false);
+ npc.setRandomWalking(false);
+ npc.disableCoreAI(true);
+ break;
+ }
+ case PLAYER_DETECTOR:
+ {
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onEventReceived(String eventName, Npc sender, Npc receiver, WorldObject reference)
+ {
+ final Instance instance = receiver.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = receiver.getParameters();
+
+ if (eventName.equals(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0))))
+ {
+ receiver.setTargetable(true);
+ receiver.disableCoreAI(false);
+ receiver.setInvisible(false);
+ }
+ }
+ return super.onEventReceived(eventName, sender, receiver, reference);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case TRAP_1:
+ case TRAP_2:
+ {
+ if (npc.isScriptValue(0))
+ {
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ npc.setScriptValue(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int killTarget = instance.getParameters().getInt("KILL_TARGET", 5);
+ int currentKillCount = instance.getParameters().getInt("KILL_COUNT", 0);
+
+ switch (npc.getId())
+ {
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ if (npcParams.getInt("last_checker", 0) == 1)
+ {
+ currentKillCount = instance.getParameters().increaseInt("KILL_COUNT", 0, 1);
+
+ if (currentKillCount >= killTarget)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName(npc.getParameters().getString("AreaTeleName"), TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(true, instance.getId());
+ showOnScreenMsg(instance, NpcStringId.THE_PORTAL_TO_THE_NEXT_ROOM_IS_NOW_OPEN, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.spawnGroup("innadril23_mb2422_pt" + instance.getStatus() + "m1");
+ instance.getParameters().set("KILL_COUNT", 0);
+
+ switch (instance.getStatus())
+ {
+ case 1:
+ {
+ instance.getParameters().set("KILL_TARGET", 12);
+ instance.setStatus(2);
+ break;
+ }
+ case 2:
+ {
+ instance.getParameters().set("KILL_TARGET", 3);
+ instance.setStatus(3);
+ break;
+ }
+ case 3:
+ {
+ instance.getParameters().set("KILL_TARGET", 18);
+ instance.setStatus(4);
+ break;
+ }
+ case 4:
+ {
+ instance.getParameters().set("KILL_TARGET", 5);
+ instance.setStatus(5);
+ break;
+ }
+ case 5:
+ {
+ instance.getParameters().set("KILL_TARGET", 20);
+ instance.setStatus(6);
+ break;
+ }
+ case 6:
+ {
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = KECHI_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = KECHI_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = KECHI_WEALTHY;
+ }
+ else
+ {
+ bossId = KECHI_ARMED;
+ }
+
+ addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOGGER.warning("Cannot find teleport zone for Crystal Cavern: Steam Corridor instance!!!");
+ }
+ }
+ }
+ break;
+ }
+ case KECHI_NORMAL:
+ case KECHI_WISE:
+ case KECHI_WEALTHY:
+ case KECHI_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ private void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance instance = npc.getInstanceWorld();
+
+ if (isInInstance(instance) && creature.isPlayer())
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case PLAYER_DETECTOR:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.broadcastEvent(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0)), 2000, null);
+
+ for (int i = 0; i < getRandom(5); i++)
+ {
+ final Npc trap = addSpawn(((npcParams.getInt("MobType", 0) == 0) ? TRAP_1 : TRAP_2), npc, true, 0, false, instance.getId());
+ trap.getVariables().set("TIMER_VAL", 4);
+ }
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsSteamCorridor();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
new file mode 100644
index 0000000000..efc886f117
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+You must be in a party to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
new file mode 100644
index 0000000000..dfa8583852
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+The leader of the party should try to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/stats/npcs/19000-19099.xml b/L2J_Mobius_5.0_Salvation/dist/game/data/stats/npcs/19000-19099.xml
index e684733153..43ec23bd28 100644
--- a/L2J_Mobius_5.0_Salvation/dist/game/data/stats/npcs/19000-19099.xml
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/stats/npcs/19000-19099.xml
@@ -366,7 +366,7 @@
-
+
@@ -403,7 +403,7 @@
-
+
diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/zones/teleportzones.xml b/L2J_Mobius_5.0_Salvation/dist/game/data/zones/teleportzones.xml
index 58d7790d91..417a27bcf4 100644
--- a/L2J_Mobius_5.0_Salvation/dist/game/data/zones/teleportzones.xml
+++ b/L2J_Mobius_5.0_Salvation/dist/game/data/zones/teleportzones.xml
@@ -154,6 +154,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/Routes.xml b/L2J_Mobius_5.5_EtinasFate/dist/game/data/Routes.xml
index 8727b0389a..b971a893a6 100644
--- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/Routes.xml
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/Routes.xml
@@ -2476,6 +2476,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsCoralGarden.xml b/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsCoralGarden.xml
new file mode 100644
index 0000000000..00ecfbdb7d
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsCoralGarden.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml b/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
new file mode 100644
index 0000000000..57900c0c1e
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
@@ -0,0 +1,2511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsSteamCorridor.xml b/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
new file mode 100644
index 0000000000..2f12da9071
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
@@ -0,0 +1,694 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
new file mode 100644
index 0000000000..35880d0254
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
@@ -0,0 +1,215 @@
+/*
+ * 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 .
+ */
+package ai.areas.Parnassus.EntrancePortalToCrystalCaverns;
+
+import java.util.Calendar;
+
+import org.l2jmobius.gameserver.instancemanager.QuestManager;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.quest.Quest;
+import org.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+
+import ai.AbstractNpcAI;
+import instances.CrystalCaverns.CrystalCavernsCoralGarden;
+import instances.CrystalCaverns.CrystalCavernsEmeraldSquare;
+import instances.CrystalCaverns.CrystalCavernsSteamCorridor;
+
+/**
+ * Entrance Portal to Crystal Caverns AI.
+ * @author St3eT
+ */
+public class EntrancePortalToCrystalCaverns extends AbstractNpcAI
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ // Misc
+ private static final int EMERALD_SQUARE_TEMPLATE_ID = 163;
+ private static final int STEAM_CORRIDOR_TEMPLATE_ID = 164;
+ private static final int CORAL_GARDEN_TEMPLATE_ID = 165;
+ private static final int PRISON_ENTRACE_TRIGGER_1 = 24230010;
+ private static final int PRISON_ENTRACE_TRIGGER_2 = 24230012;
+ private static final int CAVERNS_ENTRACE_TRIGGER_1 = 24230014;
+ private static final int CAVERNS_ENTRACE_TRIGGER_2 = 24230016;
+ private static final int CAVERNS_ENTRACE_TRIGGER_3 = 24230018;
+
+ private EntrancePortalToCrystalCaverns()
+ {
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(CAVERNS_ENTRACE);
+ addSpawnId(CAVERNS_ENTRACE);
+ addSeeCreatureId(CAVERNS_ENTRACE);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ Quest instanceScript = null;
+
+ switch (getCurrentInstanceTemplateId())
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsEmeraldSquare.class.getSimpleName());
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsSteamCorridor.class.getSimpleName());
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsCoralGarden.class.getSimpleName());
+ break;
+ }
+ }
+
+ if (instanceScript != null)
+ {
+ instanceScript.notifyEvent(event, npc, player);
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ return "EntrancePortal_" + getCurrentInstanceTemplateId() + ".html";
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ getTimers().addRepeatingTimer("LOOP_TIMER", 10000, npc, null);
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("LOOP_TIMER"))
+ {
+ final int currentTemplateId = getCurrentInstanceTemplateId();
+
+ World.getInstance().forEachVisibleObjectInRange(npc, PlayerInstance.class, 500, p ->
+ {
+ updateTriggersForPlayer(player, currentTemplateId);
+ });
+ }
+ }
+
+ @Override
+ public String onSeeCreature(Npc npc, Creature creature, boolean isSummon)
+ {
+ if (creature.isPlayer())
+ {
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_1, true));
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_2, true));
+ updateTriggersForPlayer(creature.getActingPlayer(), getCurrentInstanceTemplateId());
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ public void updateTriggersForPlayer(PlayerInstance player, int currentTemplateId)
+ {
+ if (player != null)
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, false));
+
+ switch (currentTemplateId)
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, true));
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, true));
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, true));
+ break;
+ }
+ }
+ }
+ }
+
+ public int getCurrentInstanceTemplateId()
+ {
+ final int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
+ final int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ int templateId = -1;
+
+ switch (day)
+ {
+ case Calendar.MONDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.TUESDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.WEDNESDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.THURSDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.FRIDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SATURDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SUNDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ }
+ return templateId;
+ }
+
+ public static void main(String[] args)
+ {
+ new EntrancePortalToCrystalCaverns();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
new file mode 100644
index 0000000000..6de9f6e4b9
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Emerald Square.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
new file mode 100644
index 0000000000..b0d8310c68
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Steam Corridor.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
new file mode 100644
index 0000000000..ea7efec129
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Coral Garden.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
new file mode 100644
index 0000000000..21d8f0d1b3
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
@@ -0,0 +1,253 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.instancemanager.WalkingManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.Spawn;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Coral Garden instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsCoralGarden extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int MICHAELA_NORMAL = 25799;
+ private static final int MICHAELA_WISE = 26116;
+ private static final int MICHAELA_WEALTHY = 26115;
+ private static final int MICHAELA_ARMED = 26114;
+ private static final int GOLEM_1 = 19013; // Crystalline Golem
+ private static final int GOLEM_2 = 19014; // Crystalline Golem
+ // Location
+ private static final Location BOSS_LOC = new Location(144307, 220032, -11824);
+ // Misc
+ private static final int TEMPLATE_ID = 165;
+ private static final int BOSS_DOOR_ID = 24240026;
+ private static final int PLAYER_MAX_DISTANCE = 250;
+
+ public CrystalCavernsCoralGarden()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(GOLEM_1, GOLEM_2);
+ addKillId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addAttackId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addRouteFinishedId(GOLEM_1, GOLEM_2);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "SUCCESS_TIMER":
+ {
+ showOnScreenMsg(instance, NpcStringId.GOLEM_LOCATION_SUCCESSFUL_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ break;
+ }
+ case "LOOP_TIMER":
+ {
+ player = npcVars.getObject("PLAYER_OBJECT", PlayerInstance.class);
+
+ if ((player != null) && (npc.calculateDistance3D(player) > PLAYER_MAX_DISTANCE) && npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ addMoveToDesire(npc, new Location(npc.getX() + getRandom(-100, 100), npc.getY() + getRandom(-150, 150), npc.getZ()), 23);
+ npc.setRunning();
+ npcVars.set("NPC_FOLLOWING", false);
+ getTimers().cancelTimer("LOOP_TIMER", npc, null);
+ getTimers().addTimer("FAIL_TIMER", 5000, npc, null);
+ }
+ break;
+ }
+ case "FAIL_TIMER":
+ {
+ final Spawn spawn = npc.getSpawn();
+
+ if (!npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ npc.setWalking();
+ npc.teleToLocation(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ());
+ npc.setScriptValue(0);
+ npc.setNameString(null);
+ npc.setTitleString(null);
+ npc.setTitle(null);
+ npc.broadcastInfo();
+ }
+ npcVars.set("CAN_CALL_MONSTERS", ((spawn.getX() - ((npc.getX() * spawn.getX()) - npc.getX())) + (spawn.getY() - (npc.getY() * spawn.getY()) - npc.getY())) > (200 * 200));
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.getVariables().set("PLAYER_OBJECT", player);
+ npc.setNameString(NpcStringId.TRAITOR_CRYSTALLINE_GOLEM);
+ npc.setTitleString(NpcStringId.GIVEN_TO_S1);
+ npc.setTitle(player.getName());
+ npc.broadcastInfo();
+ WalkingManager.getInstance().startMoving(npc, npc.getId() == GOLEM_1 ? "gd_golem_1" : "gd_golem_2");
+ getTimers().addRepeatingTimer("LOOP_TIMER", 500, npc, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onRouteFinished(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (instance != null)
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ showOnScreenMsg(instance, NpcStringId.GOLEM_ENTERED_THE_REQUIRED_ZONE, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ npc.deleteMe();
+
+ if (instance.getAliveNpcs(GOLEM_1, GOLEM_2).isEmpty())
+ {
+ instance.openCloseDoor(BOSS_DOOR_ID, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = MICHAELA_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = MICHAELA_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = MICHAELA_WEALTHY;
+ }
+ else
+ {
+ bossId = MICHAELA_ARMED;
+ }
+
+ final Npc boss = addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ getTimers().addTimer("SUCCESS_TIMER", 5000, boss, null);
+ }
+ }
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ instance.openCloseDoor(BOSS_DOOR_ID, false);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsCoralGarden();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
new file mode 100644
index 0000000000..26bb4ad5c8
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
@@ -0,0 +1,424 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.MonsterInstance;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.skills.Skill;
+import org.l2jmobius.gameserver.model.stats.Stats;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Emerald Square instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsEmeraldSquare extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int VERIDAN_NORMAL = 25796;
+ private static final int VERIDAN_WISE = 26107;
+ private static final int VERIDAN_WEALTHY = 26106;
+ private static final int VERIDAN_ARMED = 26105;
+ private static final int WATER_CANNON = 19008;
+ private static final int WATER_CANNON_SKILL = 19009;
+ private static final int STRONGHOLD_PROTECTOR = 23012;
+ private static final int SQUARE_INTRUDER = 23010;
+ private static final int SQUARE_ATTACKER = 23011;
+ // Skills
+ private static final SkillHolder DESTROY_SKILL = new SkillHolder(12003, 1);
+ private static final SkillHolder WATER_CANNON_SKILL_ATTACK = new SkillHolder(14179, 1);
+ // Locations
+ private static final Location[] BOSS_SPAWNS =
+ {
+ new Location(152745, 145957, -12584, 16446),
+ new Location(152816, 145968, -12633, 16446),
+ };
+ // Misc
+ private static final int TEMPLATE_ID = 163;
+ private static final int RAID_DOOR_1 = 24220005;
+ private static final int RAID_DOOR_2 = 24220006;
+
+ public CrystalCavernsEmeraldSquare()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addSpawnId(WATER_CANNON);
+ addKillId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addAttackId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addSpellFinishedId(WATER_CANNON_SKILL);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, WATER_CANNON);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final StatsSet npcVars = npc.getVariables();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (event)
+ {
+ case "HP_REGEN_TIMER":
+ {
+ int value = ((baseId == 5) || (baseId == 6)) ? 5 : baseId;
+ npc.getStat().addFixedValue(Stats.REGENERATE_HP_RATE, Double.valueOf(value * 1000));
+ break;
+ }
+ case "SUPPORT_SPAWN_TIMER":
+ {
+ int supportVal = npcVars.getInt("SUPPORT_VALUE", 0);
+
+ if (supportVal > 3)
+ {
+ return;
+ }
+
+ if ((supportVal == 0) || (supportVal == 1) || (supportVal == 2))
+ {
+ final String spawnName = npcParams.getString("SupportMaker" + (supportVal + 1), null);
+ if (spawnName != null)
+ {
+ instance.spawnGroup(spawnName);
+ }
+ npcVars.increaseInt("SUPPORT_VALUE", 1);
+ }
+
+ if (!npcVars.getBoolean("PREVIOUS_BASE_DESTROYED", false))
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", 60000, npc, null);
+ }
+ break;
+ }
+ case "CANNON_LOOP_ATTACK":
+ {
+ if (npc.getCurrentHpPercent() > 30)
+ {
+ if (npcVars.getBoolean("IS_DESTROY_ACTIVATED", false) || (getRandom(10) < 2))
+ {
+ final Npc cannonSkill = addSpawn(WATER_CANNON_SKILL, npc, true, 0, false, instance.getId());
+ addSkillCastDesire(cannonSkill, cannonSkill, WATER_CANNON_SKILL_ATTACK, 23);
+ }
+ }
+ break;
+ }
+ case "SUICIDE_TIMER":
+ {
+ npc.doDie(null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (npc.getId())
+ {
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (!npcVars.getBoolean("CLOSED_DOORS", false))
+ {
+ npcVars.set("CLOSED_DOORS", true);
+ instance.openCloseDoor(RAID_DOOR_2, false);
+ }
+ }
+ case WATER_CANNON:
+ {
+ if ((skill != null) && (skill.getId() == DESTROY_SKILL.getSkillId()) && !npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npcVars.set("IS_DESTROY_ACTIVATED", true);
+ npc.setDisplayEffect(2);
+ getTimers().addTimer("SUICIDE_TIMER", 60000, npc, null);
+ }
+
+ if (npc.getCurrentHpPercent() < 30)
+ {
+ if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(3);
+
+ }
+ }
+ else if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (npc.getId())
+ {
+ case VERIDAN_NORMAL:
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (instance.getAliveNpcs(VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED).isEmpty())
+ {
+ instance.finishInstance();
+ }
+ else
+ {
+ instance.setReenterTime();
+ }
+ break;
+ }
+ case WATER_CANNON:
+ {
+ npc.setDisplayEffect(4);
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_S1, ExShowScreenMessage.MIDDLE_CENTER, 4000, String.valueOf(npc.getParameters().getInt("base_id", -1)));
+
+ World.getInstance().forEachVisibleObjectInRange(npc, MonsterInstance.class, 400, monster ->
+ {
+ if ((monster.getId() == STRONGHOLD_PROTECTOR) || (monster.getId() == SQUARE_INTRUDER) || (monster.getId() == SQUARE_ATTACKER))
+ {
+ monster.doDie(null);
+ }
+ });
+
+ instance.getAliveNpcs(WATER_CANNON).forEach(cannon ->
+ {
+ final int cannonBaseId = cannon.getParameters().getInt("base_id", -1);
+
+ switch (baseId)
+ {
+ case 1:
+ {
+ if (cannonBaseId == 2)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (cannonBaseId == 3)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 3:
+ {
+ if (cannonBaseId == 4)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 4:
+ {
+ if ((cannonBaseId == 5) || (cannonBaseId == 6))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 5:
+ case 6:
+ {
+ if (cannonBaseId == 7)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 7:
+ {
+ if ((cannonBaseId == 8) || (cannonBaseId == 9))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ }
+ });
+
+ if ((baseId == 8) || (baseId == 9))
+ {
+ instance.getParameters().increaseInt("MAIN_TARGETS_KILLED", 0, 1);
+
+ if (instance.getParameters().getInt("MAIN_TARGETS_KILLED", 0) == 2)
+ {
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.openCloseDoor(RAID_DOOR_1, true);
+ instance.openCloseDoor(RAID_DOOR_2, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = VERIDAN_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = VERIDAN_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = VERIDAN_WEALTHY;
+ }
+ else
+ {
+ bossId = VERIDAN_ARMED;
+ }
+
+ for (Location loc : BOSS_SPAWNS)
+ {
+ addSpawn(bossId, loc, false, 0, false, instance.getId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onSpellFinished(Npc npc, PlayerInstance player, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance) && (npc.getId() == WATER_CANNON_SKILL) && (skill.getId() == WATER_CANNON_SKILL_ATTACK.getSkillId()))
+ {
+ npc.deleteMe();
+ }
+ return super.onSpellFinished(npc, player, skill);
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case WATER_CANNON:
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ if (baseId != 1)
+ {
+ npc.setTargetable(false);
+ }
+
+ getTimers().addTimer("HP_REGEN_TIMER", 10000, npc, null);
+
+ if (baseId > 0)
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", (baseId * 60) * 1000, npc, null);
+ }
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ public void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance world = npc.getInstanceWorld();
+
+ if ((world != null) && creature.isPlayer() && npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.setDisplayEffect(1);
+ getTimers().addRepeatingTimer("CANNON_LOOP_ATTACK", 1000, npc, null);
+ }
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsEmeraldSquare();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
new file mode 100644
index 0000000000..23f045e3fd
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
@@ -0,0 +1,408 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.enums.ChatType;
+import org.l2jmobius.gameserver.instancemanager.ZoneManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.zone.ZoneType;
+import org.l2jmobius.gameserver.model.zone.type.TeleportZone;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Steam Corridor instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsSteamCorridor extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int KECHI_NORMAL = 25797;
+ private static final int KECHI_WISE = 26113;
+ private static final int KECHI_WEALTHY = 26112;
+ private static final int KECHI_ARMED = 26111;
+ private static final int VICIOUS_DUELER = 23014;
+ private static final int VICIOUS_WARRIOR = 23016;
+ private static final int VICIOUS_SWORDSMAN = 23015;
+ private static final int SPIRIT_PROTECTOR = 23013;
+ private static final int FIRE_REGION = 19161;
+ private static final int PLAYER_DETECTOR = 19075;
+ private static final int TRAP_1 = 19011;
+ private static final int TRAP_2 = 19012;
+ // Skills
+ private static final SkillHolder FIRE_SKILL_1 = new SkillHolder(14373, 1);
+ private static final SkillHolder FIRE_SKILL_2 = new SkillHolder(14373, 2);
+ private static final SkillHolder FIRE_SKILL_3 = new SkillHolder(14197, 1);
+ private static final SkillHolder TRAP_SKILL_1 = new SkillHolder(14180, 1);
+ private static final SkillHolder TRAP_SKILL_2 = new SkillHolder(14181, 1);
+ private static final SkillHolder TRAP_SKILL_3 = new SkillHolder(14372, 1);
+ // Location
+ private static final Location BOSS_LOC = new Location(154078, 215125, -12140);
+ // Misc
+ private static final int TEMPLATE_ID = 164;
+
+ public CrystalCavernsSteamCorridor()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addAttackId(TRAP_1, TRAP_2);
+ addKillId(VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, KECHI_NORMAL, KECHI_WISE, KECHI_WEALTHY, KECHI_ARMED);
+ addSpawnId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, FIRE_REGION, PLAYER_DETECTOR);
+ addEventReceivedId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN);
+ addInstanceCreatedId(TEMPLATE_ID);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, PLAYER_DETECTOR);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "FIRE_REGION_TIMER_1":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_1, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_2", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_2":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_2, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_3":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_3, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 1000, npc, null);
+ break;
+ }
+ case "TRAP_REACT_TIMER":
+ {
+ final int timer = npcVars.increaseInt("TIMER_VAL", -1);
+ if (timer > 0)
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, " " + timer);
+ }
+ else
+ {
+ if (npc.getId() == TRAP_1)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_1 : TRAP_SKILL_3), 23);
+ }
+ else if (npc.getId() == TRAP_2)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_2 : TRAP_SKILL_3), 23);
+ }
+ }
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceCreated(Instance instance, PlayerInstance player)
+ {
+ instance.setStatus(1);
+ for (int i = 0; i < 6; i++)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName("24_24_fire_telezone_0" + i, TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(false, instance.getId());
+ }
+ }
+ super.onInstanceCreated(instance, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case SPIRIT_PROTECTOR:
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ npc.setTargetable(false);
+ npc.disableCoreAI(true);
+ npc.setInvisible(true);
+ break;
+ }
+ case FIRE_REGION:
+ {
+ final int timeLimit = npcParams.getInt("Limit_Time", 0);
+ if (timeLimit > 0)
+ {
+ getTimers().addTimer("FIRE_REGION_TIMER_1", ((timeLimit * 30) * 100), npc, null);
+ }
+ npc.setTargetable(false);
+ npc.setIsInvul(true);
+ npc.setRandomAnimation(false);
+ npc.setRandomWalking(false);
+ npc.disableCoreAI(true);
+ break;
+ }
+ case PLAYER_DETECTOR:
+ {
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onEventReceived(String eventName, Npc sender, Npc receiver, WorldObject reference)
+ {
+ final Instance instance = receiver.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = receiver.getParameters();
+
+ if (eventName.equals(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0))))
+ {
+ receiver.setTargetable(true);
+ receiver.disableCoreAI(false);
+ receiver.setInvisible(false);
+ }
+ }
+ return super.onEventReceived(eventName, sender, receiver, reference);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case TRAP_1:
+ case TRAP_2:
+ {
+ if (npc.isScriptValue(0))
+ {
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ npc.setScriptValue(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int killTarget = instance.getParameters().getInt("KILL_TARGET", 5);
+ int currentKillCount = instance.getParameters().getInt("KILL_COUNT", 0);
+
+ switch (npc.getId())
+ {
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ if (npcParams.getInt("last_checker", 0) == 1)
+ {
+ currentKillCount = instance.getParameters().increaseInt("KILL_COUNT", 0, 1);
+
+ if (currentKillCount >= killTarget)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName(npc.getParameters().getString("AreaTeleName"), TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(true, instance.getId());
+ showOnScreenMsg(instance, NpcStringId.THE_PORTAL_TO_THE_NEXT_ROOM_IS_NOW_OPEN, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.spawnGroup("innadril23_mb2422_pt" + instance.getStatus() + "m1");
+ instance.getParameters().set("KILL_COUNT", 0);
+
+ switch (instance.getStatus())
+ {
+ case 1:
+ {
+ instance.getParameters().set("KILL_TARGET", 12);
+ instance.setStatus(2);
+ break;
+ }
+ case 2:
+ {
+ instance.getParameters().set("KILL_TARGET", 3);
+ instance.setStatus(3);
+ break;
+ }
+ case 3:
+ {
+ instance.getParameters().set("KILL_TARGET", 18);
+ instance.setStatus(4);
+ break;
+ }
+ case 4:
+ {
+ instance.getParameters().set("KILL_TARGET", 5);
+ instance.setStatus(5);
+ break;
+ }
+ case 5:
+ {
+ instance.getParameters().set("KILL_TARGET", 20);
+ instance.setStatus(6);
+ break;
+ }
+ case 6:
+ {
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = KECHI_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = KECHI_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = KECHI_WEALTHY;
+ }
+ else
+ {
+ bossId = KECHI_ARMED;
+ }
+
+ addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOGGER.warning("Cannot find teleport zone for Crystal Cavern: Steam Corridor instance!!!");
+ }
+ }
+ }
+ break;
+ }
+ case KECHI_NORMAL:
+ case KECHI_WISE:
+ case KECHI_WEALTHY:
+ case KECHI_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ private void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance instance = npc.getInstanceWorld();
+
+ if (isInInstance(instance) && creature.isPlayer())
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case PLAYER_DETECTOR:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.broadcastEvent(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0)), 2000, null);
+
+ for (int i = 0; i < getRandom(5); i++)
+ {
+ final Npc trap = addSpawn(((npcParams.getInt("MobType", 0) == 0) ? TRAP_1 : TRAP_2), npc, true, 0, false, instance.getId());
+ trap.getVariables().set("TIMER_VAL", 4);
+ }
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsSteamCorridor();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
new file mode 100644
index 0000000000..efc886f117
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+You must be in a party to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
new file mode 100644
index 0000000000..dfa8583852
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+The leader of the party should try to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/stats/npcs/19000-19099.xml b/L2J_Mobius_5.5_EtinasFate/dist/game/data/stats/npcs/19000-19099.xml
index e684733153..43ec23bd28 100644
--- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/stats/npcs/19000-19099.xml
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/stats/npcs/19000-19099.xml
@@ -366,7 +366,7 @@
-
+
@@ -403,7 +403,7 @@
-
+
diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/zones/teleportzones.xml b/L2J_Mobius_5.5_EtinasFate/dist/game/data/zones/teleportzones.xml
index 58d7790d91..417a27bcf4 100644
--- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/zones/teleportzones.xml
+++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/zones/teleportzones.xml
@@ -154,6 +154,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/Routes.xml b/L2J_Mobius_6.0_Fafurion/dist/game/data/Routes.xml
index 8727b0389a..b971a893a6 100644
--- a/L2J_Mobius_6.0_Fafurion/dist/game/data/Routes.xml
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/Routes.xml
@@ -2476,6 +2476,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsCoralGarden.xml b/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsCoralGarden.xml
new file mode 100644
index 0000000000..00ecfbdb7d
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsCoralGarden.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml b/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
new file mode 100644
index 0000000000..57900c0c1e
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
@@ -0,0 +1,2511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsSteamCorridor.xml b/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
new file mode 100644
index 0000000000..2f12da9071
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
@@ -0,0 +1,694 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
new file mode 100644
index 0000000000..35880d0254
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
@@ -0,0 +1,215 @@
+/*
+ * 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 .
+ */
+package ai.areas.Parnassus.EntrancePortalToCrystalCaverns;
+
+import java.util.Calendar;
+
+import org.l2jmobius.gameserver.instancemanager.QuestManager;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.quest.Quest;
+import org.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+
+import ai.AbstractNpcAI;
+import instances.CrystalCaverns.CrystalCavernsCoralGarden;
+import instances.CrystalCaverns.CrystalCavernsEmeraldSquare;
+import instances.CrystalCaverns.CrystalCavernsSteamCorridor;
+
+/**
+ * Entrance Portal to Crystal Caverns AI.
+ * @author St3eT
+ */
+public class EntrancePortalToCrystalCaverns extends AbstractNpcAI
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ // Misc
+ private static final int EMERALD_SQUARE_TEMPLATE_ID = 163;
+ private static final int STEAM_CORRIDOR_TEMPLATE_ID = 164;
+ private static final int CORAL_GARDEN_TEMPLATE_ID = 165;
+ private static final int PRISON_ENTRACE_TRIGGER_1 = 24230010;
+ private static final int PRISON_ENTRACE_TRIGGER_2 = 24230012;
+ private static final int CAVERNS_ENTRACE_TRIGGER_1 = 24230014;
+ private static final int CAVERNS_ENTRACE_TRIGGER_2 = 24230016;
+ private static final int CAVERNS_ENTRACE_TRIGGER_3 = 24230018;
+
+ private EntrancePortalToCrystalCaverns()
+ {
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(CAVERNS_ENTRACE);
+ addSpawnId(CAVERNS_ENTRACE);
+ addSeeCreatureId(CAVERNS_ENTRACE);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ Quest instanceScript = null;
+
+ switch (getCurrentInstanceTemplateId())
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsEmeraldSquare.class.getSimpleName());
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsSteamCorridor.class.getSimpleName());
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsCoralGarden.class.getSimpleName());
+ break;
+ }
+ }
+
+ if (instanceScript != null)
+ {
+ instanceScript.notifyEvent(event, npc, player);
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ return "EntrancePortal_" + getCurrentInstanceTemplateId() + ".html";
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ getTimers().addRepeatingTimer("LOOP_TIMER", 10000, npc, null);
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("LOOP_TIMER"))
+ {
+ final int currentTemplateId = getCurrentInstanceTemplateId();
+
+ World.getInstance().forEachVisibleObjectInRange(npc, PlayerInstance.class, 500, p ->
+ {
+ updateTriggersForPlayer(player, currentTemplateId);
+ });
+ }
+ }
+
+ @Override
+ public String onSeeCreature(Npc npc, Creature creature, boolean isSummon)
+ {
+ if (creature.isPlayer())
+ {
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_1, true));
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_2, true));
+ updateTriggersForPlayer(creature.getActingPlayer(), getCurrentInstanceTemplateId());
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ public void updateTriggersForPlayer(PlayerInstance player, int currentTemplateId)
+ {
+ if (player != null)
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, false));
+
+ switch (currentTemplateId)
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, true));
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, true));
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, true));
+ break;
+ }
+ }
+ }
+ }
+
+ public int getCurrentInstanceTemplateId()
+ {
+ final int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
+ final int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ int templateId = -1;
+
+ switch (day)
+ {
+ case Calendar.MONDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.TUESDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.WEDNESDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.THURSDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.FRIDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SATURDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SUNDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ }
+ return templateId;
+ }
+
+ public static void main(String[] args)
+ {
+ new EntrancePortalToCrystalCaverns();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
new file mode 100644
index 0000000000..6de9f6e4b9
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Emerald Square.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
new file mode 100644
index 0000000000..b0d8310c68
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Steam Corridor.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
new file mode 100644
index 0000000000..ea7efec129
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Coral Garden.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
new file mode 100644
index 0000000000..21d8f0d1b3
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
@@ -0,0 +1,253 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.instancemanager.WalkingManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.Spawn;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Coral Garden instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsCoralGarden extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int MICHAELA_NORMAL = 25799;
+ private static final int MICHAELA_WISE = 26116;
+ private static final int MICHAELA_WEALTHY = 26115;
+ private static final int MICHAELA_ARMED = 26114;
+ private static final int GOLEM_1 = 19013; // Crystalline Golem
+ private static final int GOLEM_2 = 19014; // Crystalline Golem
+ // Location
+ private static final Location BOSS_LOC = new Location(144307, 220032, -11824);
+ // Misc
+ private static final int TEMPLATE_ID = 165;
+ private static final int BOSS_DOOR_ID = 24240026;
+ private static final int PLAYER_MAX_DISTANCE = 250;
+
+ public CrystalCavernsCoralGarden()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(GOLEM_1, GOLEM_2);
+ addKillId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addAttackId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addRouteFinishedId(GOLEM_1, GOLEM_2);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "SUCCESS_TIMER":
+ {
+ showOnScreenMsg(instance, NpcStringId.GOLEM_LOCATION_SUCCESSFUL_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ break;
+ }
+ case "LOOP_TIMER":
+ {
+ player = npcVars.getObject("PLAYER_OBJECT", PlayerInstance.class);
+
+ if ((player != null) && (npc.calculateDistance3D(player) > PLAYER_MAX_DISTANCE) && npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ addMoveToDesire(npc, new Location(npc.getX() + getRandom(-100, 100), npc.getY() + getRandom(-150, 150), npc.getZ()), 23);
+ npc.setRunning();
+ npcVars.set("NPC_FOLLOWING", false);
+ getTimers().cancelTimer("LOOP_TIMER", npc, null);
+ getTimers().addTimer("FAIL_TIMER", 5000, npc, null);
+ }
+ break;
+ }
+ case "FAIL_TIMER":
+ {
+ final Spawn spawn = npc.getSpawn();
+
+ if (!npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ npc.setWalking();
+ npc.teleToLocation(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ());
+ npc.setScriptValue(0);
+ npc.setNameString(null);
+ npc.setTitleString(null);
+ npc.setTitle(null);
+ npc.broadcastInfo();
+ }
+ npcVars.set("CAN_CALL_MONSTERS", ((spawn.getX() - ((npc.getX() * spawn.getX()) - npc.getX())) + (spawn.getY() - (npc.getY() * spawn.getY()) - npc.getY())) > (200 * 200));
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.getVariables().set("PLAYER_OBJECT", player);
+ npc.setNameString(NpcStringId.TRAITOR_CRYSTALLINE_GOLEM);
+ npc.setTitleString(NpcStringId.GIVEN_TO_S1);
+ npc.setTitle(player.getName());
+ npc.broadcastInfo();
+ WalkingManager.getInstance().startMoving(npc, npc.getId() == GOLEM_1 ? "gd_golem_1" : "gd_golem_2");
+ getTimers().addRepeatingTimer("LOOP_TIMER", 500, npc, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onRouteFinished(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (instance != null)
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ showOnScreenMsg(instance, NpcStringId.GOLEM_ENTERED_THE_REQUIRED_ZONE, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ npc.deleteMe();
+
+ if (instance.getAliveNpcs(GOLEM_1, GOLEM_2).isEmpty())
+ {
+ instance.openCloseDoor(BOSS_DOOR_ID, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = MICHAELA_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = MICHAELA_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = MICHAELA_WEALTHY;
+ }
+ else
+ {
+ bossId = MICHAELA_ARMED;
+ }
+
+ final Npc boss = addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ getTimers().addTimer("SUCCESS_TIMER", 5000, boss, null);
+ }
+ }
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ instance.openCloseDoor(BOSS_DOOR_ID, false);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsCoralGarden();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
new file mode 100644
index 0000000000..26bb4ad5c8
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
@@ -0,0 +1,424 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.MonsterInstance;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.skills.Skill;
+import org.l2jmobius.gameserver.model.stats.Stats;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Emerald Square instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsEmeraldSquare extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int VERIDAN_NORMAL = 25796;
+ private static final int VERIDAN_WISE = 26107;
+ private static final int VERIDAN_WEALTHY = 26106;
+ private static final int VERIDAN_ARMED = 26105;
+ private static final int WATER_CANNON = 19008;
+ private static final int WATER_CANNON_SKILL = 19009;
+ private static final int STRONGHOLD_PROTECTOR = 23012;
+ private static final int SQUARE_INTRUDER = 23010;
+ private static final int SQUARE_ATTACKER = 23011;
+ // Skills
+ private static final SkillHolder DESTROY_SKILL = new SkillHolder(12003, 1);
+ private static final SkillHolder WATER_CANNON_SKILL_ATTACK = new SkillHolder(14179, 1);
+ // Locations
+ private static final Location[] BOSS_SPAWNS =
+ {
+ new Location(152745, 145957, -12584, 16446),
+ new Location(152816, 145968, -12633, 16446),
+ };
+ // Misc
+ private static final int TEMPLATE_ID = 163;
+ private static final int RAID_DOOR_1 = 24220005;
+ private static final int RAID_DOOR_2 = 24220006;
+
+ public CrystalCavernsEmeraldSquare()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addSpawnId(WATER_CANNON);
+ addKillId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addAttackId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addSpellFinishedId(WATER_CANNON_SKILL);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, WATER_CANNON);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final StatsSet npcVars = npc.getVariables();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (event)
+ {
+ case "HP_REGEN_TIMER":
+ {
+ int value = ((baseId == 5) || (baseId == 6)) ? 5 : baseId;
+ npc.getStat().addFixedValue(Stats.REGENERATE_HP_RATE, Double.valueOf(value * 1000));
+ break;
+ }
+ case "SUPPORT_SPAWN_TIMER":
+ {
+ int supportVal = npcVars.getInt("SUPPORT_VALUE", 0);
+
+ if (supportVal > 3)
+ {
+ return;
+ }
+
+ if ((supportVal == 0) || (supportVal == 1) || (supportVal == 2))
+ {
+ final String spawnName = npcParams.getString("SupportMaker" + (supportVal + 1), null);
+ if (spawnName != null)
+ {
+ instance.spawnGroup(spawnName);
+ }
+ npcVars.increaseInt("SUPPORT_VALUE", 1);
+ }
+
+ if (!npcVars.getBoolean("PREVIOUS_BASE_DESTROYED", false))
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", 60000, npc, null);
+ }
+ break;
+ }
+ case "CANNON_LOOP_ATTACK":
+ {
+ if (npc.getCurrentHpPercent() > 30)
+ {
+ if (npcVars.getBoolean("IS_DESTROY_ACTIVATED", false) || (getRandom(10) < 2))
+ {
+ final Npc cannonSkill = addSpawn(WATER_CANNON_SKILL, npc, true, 0, false, instance.getId());
+ addSkillCastDesire(cannonSkill, cannonSkill, WATER_CANNON_SKILL_ATTACK, 23);
+ }
+ }
+ break;
+ }
+ case "SUICIDE_TIMER":
+ {
+ npc.doDie(null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (npc.getId())
+ {
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (!npcVars.getBoolean("CLOSED_DOORS", false))
+ {
+ npcVars.set("CLOSED_DOORS", true);
+ instance.openCloseDoor(RAID_DOOR_2, false);
+ }
+ }
+ case WATER_CANNON:
+ {
+ if ((skill != null) && (skill.getId() == DESTROY_SKILL.getSkillId()) && !npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npcVars.set("IS_DESTROY_ACTIVATED", true);
+ npc.setDisplayEffect(2);
+ getTimers().addTimer("SUICIDE_TIMER", 60000, npc, null);
+ }
+
+ if (npc.getCurrentHpPercent() < 30)
+ {
+ if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(3);
+
+ }
+ }
+ else if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (npc.getId())
+ {
+ case VERIDAN_NORMAL:
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (instance.getAliveNpcs(VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED).isEmpty())
+ {
+ instance.finishInstance();
+ }
+ else
+ {
+ instance.setReenterTime();
+ }
+ break;
+ }
+ case WATER_CANNON:
+ {
+ npc.setDisplayEffect(4);
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_S1, ExShowScreenMessage.MIDDLE_CENTER, 4000, String.valueOf(npc.getParameters().getInt("base_id", -1)));
+
+ World.getInstance().forEachVisibleObjectInRange(npc, MonsterInstance.class, 400, monster ->
+ {
+ if ((monster.getId() == STRONGHOLD_PROTECTOR) || (monster.getId() == SQUARE_INTRUDER) || (monster.getId() == SQUARE_ATTACKER))
+ {
+ monster.doDie(null);
+ }
+ });
+
+ instance.getAliveNpcs(WATER_CANNON).forEach(cannon ->
+ {
+ final int cannonBaseId = cannon.getParameters().getInt("base_id", -1);
+
+ switch (baseId)
+ {
+ case 1:
+ {
+ if (cannonBaseId == 2)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (cannonBaseId == 3)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 3:
+ {
+ if (cannonBaseId == 4)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 4:
+ {
+ if ((cannonBaseId == 5) || (cannonBaseId == 6))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 5:
+ case 6:
+ {
+ if (cannonBaseId == 7)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 7:
+ {
+ if ((cannonBaseId == 8) || (cannonBaseId == 9))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ }
+ });
+
+ if ((baseId == 8) || (baseId == 9))
+ {
+ instance.getParameters().increaseInt("MAIN_TARGETS_KILLED", 0, 1);
+
+ if (instance.getParameters().getInt("MAIN_TARGETS_KILLED", 0) == 2)
+ {
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.openCloseDoor(RAID_DOOR_1, true);
+ instance.openCloseDoor(RAID_DOOR_2, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = VERIDAN_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = VERIDAN_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = VERIDAN_WEALTHY;
+ }
+ else
+ {
+ bossId = VERIDAN_ARMED;
+ }
+
+ for (Location loc : BOSS_SPAWNS)
+ {
+ addSpawn(bossId, loc, false, 0, false, instance.getId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onSpellFinished(Npc npc, PlayerInstance player, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance) && (npc.getId() == WATER_CANNON_SKILL) && (skill.getId() == WATER_CANNON_SKILL_ATTACK.getSkillId()))
+ {
+ npc.deleteMe();
+ }
+ return super.onSpellFinished(npc, player, skill);
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case WATER_CANNON:
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ if (baseId != 1)
+ {
+ npc.setTargetable(false);
+ }
+
+ getTimers().addTimer("HP_REGEN_TIMER", 10000, npc, null);
+
+ if (baseId > 0)
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", (baseId * 60) * 1000, npc, null);
+ }
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ public void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance world = npc.getInstanceWorld();
+
+ if ((world != null) && creature.isPlayer() && npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.setDisplayEffect(1);
+ getTimers().addRepeatingTimer("CANNON_LOOP_ATTACK", 1000, npc, null);
+ }
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsEmeraldSquare();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
new file mode 100644
index 0000000000..23f045e3fd
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
@@ -0,0 +1,408 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.enums.ChatType;
+import org.l2jmobius.gameserver.instancemanager.ZoneManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.zone.ZoneType;
+import org.l2jmobius.gameserver.model.zone.type.TeleportZone;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Steam Corridor instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsSteamCorridor extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int KECHI_NORMAL = 25797;
+ private static final int KECHI_WISE = 26113;
+ private static final int KECHI_WEALTHY = 26112;
+ private static final int KECHI_ARMED = 26111;
+ private static final int VICIOUS_DUELER = 23014;
+ private static final int VICIOUS_WARRIOR = 23016;
+ private static final int VICIOUS_SWORDSMAN = 23015;
+ private static final int SPIRIT_PROTECTOR = 23013;
+ private static final int FIRE_REGION = 19161;
+ private static final int PLAYER_DETECTOR = 19075;
+ private static final int TRAP_1 = 19011;
+ private static final int TRAP_2 = 19012;
+ // Skills
+ private static final SkillHolder FIRE_SKILL_1 = new SkillHolder(14373, 1);
+ private static final SkillHolder FIRE_SKILL_2 = new SkillHolder(14373, 2);
+ private static final SkillHolder FIRE_SKILL_3 = new SkillHolder(14197, 1);
+ private static final SkillHolder TRAP_SKILL_1 = new SkillHolder(14180, 1);
+ private static final SkillHolder TRAP_SKILL_2 = new SkillHolder(14181, 1);
+ private static final SkillHolder TRAP_SKILL_3 = new SkillHolder(14372, 1);
+ // Location
+ private static final Location BOSS_LOC = new Location(154078, 215125, -12140);
+ // Misc
+ private static final int TEMPLATE_ID = 164;
+
+ public CrystalCavernsSteamCorridor()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addAttackId(TRAP_1, TRAP_2);
+ addKillId(VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, KECHI_NORMAL, KECHI_WISE, KECHI_WEALTHY, KECHI_ARMED);
+ addSpawnId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, FIRE_REGION, PLAYER_DETECTOR);
+ addEventReceivedId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN);
+ addInstanceCreatedId(TEMPLATE_ID);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, PLAYER_DETECTOR);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "FIRE_REGION_TIMER_1":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_1, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_2", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_2":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_2, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_3":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_3, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 1000, npc, null);
+ break;
+ }
+ case "TRAP_REACT_TIMER":
+ {
+ final int timer = npcVars.increaseInt("TIMER_VAL", -1);
+ if (timer > 0)
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, " " + timer);
+ }
+ else
+ {
+ if (npc.getId() == TRAP_1)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_1 : TRAP_SKILL_3), 23);
+ }
+ else if (npc.getId() == TRAP_2)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_2 : TRAP_SKILL_3), 23);
+ }
+ }
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceCreated(Instance instance, PlayerInstance player)
+ {
+ instance.setStatus(1);
+ for (int i = 0; i < 6; i++)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName("24_24_fire_telezone_0" + i, TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(false, instance.getId());
+ }
+ }
+ super.onInstanceCreated(instance, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case SPIRIT_PROTECTOR:
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ npc.setTargetable(false);
+ npc.disableCoreAI(true);
+ npc.setInvisible(true);
+ break;
+ }
+ case FIRE_REGION:
+ {
+ final int timeLimit = npcParams.getInt("Limit_Time", 0);
+ if (timeLimit > 0)
+ {
+ getTimers().addTimer("FIRE_REGION_TIMER_1", ((timeLimit * 30) * 100), npc, null);
+ }
+ npc.setTargetable(false);
+ npc.setIsInvul(true);
+ npc.setRandomAnimation(false);
+ npc.setRandomWalking(false);
+ npc.disableCoreAI(true);
+ break;
+ }
+ case PLAYER_DETECTOR:
+ {
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onEventReceived(String eventName, Npc sender, Npc receiver, WorldObject reference)
+ {
+ final Instance instance = receiver.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = receiver.getParameters();
+
+ if (eventName.equals(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0))))
+ {
+ receiver.setTargetable(true);
+ receiver.disableCoreAI(false);
+ receiver.setInvisible(false);
+ }
+ }
+ return super.onEventReceived(eventName, sender, receiver, reference);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case TRAP_1:
+ case TRAP_2:
+ {
+ if (npc.isScriptValue(0))
+ {
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ npc.setScriptValue(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int killTarget = instance.getParameters().getInt("KILL_TARGET", 5);
+ int currentKillCount = instance.getParameters().getInt("KILL_COUNT", 0);
+
+ switch (npc.getId())
+ {
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ if (npcParams.getInt("last_checker", 0) == 1)
+ {
+ currentKillCount = instance.getParameters().increaseInt("KILL_COUNT", 0, 1);
+
+ if (currentKillCount >= killTarget)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName(npc.getParameters().getString("AreaTeleName"), TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(true, instance.getId());
+ showOnScreenMsg(instance, NpcStringId.THE_PORTAL_TO_THE_NEXT_ROOM_IS_NOW_OPEN, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.spawnGroup("innadril23_mb2422_pt" + instance.getStatus() + "m1");
+ instance.getParameters().set("KILL_COUNT", 0);
+
+ switch (instance.getStatus())
+ {
+ case 1:
+ {
+ instance.getParameters().set("KILL_TARGET", 12);
+ instance.setStatus(2);
+ break;
+ }
+ case 2:
+ {
+ instance.getParameters().set("KILL_TARGET", 3);
+ instance.setStatus(3);
+ break;
+ }
+ case 3:
+ {
+ instance.getParameters().set("KILL_TARGET", 18);
+ instance.setStatus(4);
+ break;
+ }
+ case 4:
+ {
+ instance.getParameters().set("KILL_TARGET", 5);
+ instance.setStatus(5);
+ break;
+ }
+ case 5:
+ {
+ instance.getParameters().set("KILL_TARGET", 20);
+ instance.setStatus(6);
+ break;
+ }
+ case 6:
+ {
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = KECHI_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = KECHI_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = KECHI_WEALTHY;
+ }
+ else
+ {
+ bossId = KECHI_ARMED;
+ }
+
+ addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOGGER.warning("Cannot find teleport zone for Crystal Cavern: Steam Corridor instance!!!");
+ }
+ }
+ }
+ break;
+ }
+ case KECHI_NORMAL:
+ case KECHI_WISE:
+ case KECHI_WEALTHY:
+ case KECHI_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ private void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance instance = npc.getInstanceWorld();
+
+ if (isInInstance(instance) && creature.isPlayer())
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case PLAYER_DETECTOR:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.broadcastEvent(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0)), 2000, null);
+
+ for (int i = 0; i < getRandom(5); i++)
+ {
+ final Npc trap = addSpawn(((npcParams.getInt("MobType", 0) == 0) ? TRAP_1 : TRAP_2), npc, true, 0, false, instance.getId());
+ trap.getVariables().set("TIMER_VAL", 4);
+ }
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsSteamCorridor();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
new file mode 100644
index 0000000000..efc886f117
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+You must be in a party to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
new file mode 100644
index 0000000000..dfa8583852
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+The leader of the party should try to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/stats/npcs/19000-19099.xml b/L2J_Mobius_6.0_Fafurion/dist/game/data/stats/npcs/19000-19099.xml
index d1a15f1afa..47a6497a67 100644
--- a/L2J_Mobius_6.0_Fafurion/dist/game/data/stats/npcs/19000-19099.xml
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/stats/npcs/19000-19099.xml
@@ -366,7 +366,7 @@
-
+
@@ -403,7 +403,7 @@
-
+
diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/zones/teleportzones.xml b/L2J_Mobius_6.0_Fafurion/dist/game/data/zones/teleportzones.xml
index 58d7790d91..417a27bcf4 100644
--- a/L2J_Mobius_6.0_Fafurion/dist/game/data/zones/teleportzones.xml
+++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/zones/teleportzones.xml
@@ -154,6 +154,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/Routes.xml b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/Routes.xml
index 8727b0389a..b971a893a6 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/Routes.xml
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/Routes.xml
@@ -2476,6 +2476,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsCoralGarden.xml b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsCoralGarden.xml
new file mode 100644
index 0000000000..00ecfbdb7d
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsCoralGarden.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
new file mode 100644
index 0000000000..57900c0c1e
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsEmeraldSquare.xml
@@ -0,0 +1,2511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsSteamCorridor.xml b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
new file mode 100644
index 0000000000..2f12da9071
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/instances/CrystalCavernsSteamCorridor.xml
@@ -0,0 +1,694 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
new file mode 100644
index 0000000000..35880d0254
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortalToCrystalCaverns.java
@@ -0,0 +1,215 @@
+/*
+ * 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 .
+ */
+package ai.areas.Parnassus.EntrancePortalToCrystalCaverns;
+
+import java.util.Calendar;
+
+import org.l2jmobius.gameserver.instancemanager.QuestManager;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.quest.Quest;
+import org.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+
+import ai.AbstractNpcAI;
+import instances.CrystalCaverns.CrystalCavernsCoralGarden;
+import instances.CrystalCaverns.CrystalCavernsEmeraldSquare;
+import instances.CrystalCaverns.CrystalCavernsSteamCorridor;
+
+/**
+ * Entrance Portal to Crystal Caverns AI.
+ * @author St3eT
+ */
+public class EntrancePortalToCrystalCaverns extends AbstractNpcAI
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ // Misc
+ private static final int EMERALD_SQUARE_TEMPLATE_ID = 163;
+ private static final int STEAM_CORRIDOR_TEMPLATE_ID = 164;
+ private static final int CORAL_GARDEN_TEMPLATE_ID = 165;
+ private static final int PRISON_ENTRACE_TRIGGER_1 = 24230010;
+ private static final int PRISON_ENTRACE_TRIGGER_2 = 24230012;
+ private static final int CAVERNS_ENTRACE_TRIGGER_1 = 24230014;
+ private static final int CAVERNS_ENTRACE_TRIGGER_2 = 24230016;
+ private static final int CAVERNS_ENTRACE_TRIGGER_3 = 24230018;
+
+ private EntrancePortalToCrystalCaverns()
+ {
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(CAVERNS_ENTRACE);
+ addSpawnId(CAVERNS_ENTRACE);
+ addSeeCreatureId(CAVERNS_ENTRACE);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ Quest instanceScript = null;
+
+ switch (getCurrentInstanceTemplateId())
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsEmeraldSquare.class.getSimpleName());
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsSteamCorridor.class.getSimpleName());
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ instanceScript = QuestManager.getInstance().getQuest(CrystalCavernsCoralGarden.class.getSimpleName());
+ break;
+ }
+ }
+
+ if (instanceScript != null)
+ {
+ instanceScript.notifyEvent(event, npc, player);
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ return "EntrancePortal_" + getCurrentInstanceTemplateId() + ".html";
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ getTimers().addRepeatingTimer("LOOP_TIMER", 10000, npc, null);
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("LOOP_TIMER"))
+ {
+ final int currentTemplateId = getCurrentInstanceTemplateId();
+
+ World.getInstance().forEachVisibleObjectInRange(npc, PlayerInstance.class, 500, p ->
+ {
+ updateTriggersForPlayer(player, currentTemplateId);
+ });
+ }
+ }
+
+ @Override
+ public String onSeeCreature(Npc npc, Creature creature, boolean isSummon)
+ {
+ if (creature.isPlayer())
+ {
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_1, true));
+ creature.getActingPlayer().sendPacket(new OnEventTrigger(PRISON_ENTRACE_TRIGGER_2, true));
+ updateTriggersForPlayer(creature.getActingPlayer(), getCurrentInstanceTemplateId());
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ public void updateTriggersForPlayer(PlayerInstance player, int currentTemplateId)
+ {
+ if (player != null)
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, false));
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, false));
+
+ switch (currentTemplateId)
+ {
+ case EMERALD_SQUARE_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_1, true));
+ break;
+ }
+ case STEAM_CORRIDOR_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_2, true));
+ break;
+ }
+ case CORAL_GARDEN_TEMPLATE_ID:
+ {
+ player.sendPacket(new OnEventTrigger(CAVERNS_ENTRACE_TRIGGER_3, true));
+ break;
+ }
+ }
+ }
+ }
+
+ public int getCurrentInstanceTemplateId()
+ {
+ final int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
+ final int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ int templateId = -1;
+
+ switch (day)
+ {
+ case Calendar.MONDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.TUESDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.WEDNESDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.THURSDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.FRIDAY:
+ {
+ templateId = (hour < 18) ? CORAL_GARDEN_TEMPLATE_ID : EMERALD_SQUARE_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SATURDAY:
+ {
+ templateId = (hour < 18) ? STEAM_CORRIDOR_TEMPLATE_ID : CORAL_GARDEN_TEMPLATE_ID;
+ break;
+ }
+ case Calendar.SUNDAY:
+ {
+ templateId = (hour < 18) ? EMERALD_SQUARE_TEMPLATE_ID : STEAM_CORRIDOR_TEMPLATE_ID;
+ break;
+ }
+ }
+ return templateId;
+ }
+
+ public static void main(String[] args)
+ {
+ new EntrancePortalToCrystalCaverns();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
new file mode 100644
index 0000000000..6de9f6e4b9
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_163.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Emerald Square.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
new file mode 100644
index 0000000000..b0d8310c68
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_164.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Steam Corridor.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
new file mode 100644
index 0000000000..ea7efec129
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/Parnassus/EntrancePortalToCrystalCaverns/EntrancePortal_165.html
@@ -0,0 +1,4 @@
+Entrance Portal to Crystal Caverns:
+At this time, you can go into the Coral Garden.
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
new file mode 100644
index 0000000000..21d8f0d1b3
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsCoralGarden.java
@@ -0,0 +1,253 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.instancemanager.WalkingManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.Spawn;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Coral Garden instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsCoralGarden extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int MICHAELA_NORMAL = 25799;
+ private static final int MICHAELA_WISE = 26116;
+ private static final int MICHAELA_WEALTHY = 26115;
+ private static final int MICHAELA_ARMED = 26114;
+ private static final int GOLEM_1 = 19013; // Crystalline Golem
+ private static final int GOLEM_2 = 19014; // Crystalline Golem
+ // Location
+ private static final Location BOSS_LOC = new Location(144307, 220032, -11824);
+ // Misc
+ private static final int TEMPLATE_ID = 165;
+ private static final int BOSS_DOOR_ID = 24240026;
+ private static final int PLAYER_MAX_DISTANCE = 250;
+
+ public CrystalCavernsCoralGarden()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addFirstTalkId(GOLEM_1, GOLEM_2);
+ addKillId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addAttackId(MICHAELA_NORMAL, MICHAELA_WISE, MICHAELA_WEALTHY, MICHAELA_ARMED);
+ addRouteFinishedId(GOLEM_1, GOLEM_2);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "SUCCESS_TIMER":
+ {
+ showOnScreenMsg(instance, NpcStringId.GOLEM_LOCATION_SUCCESSFUL_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ break;
+ }
+ case "LOOP_TIMER":
+ {
+ player = npcVars.getObject("PLAYER_OBJECT", PlayerInstance.class);
+
+ if ((player != null) && (npc.calculateDistance3D(player) > PLAYER_MAX_DISTANCE) && npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ addMoveToDesire(npc, new Location(npc.getX() + getRandom(-100, 100), npc.getY() + getRandom(-150, 150), npc.getZ()), 23);
+ npc.setRunning();
+ npcVars.set("NPC_FOLLOWING", false);
+ getTimers().cancelTimer("LOOP_TIMER", npc, null);
+ getTimers().addTimer("FAIL_TIMER", 5000, npc, null);
+ }
+ break;
+ }
+ case "FAIL_TIMER":
+ {
+ final Spawn spawn = npc.getSpawn();
+
+ if (!npcVars.getBoolean("NPC_FOLLOWING", true))
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ npc.setWalking();
+ npc.teleToLocation(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ());
+ npc.setScriptValue(0);
+ npc.setNameString(null);
+ npc.setTitleString(null);
+ npc.setTitle(null);
+ npc.broadcastInfo();
+ }
+ npcVars.set("CAN_CALL_MONSTERS", ((spawn.getX() - ((npc.getX() * spawn.getX()) - npc.getX())) + (spawn.getY() - (npc.getY() * spawn.getY()) - npc.getY())) > (200 * 200));
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onFirstTalk(Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.getVariables().set("PLAYER_OBJECT", player);
+ npc.setNameString(NpcStringId.TRAITOR_CRYSTALLINE_GOLEM);
+ npc.setTitleString(NpcStringId.GIVEN_TO_S1);
+ npc.setTitle(player.getName());
+ npc.broadcastInfo();
+ WalkingManager.getInstance().startMoving(npc, npc.getId() == GOLEM_1 ? "gd_golem_1" : "gd_golem_2");
+ getTimers().addRepeatingTimer("LOOP_TIMER", 500, npc, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onRouteFinished(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (instance != null)
+ {
+ WalkingManager.getInstance().cancelMoving(npc);
+ showOnScreenMsg(instance, NpcStringId.GOLEM_ENTERED_THE_REQUIRED_ZONE, ExShowScreenMessage.MIDDLE_CENTER, 5000);
+ npc.deleteMe();
+
+ if (instance.getAliveNpcs(GOLEM_1, GOLEM_2).isEmpty())
+ {
+ instance.openCloseDoor(BOSS_DOOR_ID, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = MICHAELA_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = MICHAELA_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = MICHAELA_WEALTHY;
+ }
+ else
+ {
+ bossId = MICHAELA_ARMED;
+ }
+
+ final Npc boss = addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ getTimers().addTimer("SUCCESS_TIMER", 5000, boss, null);
+ }
+ }
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case MICHAELA_NORMAL:
+ case MICHAELA_WISE:
+ case MICHAELA_WEALTHY:
+ case MICHAELA_ARMED:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ instance.openCloseDoor(BOSS_DOOR_ID, false);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsCoralGarden();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
new file mode 100644
index 0000000000..26bb4ad5c8
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsEmeraldSquare.java
@@ -0,0 +1,424 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.World;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.MonsterInstance;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.skills.Skill;
+import org.l2jmobius.gameserver.model.stats.Stats;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Emerald Square instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsEmeraldSquare extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int VERIDAN_NORMAL = 25796;
+ private static final int VERIDAN_WISE = 26107;
+ private static final int VERIDAN_WEALTHY = 26106;
+ private static final int VERIDAN_ARMED = 26105;
+ private static final int WATER_CANNON = 19008;
+ private static final int WATER_CANNON_SKILL = 19009;
+ private static final int STRONGHOLD_PROTECTOR = 23012;
+ private static final int SQUARE_INTRUDER = 23010;
+ private static final int SQUARE_ATTACKER = 23011;
+ // Skills
+ private static final SkillHolder DESTROY_SKILL = new SkillHolder(12003, 1);
+ private static final SkillHolder WATER_CANNON_SKILL_ATTACK = new SkillHolder(14179, 1);
+ // Locations
+ private static final Location[] BOSS_SPAWNS =
+ {
+ new Location(152745, 145957, -12584, 16446),
+ new Location(152816, 145968, -12633, 16446),
+ };
+ // Misc
+ private static final int TEMPLATE_ID = 163;
+ private static final int RAID_DOOR_1 = 24220005;
+ private static final int RAID_DOOR_2 = 24220006;
+
+ public CrystalCavernsEmeraldSquare()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addSpawnId(WATER_CANNON);
+ addKillId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addAttackId(WATER_CANNON, VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED);
+ addSpellFinishedId(WATER_CANNON_SKILL);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, WATER_CANNON);
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final StatsSet npcVars = npc.getVariables();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (event)
+ {
+ case "HP_REGEN_TIMER":
+ {
+ int value = ((baseId == 5) || (baseId == 6)) ? 5 : baseId;
+ npc.getStat().addFixedValue(Stats.REGENERATE_HP_RATE, Double.valueOf(value * 1000));
+ break;
+ }
+ case "SUPPORT_SPAWN_TIMER":
+ {
+ int supportVal = npcVars.getInt("SUPPORT_VALUE", 0);
+
+ if (supportVal > 3)
+ {
+ return;
+ }
+
+ if ((supportVal == 0) || (supportVal == 1) || (supportVal == 2))
+ {
+ final String spawnName = npcParams.getString("SupportMaker" + (supportVal + 1), null);
+ if (spawnName != null)
+ {
+ instance.spawnGroup(spawnName);
+ }
+ npcVars.increaseInt("SUPPORT_VALUE", 1);
+ }
+
+ if (!npcVars.getBoolean("PREVIOUS_BASE_DESTROYED", false))
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", 60000, npc, null);
+ }
+ break;
+ }
+ case "CANNON_LOOP_ATTACK":
+ {
+ if (npc.getCurrentHpPercent() > 30)
+ {
+ if (npcVars.getBoolean("IS_DESTROY_ACTIVATED", false) || (getRandom(10) < 2))
+ {
+ final Npc cannonSkill = addSpawn(WATER_CANNON_SKILL, npc, true, 0, false, instance.getId());
+ addSkillCastDesire(cannonSkill, cannonSkill, WATER_CANNON_SKILL_ATTACK, 23);
+ }
+ }
+ break;
+ }
+ case "SUICIDE_TIMER":
+ {
+ npc.doDie(null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (npc.getId())
+ {
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (!npcVars.getBoolean("CLOSED_DOORS", false))
+ {
+ npcVars.set("CLOSED_DOORS", true);
+ instance.openCloseDoor(RAID_DOOR_2, false);
+ }
+ }
+ case WATER_CANNON:
+ {
+ if ((skill != null) && (skill.getId() == DESTROY_SKILL.getSkillId()) && !npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npcVars.set("IS_DESTROY_ACTIVATED", true);
+ npc.setDisplayEffect(2);
+ getTimers().addTimer("SUICIDE_TIMER", 60000, npc, null);
+ }
+
+ if (npc.getCurrentHpPercent() < 30)
+ {
+ if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(3);
+
+ }
+ }
+ else if (!npcVars.getBoolean("IS_DESTROY_ACTIVATED", false))
+ {
+ npc.setDisplayEffect(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ switch (npc.getId())
+ {
+ case VERIDAN_NORMAL:
+ case VERIDAN_WISE:
+ case VERIDAN_WEALTHY:
+ case VERIDAN_ARMED:
+ {
+ if (instance.getAliveNpcs(VERIDAN_NORMAL, VERIDAN_WISE, VERIDAN_WEALTHY, VERIDAN_ARMED).isEmpty())
+ {
+ instance.finishInstance();
+ }
+ else
+ {
+ instance.setReenterTime();
+ }
+ break;
+ }
+ case WATER_CANNON:
+ {
+ npc.setDisplayEffect(4);
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_S1, ExShowScreenMessage.MIDDLE_CENTER, 4000, String.valueOf(npc.getParameters().getInt("base_id", -1)));
+
+ World.getInstance().forEachVisibleObjectInRange(npc, MonsterInstance.class, 400, monster ->
+ {
+ if ((monster.getId() == STRONGHOLD_PROTECTOR) || (monster.getId() == SQUARE_INTRUDER) || (monster.getId() == SQUARE_ATTACKER))
+ {
+ monster.doDie(null);
+ }
+ });
+
+ instance.getAliveNpcs(WATER_CANNON).forEach(cannon ->
+ {
+ final int cannonBaseId = cannon.getParameters().getInt("base_id", -1);
+
+ switch (baseId)
+ {
+ case 1:
+ {
+ if (cannonBaseId == 2)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (cannonBaseId == 3)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 3:
+ {
+ if (cannonBaseId == 4)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 4:
+ {
+ if ((cannonBaseId == 5) || (cannonBaseId == 6))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 5:
+ case 6:
+ {
+ if (cannonBaseId == 7)
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ case 7:
+ {
+ if ((cannonBaseId == 8) || (cannonBaseId == 9))
+ {
+ cannon.getVariables().set("PREVIOUS_BASE_DESTROYED", true);
+ cannon.setTargetable(true);
+ }
+ break;
+ }
+ }
+ });
+
+ if ((baseId == 8) || (baseId == 9))
+ {
+ instance.getParameters().increaseInt("MAIN_TARGETS_KILLED", 0, 1);
+
+ if (instance.getParameters().getInt("MAIN_TARGETS_KILLED", 0) == 2)
+ {
+ showOnScreenMsg(instance, NpcStringId.SUCCESSFUL_DESTRUCTION_OF_STRONGHOLD_ENTRY_ACCESSED, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.openCloseDoor(RAID_DOOR_1, true);
+ instance.openCloseDoor(RAID_DOOR_2, true);
+
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = VERIDAN_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = VERIDAN_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = VERIDAN_WEALTHY;
+ }
+ else
+ {
+ bossId = VERIDAN_ARMED;
+ }
+
+ for (Location loc : BOSS_SPAWNS)
+ {
+ addSpawn(bossId, loc, false, 0, false, instance.getId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onSpellFinished(Npc npc, PlayerInstance player, Skill skill)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance) && (npc.getId() == WATER_CANNON_SKILL) && (skill.getId() == WATER_CANNON_SKILL_ATTACK.getSkillId()))
+ {
+ npc.deleteMe();
+ }
+ return super.onSpellFinished(npc, player, skill);
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case WATER_CANNON:
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int baseId = npcParams.getInt("base_id", -1);
+
+ if (baseId != 1)
+ {
+ npc.setTargetable(false);
+ }
+
+ getTimers().addTimer("HP_REGEN_TIMER", 10000, npc, null);
+
+ if (baseId > 0)
+ {
+ getTimers().addTimer("SUPPORT_SPAWN_TIMER", (baseId * 60) * 1000, npc, null);
+ }
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ public void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance world = npc.getInstanceWorld();
+
+ if ((world != null) && creature.isPlayer() && npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.setDisplayEffect(1);
+ getTimers().addRepeatingTimer("CANNON_LOOP_ATTACK", 1000, npc, null);
+ }
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsEmeraldSquare();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
new file mode 100644
index 0000000000..23f045e3fd
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/CrystalCavernsSteamCorridor.java
@@ -0,0 +1,408 @@
+/*
+ * 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 .
+ */
+package instances.CrystalCaverns;
+
+import org.l2jmobius.gameserver.enums.ChatType;
+import org.l2jmobius.gameserver.instancemanager.ZoneManager;
+import org.l2jmobius.gameserver.model.Location;
+import org.l2jmobius.gameserver.model.StatsSet;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Npc;
+import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
+import org.l2jmobius.gameserver.model.events.impl.creature.OnCreatureSee;
+import org.l2jmobius.gameserver.model.holders.SkillHolder;
+import org.l2jmobius.gameserver.model.instancezone.Instance;
+import org.l2jmobius.gameserver.model.zone.ZoneType;
+import org.l2jmobius.gameserver.model.zone.type.TeleportZone;
+import org.l2jmobius.gameserver.network.NpcStringId;
+import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+
+import instances.AbstractInstance;
+
+/**
+ * Crystal Caverns - Steam Corridor instance zone.
+ * @author St3eT
+ */
+public class CrystalCavernsSteamCorridor extends AbstractInstance
+{
+ // NPCs
+ private static final int CAVERNS_ENTRACE = 33522;
+ private static final int KECHI_NORMAL = 25797;
+ private static final int KECHI_WISE = 26113;
+ private static final int KECHI_WEALTHY = 26112;
+ private static final int KECHI_ARMED = 26111;
+ private static final int VICIOUS_DUELER = 23014;
+ private static final int VICIOUS_WARRIOR = 23016;
+ private static final int VICIOUS_SWORDSMAN = 23015;
+ private static final int SPIRIT_PROTECTOR = 23013;
+ private static final int FIRE_REGION = 19161;
+ private static final int PLAYER_DETECTOR = 19075;
+ private static final int TRAP_1 = 19011;
+ private static final int TRAP_2 = 19012;
+ // Skills
+ private static final SkillHolder FIRE_SKILL_1 = new SkillHolder(14373, 1);
+ private static final SkillHolder FIRE_SKILL_2 = new SkillHolder(14373, 2);
+ private static final SkillHolder FIRE_SKILL_3 = new SkillHolder(14197, 1);
+ private static final SkillHolder TRAP_SKILL_1 = new SkillHolder(14180, 1);
+ private static final SkillHolder TRAP_SKILL_2 = new SkillHolder(14181, 1);
+ private static final SkillHolder TRAP_SKILL_3 = new SkillHolder(14372, 1);
+ // Location
+ private static final Location BOSS_LOC = new Location(154078, 215125, -12140);
+ // Misc
+ private static final int TEMPLATE_ID = 164;
+
+ public CrystalCavernsSteamCorridor()
+ {
+ super(TEMPLATE_ID);
+ addStartNpc(CAVERNS_ENTRACE);
+ addTalkId(CAVERNS_ENTRACE);
+ addAttackId(TRAP_1, TRAP_2);
+ addKillId(VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, KECHI_NORMAL, KECHI_WISE, KECHI_WEALTHY, KECHI_ARMED);
+ addSpawnId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN, FIRE_REGION, PLAYER_DETECTOR);
+ addEventReceivedId(SPIRIT_PROTECTOR, VICIOUS_DUELER, VICIOUS_WARRIOR, VICIOUS_SWORDSMAN);
+ addInstanceCreatedId(TEMPLATE_ID);
+ addInstanceEnterId(TEMPLATE_ID);
+ addInstanceLeaveId(TEMPLATE_ID);
+ setCreatureSeeId(this::onCreatureSee, PLAYER_DETECTOR);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, Npc npc, PlayerInstance player)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcVars = npc.getVariables();
+
+ switch (event)
+ {
+ case "FIRE_REGION_TIMER_1":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_1, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_2", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_2":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_2, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 10000, npc, null);
+ break;
+ }
+ case "FIRE_REGION_TIMER_3":
+ {
+ addSkillCastDesire(npc, npc, FIRE_SKILL_3, 23);
+ getTimers().addTimer("FIRE_REGION_TIMER_3", 1000, npc, null);
+ break;
+ }
+ case "TRAP_REACT_TIMER":
+ {
+ final int timer = npcVars.increaseInt("TIMER_VAL", -1);
+ if (timer > 0)
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, " " + timer);
+ }
+ else
+ {
+ if (npc.getId() == TRAP_1)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_1 : TRAP_SKILL_3), 23);
+ }
+ else if (npc.getId() == TRAP_2)
+ {
+ addSkillCastDesire(npc, npc, (getRandom(10) < 8 ? TRAP_SKILL_2 : TRAP_SKILL_3), 23);
+ }
+ }
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, Npc npc, PlayerInstance player)
+ {
+ if (event.equals("enterInstance"))
+ {
+ enterInstance(player, npc, TEMPLATE_ID);
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public void onInstanceCreated(Instance instance, PlayerInstance player)
+ {
+ instance.setStatus(1);
+ for (int i = 0; i < 6; i++)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName("24_24_fire_telezone_0" + i, TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(false, instance.getId());
+ }
+ }
+ super.onInstanceCreated(instance, player);
+ }
+
+ @Override
+ public void onInstanceEnter(PlayerInstance player, Instance instance)
+ {
+ final int startTime = (int) (instance.getElapsedTime() / 1000);
+ final int endTime = (int) (instance.getRemainingTime() / 1000);
+ player.sendPacket(new ExSendUIEvent(player, false, true, startTime, endTime, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public void onInstanceLeave(PlayerInstance player, Instance instance)
+ {
+ player.sendPacket(new ExSendUIEvent(player, true, true, 0, 0, NpcStringId.ELAPSED_TIME));
+ }
+
+ @Override
+ public String onSpawn(Npc npc)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case SPIRIT_PROTECTOR:
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ npc.setTargetable(false);
+ npc.disableCoreAI(true);
+ npc.setInvisible(true);
+ break;
+ }
+ case FIRE_REGION:
+ {
+ final int timeLimit = npcParams.getInt("Limit_Time", 0);
+ if (timeLimit > 0)
+ {
+ getTimers().addTimer("FIRE_REGION_TIMER_1", ((timeLimit * 30) * 100), npc, null);
+ }
+ npc.setTargetable(false);
+ npc.setIsInvul(true);
+ npc.setRandomAnimation(false);
+ npc.setRandomWalking(false);
+ npc.disableCoreAI(true);
+ break;
+ }
+ case PLAYER_DETECTOR:
+ {
+ npc.initSeenCreatures();
+ break;
+ }
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onEventReceived(String eventName, Npc sender, Npc receiver, WorldObject reference)
+ {
+ final Instance instance = receiver.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = receiver.getParameters();
+
+ if (eventName.equals(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0))))
+ {
+ receiver.setTargetable(true);
+ receiver.disableCoreAI(false);
+ receiver.setInvisible(false);
+ }
+ }
+ return super.onEventReceived(eventName, sender, receiver, reference);
+ }
+
+ @Override
+ public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ switch (npc.getId())
+ {
+ case TRAP_1:
+ case TRAP_2:
+ {
+ if (npc.isScriptValue(0))
+ {
+ getTimers().addTimer("TRAP_REACT_TIMER", 1000, npc, null);
+ npc.setScriptValue(1);
+ }
+ break;
+ }
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onKill(Npc npc, PlayerInstance killer, boolean isSummon)
+ {
+ final Instance instance = npc.getInstanceWorld();
+ if (isInInstance(instance))
+ {
+ final StatsSet npcParams = npc.getParameters();
+ final int killTarget = instance.getParameters().getInt("KILL_TARGET", 5);
+ int currentKillCount = instance.getParameters().getInt("KILL_COUNT", 0);
+
+ switch (npc.getId())
+ {
+ case VICIOUS_DUELER:
+ case VICIOUS_WARRIOR:
+ case VICIOUS_SWORDSMAN:
+ {
+ if (npcParams.getInt("last_checker", 0) == 1)
+ {
+ currentKillCount = instance.getParameters().increaseInt("KILL_COUNT", 0, 1);
+
+ if (currentKillCount >= killTarget)
+ {
+ final ZoneType zone = ZoneManager.getInstance().getZoneByName(npc.getParameters().getString("AreaTeleName"), TeleportZone.class);
+ if (zone != null)
+ {
+ zone.setEnabled(true, instance.getId());
+ showOnScreenMsg(instance, NpcStringId.THE_PORTAL_TO_THE_NEXT_ROOM_IS_NOW_OPEN, ExShowScreenMessage.MIDDLE_CENTER, 4000);
+ instance.spawnGroup("innadril23_mb2422_pt" + instance.getStatus() + "m1");
+ instance.getParameters().set("KILL_COUNT", 0);
+
+ switch (instance.getStatus())
+ {
+ case 1:
+ {
+ instance.getParameters().set("KILL_TARGET", 12);
+ instance.setStatus(2);
+ break;
+ }
+ case 2:
+ {
+ instance.getParameters().set("KILL_TARGET", 3);
+ instance.setStatus(3);
+ break;
+ }
+ case 3:
+ {
+ instance.getParameters().set("KILL_TARGET", 18);
+ instance.setStatus(4);
+ break;
+ }
+ case 4:
+ {
+ instance.getParameters().set("KILL_TARGET", 5);
+ instance.setStatus(5);
+ break;
+ }
+ case 5:
+ {
+ instance.getParameters().set("KILL_TARGET", 20);
+ instance.setStatus(6);
+ break;
+ }
+ case 6:
+ {
+ final int random = getRandom(100);
+ int bossId = -1;
+
+ if (random < 55)
+ {
+ bossId = KECHI_NORMAL;
+ }
+ else if (random < 80)
+ {
+ bossId = KECHI_WISE;
+ }
+ else if (random < 95)
+ {
+ bossId = KECHI_WEALTHY;
+ }
+ else
+ {
+ bossId = KECHI_ARMED;
+ }
+
+ addSpawn(bossId, BOSS_LOC, false, 0, false, instance.getId());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOGGER.warning("Cannot find teleport zone for Crystal Cavern: Steam Corridor instance!!!");
+ }
+ }
+ }
+ break;
+ }
+ case KECHI_NORMAL:
+ case KECHI_WISE:
+ case KECHI_WEALTHY:
+ case KECHI_ARMED:
+ {
+ instance.finishInstance();
+ break;
+ }
+ }
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ private void onCreatureSee(OnCreatureSee event)
+ {
+ final Creature creature = event.getSeen();
+ final Npc npc = (Npc) event.getSeer();
+ final Instance instance = npc.getInstanceWorld();
+
+ if (isInInstance(instance) && creature.isPlayer())
+ {
+ final StatsSet npcParams = npc.getParameters();
+
+ switch (npc.getId())
+ {
+ case PLAYER_DETECTOR:
+ {
+ if (npc.isScriptValue(0))
+ {
+ npc.setScriptValue(1);
+ npc.broadcastEvent(String.valueOf(24220005 + npcParams.getInt("Terri_ID", 0)), 2000, null);
+
+ for (int i = 0; i < getRandom(5); i++)
+ {
+ final Npc trap = addSpawn(((npcParams.getInt("MobType", 0) == 0) ? TRAP_1 : TRAP_2), npc, true, 0, false, instance.getId());
+ trap.getVariables().set("TIMER_VAL", 4);
+ }
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new CrystalCavernsSteamCorridor();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
new file mode 100644
index 0000000000..efc886f117
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/condNoParty.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+You must be in a party to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
new file mode 100644
index 0000000000..dfa8583852
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/instances/CrystalCaverns/condNoPartyLeader.html
@@ -0,0 +1,3 @@
+Crystal Caverns Admission Portal:
+The leader of the party should try to enter.
+
\ No newline at end of file
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/stats/npcs/19000-19099.xml b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/stats/npcs/19000-19099.xml
index d1a15f1afa..47a6497a67 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/stats/npcs/19000-19099.xml
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/stats/npcs/19000-19099.xml
@@ -366,7 +366,7 @@
-
+
@@ -403,7 +403,7 @@
-
+
diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/zones/teleportzones.xml b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/zones/teleportzones.xml
index 58d7790d91..417a27bcf4 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/zones/teleportzones.xml
+++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/zones/teleportzones.xml
@@ -154,6 +154,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+