From 43281da714802d6ebdeaa6a89f2cc16699953df9 Mon Sep 17 00:00:00 2001
From: Noe Caratini <caratinin@gmail.com>
Date: Sun, 1 May 2022 18:46:48 +0100
Subject: [PATCH] refactor(quest): Simplified Q00372_LegacyOfInsolence and
 aligned with retail

---
 pom.xml                                       |  11 +-
 .../Q00372_LegacyOfInsolence.java             | 591 +++++-------------
 .../Q00372LegacyOfInsolenceTest.java          | 546 ++++++++++++++++
 3 files changed, 727 insertions(+), 421 deletions(-)
 create mode 100644 src/test/java/com/l2jserver/datapack/quests/Q00372_LegacyOfInsolence/Q00372LegacyOfInsolenceTest.java

diff --git a/pom.xml b/pom.xml
index 525ed4fda0..e2f1874fe5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,7 +1,7 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>com.l2jserver</groupId>
-	<artifactId>l2j-server-datapack</artifactId>
+	<artifactId>l2j-server-datapack</artifactId>
 	<version>2.6.3.0-SNAPSHOT</version>
 	<name>L2J DataPack</name>
 	<properties>
@@ -14,7 +14,8 @@
 		<l2j-server-game.version>2.6.3.0-SNAPSHOT</l2j-server-game.version>
 		<!-- Test -->
 		<junit-jupiter.version>5.8.0</junit-jupiter.version>
-		<mockito.version>3.12.4</mockito.version>
+		<mockito.version>4.5.1</mockito.version>
+		<assertj-core.version>3.22.0</assertj-core.version>
 		<!-- Plugins -->
 		<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
 		<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
@@ -62,6 +63,12 @@
 			<version>${mockito.version}</version>
 			<scope>test</scope>
 		</dependency>
+		<dependency>
+			<groupId>org.assertj</groupId>
+			<artifactId>assertj-core</artifactId>
+			<version>${assertj-core.version}</version>
+			<scope>test</scope>
+		</dependency>
 	</dependencies>
 	<build>
 		<plugins>
diff --git a/src/main/java/com/l2jserver/datapack/quests/Q00372_LegacyOfInsolence/Q00372_LegacyOfInsolence.java b/src/main/java/com/l2jserver/datapack/quests/Q00372_LegacyOfInsolence/Q00372_LegacyOfInsolence.java
index 9e48f5dd4a..02b573648e 100644
--- a/src/main/java/com/l2jserver/datapack/quests/Q00372_LegacyOfInsolence/Q00372_LegacyOfInsolence.java
+++ b/src/main/java/com/l2jserver/datapack/quests/Q00372_LegacyOfInsolence/Q00372_LegacyOfInsolence.java
@@ -18,395 +18,182 @@
  */
 package com.l2jserver.datapack.quests.Q00372_LegacyOfInsolence;
 
-import com.l2jserver.gameserver.enums.audio.Sound;
 import com.l2jserver.gameserver.model.actor.L2Npc;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jserver.gameserver.model.holders.ItemChanceHolder;
 import com.l2jserver.gameserver.model.quest.Quest;
+import com.l2jserver.gameserver.model.quest.QuestDroplist;
 import com.l2jserver.gameserver.model.quest.QuestState;
-import com.l2jserver.gameserver.util.Util;
 
-import java.util.HashMap;
 import java.util.Map;
 
 /**
  * Legacy Of Insolence (372)
  * @author ivantotov
+ * @author Noé Caratini aka Kita
  */
 public final class Q00372_LegacyOfInsolence extends Quest {
+	// Misc
+	private static final int MIN_LEVEL = 59;
 	// NPCs
 	private static final int TRADER_HOLLY = 30839;
 	private static final int WAREHOUSE_KEEPER_WALDERAL = 30844;
 	private static final int MAGISTER_DESMOND = 30855;
 	private static final int ANTIQUE_DEALER_PATRIN = 30929;
 	private static final int CLAUDIA_ATHEBALDT = 31001;
+	// Monsters
+	private static final int CORRUPT_SAGE = 20817;
+	private static final int ERIN_EDIUNCE = 20821;
+	private static final int HALLATES_INSPECTOR = 20825;
+	private static final int PLATINUM_TRIBE_OVERLORD = 20829;
+	private static final int MESSENGER_ANGEL = 21062;
+	private static final int PLATINUM_GUARDIAN_PREFECT = 21069;
 	// Items
 	private static final int ANCIENT_RED_PAPYRUS = 5966;
 	private static final int ANCIENT_BLUE_PAPYRUS = 5967;
 	private static final int ANCIENT_BLACK_PAPYRUS = 5968;
 	private static final int ANCIENT_WHITE_PAPYRUS = 5969;
-	private static final int REVELATION_OF_THE_SEALS_CHAPTER_OF_AVARICE = 5972;
-	private static final int REVELATION_OF_THE_SEALS_CHAPTER_OF_GNOSIS = 5973;
-	private static final int REVELATION_OF_THE_SEALS_CHAPTER_OF_STRIFE = 5974;
-	private static final int REVELATION_OF_THE_SEALS_CHAPTER_OF_VENGEANCE = 5975;
-	private static final int REVELATION_OF_THE_SEALS_CHAPTER_OF_AWEKENING = 5976;
-	private static final int REVELATION_OF_THE_SEALS_CHAPTER_OF_CALAMITY = 5977;
-	private static final int REVELATION_OF_THE_SEALS_CHAPTER_OF_DESCENT = 5978;
-	private static final int ANCIENT_EPIC_CHAPTER_1 = 5979;
-	private static final int ANCIENT_EPIC_CHAPTER_2 = 5980;
-	private static final int ANCIENT_EPIC_CHAPTER_3 = 5981;
-	private static final int ANCIENT_EPIC_CHAPTER_4 = 5982;
-	private static final int ANCIENT_EPIC_CHAPTER_5 = 5983;
-	private static final int IMPERIAL_GENEALOGY_1 = 5984;
-	private static final int IMPERIAL_GENEALOGY_2 = 5985;
-	private static final int IMPERIAL_GENEALOGY_3 = 5986;
-	private static final int IMPERIAL_GENEALOGY_4 = 5987;
-	private static final int IMPERIAL_GENEALOGY_5 = 5988;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR = 5989;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR = 5990;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR = 5991;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR = 5992;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR = 5993;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR = 5994;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR = 5995;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR = 5996;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR = 5997;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR = 5998;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR = 5999;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR = 6000;
-	private static final int BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR = 6001;
+	private static final int[] REVELATION_OF_THE_SEALS = {5972, 5973, 5974, 5975, 5976, 5977, 5978};
+	private static final int[] ANCIENT_EPIC = {5979, 5980, 5981, 5982, 5983};
+	private static final int[] IMPERIAL_GENEALOGY = {5984, 5985, 5986, 5987, 5988};
+	private static final int[] BLUEPRINTS = {5989, 5990, 5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999, 6000, 6001};
+	// Droplist
+	private static final QuestDroplist DROPLIST = QuestDroplist.builder()
+			.addSingleDrop(CORRUPT_SAGE, ANCIENT_RED_PAPYRUS, 30.2)
+			.addSingleDrop(ERIN_EDIUNCE, ANCIENT_RED_PAPYRUS, 41.0)
+			.addSingleDrop(HALLATES_INSPECTOR, ANCIENT_RED_PAPYRUS, 44.7)
+			.addSingleDrop(PLATINUM_TRIBE_OVERLORD, ANCIENT_BLUE_PAPYRUS, 45.1)
+			.addSingleDrop(MESSENGER_ANGEL, ANCIENT_WHITE_PAPYRUS, 29.0)
+			.addSingleDrop(PLATINUM_GUARDIAN_PREFECT, ANCIENT_BLACK_PAPYRUS, 28.0)
+			.build();
+	private static final Map<Integer, Integer> QS_KILLER_CHANCE = Map.of(
+			CORRUPT_SAGE, 1,
+			ERIN_EDIUNCE, 1,
+			HALLATES_INSPECTOR, 3,
+			PLATINUM_TRIBE_OVERLORD, 3,
+			MESSENGER_ANGEL, 1,
+			PLATINUM_GUARDIAN_PREFECT, 1
+	);
 	// Rewards
-	private static final int RECIPE_SEALED_DARK_CRYSTAL_BOOTS_60 = 5368;
-	private static final int RECIPE_SEALED_TALLUM_BOOTS_60 = 5370;
-	private static final int RECIPE_SEALED_BOOTS_OF_NIGHTMARE_60 = 5380;
-	private static final int RECIPE_SEALED_MAJESTIC_BOOTS_60 = 5382;
-	private static final int RECIPE_SEALED_DARK_CRYSTAL_GLOVES_60 = 5392;
-	private static final int RECIPE_SEALED_TALLUM_GLOVES_60 = 5394;
-	private static final int RECIPE_SEALED_GAUNTLETS_OF_NIGHTMARE_60 = 5404;
-	private static final int RECIPE_SEALED_MAJESTIC_GAUNTLETS_60 = 5406;
-	private static final int RECIPE_SEALED_DARK_CRYSTAL_HELMET_60 = 5426;
-	private static final int RECIPE_SEALED_TALLUM_HELMET_60 = 5428;
-	private static final int RECIPE_SEALED_HELM_OF_NIGHTMARE_60 = 5430;
-	private static final int RECIPE_SEALED_MAJESTIC_CIRCLET_60 = 5432;
-	private static final int SEALED_DARK_CRYSTAL_BOOTS_LINING = 5496;
-	private static final int SEALED_TALLUM_BOOTS_LINING = 5497;
-	private static final int SEALED_BOOTS_OF_NIGHTMARE_LINING = 5502;
-	private static final int SEALED_MAJESTIC_BOOTS_LINING = 5503;
-	private static final int SEALED_DARK_CRYSTAL_GLOVES_DESIGN = 5508;
-	private static final int SEALED_TALLUM_GLOVES_DESIGN = 5509;
-	private static final int SEALED_GAUNTLETS_OF_NIGHTMARE_DESIGN = 5514;
-	private static final int SEALED_MAJESTIC_GAUNTLETS_DESIGN = 5515;
-	private static final int SEALED_DARK_CRYSTAL_HELMET_DESIGN = 5525;
-	private static final int SEALED_TALLUM_HELM_DESIGN = 5526;
-	private static final int SEALED_HELM_OF_NIGHTMARE_DESIGN = 5527;
-	private static final int SEALED_MAJESTIC_CIRCLET_DESIGN = 5528;
-	// Monsters
-	private static final int HALLATES_INSPECTOR = 20825;
-	private static final Map<Integer, ItemChanceHolder> MONSTER_REWARDS = new HashMap<>();
-	
-	static {
-		MONSTER_REWARDS.put(20817, new ItemChanceHolder(ANCIENT_RED_PAPYRUS, 302, 1));
-		MONSTER_REWARDS.put(20821, new ItemChanceHolder(ANCIENT_RED_PAPYRUS, 410, 1));
-		MONSTER_REWARDS.put(HALLATES_INSPECTOR, new ItemChanceHolder(ANCIENT_RED_PAPYRUS, 1, 447));
-		MONSTER_REWARDS.put(20829, new ItemChanceHolder(ANCIENT_BLUE_PAPYRUS, 451, 1));
-		MONSTER_REWARDS.put(21062, new ItemChanceHolder(ANCIENT_WHITE_PAPYRUS, 290, 1));
-		MONSTER_REWARDS.put(21069, new ItemChanceHolder(ANCIENT_BLACK_PAPYRUS, 280, 1));
-	}
-	
-	// Misc
-	private static final int MIN_LEVEL = 59;
+	private static final RewardsGroup REWARDS_DARK_CRYSTAL = new RewardsGroup(
+			5496, // Sealed Dark Crystal Boots Lining
+			5508, // Sealed Dark Crystal Gloves Design
+			5525, // Sealed Dark Crystal Helmet Design
+			5368, // Recipe: Sealed Dark Crystal Boots(60%)
+			5392, // Recipe: Sealed Dark Crystal Gloves(60%)
+			5426 // Recipe: Sealed Dark Crystal Helmet(60%)
+	);
+	private static final RewardsGroup REWARDS_TALLUM = new RewardsGroup(
+			5497, // Sealed Tallum Boots Lining
+			5509, // Sealed Tallum Gloves Design
+			5526, // Sealed Tallum Helm Design
+			5370, // Recipe: Sealed Tallum Boots(60%)
+			5394, // Recipe: Sealed Tallum Gloves(60%)
+			5428 // Recipe: Sealed Tallum Helmet(60%)
+	);
+	private static final RewardsGroup REWARDS_NIGHTMARE = new RewardsGroup(
+			5502, // Sealed Boots of Nightmare Lining
+			5514, // Sealed Gauntlets of Nightmare Design
+			5527, // Sealed Helm of Nightmare Design
+			5380, // Recipe: Sealed Boots of Nightmare(60%)
+			5404, // Recipe: Sealed Gauntlets of Nightmare(60%)
+			5430 // Recipe: Sealed Helm of Nightmare(60%)
+	);
+	private static final RewardsGroup REWARDS_MAJESTIC = new RewardsGroup(
+			5503, // Sealed Majestic Boots Lining
+			5515, // Sealed Majestic Gauntlets Design
+			5528, // Sealed Majestic Circlet Design
+			5382, // Recipe: Sealed Majestic Boots(60%)
+			5406, // Recipe: Sealed Majestic Gauntlets(60%)
+			5432 // Recipe: Sealed Majestic Circlet (60%)
+	);
+	private static final int[] WALDERAL_CHANCES_LOW_A = {10, 20, 30, 40, 51, 62, 79, 100};
+	private static final int[] WALDERAL_CHANCES_HIGH_A = {17, 34, 49, 58, 70, 82, 92, 100};
+	private static final int[] OTHER_CHANCES_LOW_A = {30, 60, 80, 90, 100};
+	private static final int[] OTHER_CHANCES_HIGH_A = {31, 62, 75, 83, 100};
 	
 	public Q00372_LegacyOfInsolence() {
 		super(372, Q00372_LegacyOfInsolence.class.getSimpleName(), "Legacy Of Insolence");
 		addStartNpc(WAREHOUSE_KEEPER_WALDERAL);
 		addTalkId(WAREHOUSE_KEEPER_WALDERAL, TRADER_HOLLY, MAGISTER_DESMOND, ANTIQUE_DEALER_PATRIN, CLAUDIA_ATHEBALDT);
-		addKillId(MONSTER_REWARDS.keySet());
+		addKillId(DROPLIST.getNpcIds());
 	}
 	
 	@Override
 	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player) {
 		final QuestState qs = getQuestState(player, false);
-		final int chance = getRandom(100);
-		
 		if (qs == null) {
 			return super.onAdvEvent(event, npc, player);
 		}
 		
-		String htmltext = null;
 		switch (event) {
-			case "30844-04.htm": {
+			case "30844-04.htm" -> {
 				if (qs.isCreated()) {
 					qs.startQuest();
-					htmltext = event;
-				}
-				break;
-			}
-			case "30844-07.html": {
-				if (hasQuestItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR)) {
-					htmltext = event;
 				} else {
-					htmltext = "30844-06.html";
+					return null;
 				}
-				break;
 			}
-			case "30844-09.html": {
-				qs.exitQuest(true, true);
-				htmltext = event;
-				break;
+			case "30844-05b.html" -> qs.setCond(2);
+			case "30844-07.html" -> {
+				if (!hasQuestItems(player, BLUEPRINTS)) {
+					return "30844-06.html";
+				}
 			}
-			case "30844-07a.html": {
-				if (hasQuestItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR)) {
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR, 1);
-					
-					if (chance < 10) {
-						giveItems(player, SEALED_DARK_CRYSTAL_BOOTS_LINING, 1);
-					} else if (chance < 20) {
-						giveItems(player, SEALED_DARK_CRYSTAL_GLOVES_DESIGN, 1);
-					} else if (chance < 30) {
-						giveItems(player, SEALED_DARK_CRYSTAL_HELMET_DESIGN, 1);
-					} else if (chance < 40) {
-						giveItems(player, SEALED_DARK_CRYSTAL_BOOTS_LINING, 1);
-						giveItems(player, SEALED_DARK_CRYSTAL_GLOVES_DESIGN, 1);
-						giveItems(player, SEALED_DARK_CRYSTAL_HELMET_DESIGN, 1);
-					} else if (chance < 51) {
-						giveItems(player, RECIPE_SEALED_DARK_CRYSTAL_BOOTS_60, 1);
-					} else if (chance < 62) {
-						giveItems(player, RECIPE_SEALED_DARK_CRYSTAL_GLOVES_60, 1);
-					} else if (chance < 79) {
-						giveItems(player, RECIPE_SEALED_DARK_CRYSTAL_HELMET_60, 1);
-					} else if (chance < 100) {
-						giveItems(player, RECIPE_SEALED_DARK_CRYSTAL_BOOTS_60, 1);
-						giveItems(player, RECIPE_SEALED_DARK_CRYSTAL_GLOVES_60, 1);
-						giveItems(player, RECIPE_SEALED_DARK_CRYSTAL_HELMET_60, 1);
-					}
-					htmltext = event;
+			case "30844-07a.html" -> {
+				if (hasQuestItems(player, BLUEPRINTS)) {
+					takeItems(player, 1, BLUEPRINTS);
+					walderalGiveRewards(player, REWARDS_DARK_CRYSTAL, WALDERAL_CHANCES_LOW_A);
 				} else {
-					htmltext = "30844-07e.html";
+					return "30844-07e.html";
 				}
-				break;
 			}
-			case "30844-07b.html": {
-				if (hasQuestItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR)) {
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR, 1);
-					
-					if (chance < 10) {
-						giveItems(player, SEALED_TALLUM_BOOTS_LINING, 1);
-					} else if (chance < 20) {
-						giveItems(player, SEALED_TALLUM_GLOVES_DESIGN, 1);
-					} else if (chance < 30) {
-						giveItems(player, SEALED_TALLUM_HELM_DESIGN, 1);
-					} else if (chance < 40) {
-						giveItems(player, SEALED_TALLUM_BOOTS_LINING, 1);
-						giveItems(player, SEALED_TALLUM_GLOVES_DESIGN, 1);
-						giveItems(player, SEALED_TALLUM_HELM_DESIGN, 1);
-					} else if (chance < 51) {
-						giveItems(player, RECIPE_SEALED_TALLUM_BOOTS_60, 1);
-					} else if (chance < 62) {
-						giveItems(player, RECIPE_SEALED_TALLUM_GLOVES_60, 1);
-					} else if (chance < 79) {
-						giveItems(player, RECIPE_SEALED_TALLUM_HELMET_60, 1);
-					} else if (chance < 100) {
-						giveItems(player, RECIPE_SEALED_TALLUM_BOOTS_60, 1);
-						giveItems(player, RECIPE_SEALED_TALLUM_GLOVES_60, 1);
-						giveItems(player, RECIPE_SEALED_TALLUM_HELMET_60, 1);
-					}
-					htmltext = event;
+			case "30844-07b.html" -> {
+				if (hasQuestItems(player, BLUEPRINTS)) {
+					takeItems(player, 1, BLUEPRINTS);
+					walderalGiveRewards(player, REWARDS_TALLUM, WALDERAL_CHANCES_LOW_A);
 				} else {
-					htmltext = "30844-07e.html";
+					return "30844-07e.html";
 				}
-				break;
 			}
-			case "30844-07c.html": {
-				if (hasQuestItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR)) {
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR, 1);
-					
-					if (chance < 17) {
-						giveItems(player, SEALED_BOOTS_OF_NIGHTMARE_LINING, 1);
-					} else if (chance < 34) {
-						giveItems(player, SEALED_GAUNTLETS_OF_NIGHTMARE_DESIGN, 1);
-					} else if (chance < 49) {
-						giveItems(player, SEALED_HELM_OF_NIGHTMARE_DESIGN, 1);
-					} else if (chance < 58) {
-						giveItems(player, SEALED_BOOTS_OF_NIGHTMARE_LINING, 1);
-						giveItems(player, SEALED_GAUNTLETS_OF_NIGHTMARE_DESIGN, 1);
-						giveItems(player, SEALED_HELM_OF_NIGHTMARE_DESIGN, 1);
-					} else if (chance < 70) {
-						giveItems(player, RECIPE_SEALED_BOOTS_OF_NIGHTMARE_60, 1);
-					} else if (chance < 82) {
-						giveItems(player, RECIPE_SEALED_GAUNTLETS_OF_NIGHTMARE_60, 1);
-					} else if (chance < 92) {
-						giveItems(player, RECIPE_SEALED_HELM_OF_NIGHTMARE_60, 1);
-					} else if (chance < 100) {
-						giveItems(player, RECIPE_SEALED_BOOTS_OF_NIGHTMARE_60, 1);
-						giveItems(player, RECIPE_SEALED_GAUNTLETS_OF_NIGHTMARE_60, 1);
-						giveItems(player, RECIPE_SEALED_HELM_OF_NIGHTMARE_60, 1);
-					}
-					htmltext = event;
+			case "30844-07c.html" -> {
+				if (hasQuestItems(player, BLUEPRINTS)) {
+					takeItems(player, 1, BLUEPRINTS);
+					walderalGiveRewards(player, REWARDS_NIGHTMARE, WALDERAL_CHANCES_HIGH_A);
 				} else {
-					htmltext = "30844-07e.html";
+					return "30844-07e.html";
 				}
-				break;
 			}
-			case "30844-07d.html": {
-				if (hasQuestItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, //
-					BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR)) {
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_1ST_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_2ND_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_3RD_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_4TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_5TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_6TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_7TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_8TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_9TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_10TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_11TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_12TH_FLOOR, 1);
-					takeItems(player, BLUEPRINT_TOWER_OF_INSOLENCE_13TH_FLOOR, 1);
-					
-					if (chance < 17) {
-						giveItems(player, SEALED_MAJESTIC_BOOTS_LINING, 1);
-					} else if (chance < 34) {
-						giveItems(player, SEALED_MAJESTIC_GAUNTLETS_DESIGN, 1);
-					} else if (chance < 49) {
-						giveItems(player, SEALED_MAJESTIC_CIRCLET_DESIGN, 1);
-					} else if (chance < 58) {
-						giveItems(player, SEALED_MAJESTIC_BOOTS_LINING, 1);
-						giveItems(player, SEALED_MAJESTIC_GAUNTLETS_DESIGN, 1);
-						giveItems(player, SEALED_MAJESTIC_CIRCLET_DESIGN, 1);
-					} else if (chance < 70) {
-						giveItems(player, RECIPE_SEALED_MAJESTIC_BOOTS_60, 1);
-					} else if (chance < 82) {
-						giveItems(player, RECIPE_SEALED_MAJESTIC_GAUNTLETS_60, 1);
-					} else if (chance < 92) {
-						giveItems(player, RECIPE_SEALED_MAJESTIC_CIRCLET_60, 1);
-					} else if (chance < 100) {
-						giveItems(player, RECIPE_SEALED_MAJESTIC_BOOTS_60, 1);
-						giveItems(player, RECIPE_SEALED_MAJESTIC_GAUNTLETS_60, 1);
-						giveItems(player, RECIPE_SEALED_MAJESTIC_CIRCLET_60, 1);
-					}
-					htmltext = event;
+			case "30844-07d.html" -> {
+				if (hasQuestItems(player, BLUEPRINTS)) {
+					takeItems(player, 1, BLUEPRINTS);
+					walderalGiveRewards(player, REWARDS_MAJESTIC, WALDERAL_CHANCES_HIGH_A);
 				} else {
-					htmltext = "30844-07e.html";
+					return "30844-07e.html";
 				}
-				break;
 			}
-			case "30844-05b.html": {
-				qs.setCond(2);
-				htmltext = event;
-				break;
+			case "30844-09.html" -> qs.exitQuest(true, true);
+			case "30844-03.htm", "30844-05.html", "30844-05a.html", "30844-08.html", "30844-10.html", "30844-11.html" -> {
+				return event;
 			}
-			case "30844-03.htm":
-			case "30844-05.html":
-			case "30844-05a.html":
-			case "30844-08.html":
-			case "30844-10.html":
-			case "30844-11.html": {
-				htmltext = event;
-				break;
+			default -> {
+				return null;
 			}
 		}
-		return htmltext;
+		return event;
 	}
 	
 	@Override
 	public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon) {
-		final ItemChanceHolder item = MONSTER_REWARDS.get(npc.getId());
-		if (npc.getId() == HALLATES_INSPECTOR) {
-			if (getRandom(1000) < item.getChance()) {
-				final QuestState qs = getRandomPartyMemberState(killer, -1, 3, npc);
-				if (qs != null) {
-					giveItems(qs.getPlayer(), item.getId(), item.getCount());
-					playSound(qs.getPlayer(), Sound.ITEMSOUND_QUEST_ITEMGET);
-				}
-			}
-			return super.onKill(npc, killer, isSummon);
+		final QuestState qs = getRandomPartyMemberState(killer, -1, QS_KILLER_CHANCE.get(npc.getId()), npc);
+		if (qs != null) {
+			giveItemRandomly(qs.getPlayer(), npc, DROPLIST.get(npc), true);
 		}
-		
-		if (Util.checkIfInRange(1500, npc, killer, true) && (getRandom(1000) < item.getChance())) {
-			L2PcInstance rewardedPlayer = null;
-			if (!killer.isInParty()) {
-				final QuestState qs = getQuestState(killer, false);
-				if ((qs != null) && qs.isStarted()) {
-					rewardedPlayer = killer;
-				}
-			} else {
-				int chance = 0;
-				for (L2PcInstance partyMember : killer.getParty().getMembers()) {
-					final QuestState partyMemberQuestState = getQuestState(partyMember, false);
-					if ((partyMemberQuestState != null) && partyMemberQuestState.isStarted()) {
-						final int chance2 = getRandom(1000);
-						if (chance < chance2) {
-							chance = chance2;
-							rewardedPlayer = partyMember;
-						}
-					}
-				}
-			}
-			
-			if ((rewardedPlayer != null) && Util.checkIfInRange(1500, npc, rewardedPlayer, true)) {
-				giveItems(rewardedPlayer, item.getId(), item.getCount());
-				playSound(rewardedPlayer, Sound.ITEMSOUND_QUEST_ITEMGET);
-			}
-		}
-		
 		return super.onKill(npc, killer, isSummon);
 	}
 	
 	@Override
 	public String onTalk(L2Npc npc, L2PcInstance player) {
 		final QuestState qs = getQuestState(player, true);
-		final int chance = getRandom(100);
 		String htmltext = getNoQuestMsg(player);
 		if (qs.isCreated()) {
 			if (npc.getId() == WAREHOUSE_KEEPER_WALDERAL) {
@@ -418,128 +205,94 @@ public final class Q00372_LegacyOfInsolence extends Quest {
 			}
 		} else if (qs.isStarted()) {
 			switch (npc.getId()) {
-				case WAREHOUSE_KEEPER_WALDERAL: {
-					htmltext = "30844-05.html";
-					break;
-				}
-				case TRADER_HOLLY: {
-					if (hasQuestItems(player, IMPERIAL_GENEALOGY_1, IMPERIAL_GENEALOGY_2, IMPERIAL_GENEALOGY_3, IMPERIAL_GENEALOGY_4, IMPERIAL_GENEALOGY_5)) {
-						takeItems(player, IMPERIAL_GENEALOGY_1, 1);
-						takeItems(player, IMPERIAL_GENEALOGY_2, 1);
-						takeItems(player, IMPERIAL_GENEALOGY_3, 1);
-						takeItems(player, IMPERIAL_GENEALOGY_4, 1);
-						takeItems(player, IMPERIAL_GENEALOGY_5, 1);
-						
-						if (chance < 30) {
-							giveItems(player, SEALED_DARK_CRYSTAL_BOOTS_LINING, 1);
-						} else if (chance < 60) {
-							giveItems(player, SEALED_DARK_CRYSTAL_GLOVES_DESIGN, 1);
-						} else if (chance < 80) {
-							giveItems(player, SEALED_DARK_CRYSTAL_HELMET_DESIGN, 1);
-						} else if (chance < 90) {
-							giveItems(player, SEALED_DARK_CRYSTAL_BOOTS_LINING, 1);
-							giveItems(player, SEALED_DARK_CRYSTAL_GLOVES_DESIGN, 1);
-							giveItems(player, SEALED_DARK_CRYSTAL_HELMET_DESIGN, 1);
-						} else if (chance < 100) {
-							giveAdena(player, 4000, true);
-						}
+				case WAREHOUSE_KEEPER_WALDERAL -> htmltext = "30844-05.html";
+				case TRADER_HOLLY -> {
+					if (hasQuestItems(player, IMPERIAL_GENEALOGY)) {
+						takeItems(player, 1, IMPERIAL_GENEALOGY);
+						otherGiveRewards(player, REWARDS_DARK_CRYSTAL, OTHER_CHANCES_LOW_A);
 						htmltext = "30839-02.html";
 					} else {
 						htmltext = "30839-01.html";
 					}
-					break;
 				}
-				case MAGISTER_DESMOND: {
-					if (hasQuestItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_AVARICE, REVELATION_OF_THE_SEALS_CHAPTER_OF_GNOSIS, REVELATION_OF_THE_SEALS_CHAPTER_OF_STRIFE, //
-						REVELATION_OF_THE_SEALS_CHAPTER_OF_VENGEANCE, REVELATION_OF_THE_SEALS_CHAPTER_OF_AWEKENING, REVELATION_OF_THE_SEALS_CHAPTER_OF_CALAMITY, //
-						REVELATION_OF_THE_SEALS_CHAPTER_OF_DESCENT)) {
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_AVARICE, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_GNOSIS, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_STRIFE, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_VENGEANCE, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_AWEKENING, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_CALAMITY, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_DESCENT, 1);
-						
-						if (chance < 31) {
-							giveItems(player, SEALED_MAJESTIC_BOOTS_LINING, 1);
-						} else if (chance < 62) {
-							giveItems(player, SEALED_MAJESTIC_GAUNTLETS_DESIGN, 1);
-						} else if (chance < 75) {
-							giveItems(player, SEALED_MAJESTIC_CIRCLET_DESIGN, 1);
-						} else if (chance < 83) {
-							giveItems(player, SEALED_MAJESTIC_BOOTS_LINING, 1);
-							giveItems(player, SEALED_MAJESTIC_GAUNTLETS_DESIGN, 1);
-							giveItems(player, SEALED_MAJESTIC_CIRCLET_DESIGN, 1);
-						} else if (chance < 100) {
-							giveAdena(player, 4000, true);
-						}
+				case MAGISTER_DESMOND -> {
+					if (hasQuestItems(player, REVELATION_OF_THE_SEALS)) {
+						takeItems(player, 1, REVELATION_OF_THE_SEALS);
+						otherGiveRewards(player, REWARDS_MAJESTIC, OTHER_CHANCES_HIGH_A);
 						htmltext = "30855-02.html";
 					} else {
 						htmltext = "30855-01.html";
 					}
-					break;
 				}
-				case ANTIQUE_DEALER_PATRIN: {
-					if (hasQuestItems(player, ANCIENT_EPIC_CHAPTER_1, ANCIENT_EPIC_CHAPTER_2, ANCIENT_EPIC_CHAPTER_3, ANCIENT_EPIC_CHAPTER_4, ANCIENT_EPIC_CHAPTER_5)) {
-						takeItems(player, ANCIENT_EPIC_CHAPTER_1, 1);
-						takeItems(player, ANCIENT_EPIC_CHAPTER_2, 1);
-						takeItems(player, ANCIENT_EPIC_CHAPTER_3, 1);
-						takeItems(player, ANCIENT_EPIC_CHAPTER_4, 1);
-						takeItems(player, ANCIENT_EPIC_CHAPTER_5, 1);
-						
-						if (chance < 30) {
-							giveItems(player, SEALED_TALLUM_BOOTS_LINING, 1);
-						} else if (chance < 60) {
-							giveItems(player, SEALED_TALLUM_GLOVES_DESIGN, 1);
-						} else if (chance < 80) {
-							giveItems(player, SEALED_TALLUM_HELM_DESIGN, 1);
-						} else if (chance < 90) {
-							giveItems(player, SEALED_TALLUM_BOOTS_LINING, 1);
-							giveItems(player, SEALED_TALLUM_GLOVES_DESIGN, 1);
-							giveItems(player, SEALED_TALLUM_HELM_DESIGN, 1);
-						} else if (chance < 100) {
-							giveAdena(player, 4000, true);
-						}
+				case ANTIQUE_DEALER_PATRIN -> {
+					if (hasQuestItems(player, ANCIENT_EPIC)) {
+						takeItems(player, 1, ANCIENT_EPIC);
+						otherGiveRewards(player, REWARDS_TALLUM, OTHER_CHANCES_LOW_A);
 						htmltext = "30929-02.html";
 					} else {
-						htmltext = "30929-02.html";
+						htmltext = "30929-01.html";
 					}
-					break;
 				}
-				case CLAUDIA_ATHEBALDT: {
-					if (hasQuestItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_AVARICE, REVELATION_OF_THE_SEALS_CHAPTER_OF_GNOSIS, REVELATION_OF_THE_SEALS_CHAPTER_OF_STRIFE, //
-						REVELATION_OF_THE_SEALS_CHAPTER_OF_VENGEANCE, REVELATION_OF_THE_SEALS_CHAPTER_OF_AWEKENING, REVELATION_OF_THE_SEALS_CHAPTER_OF_CALAMITY, //
-						REVELATION_OF_THE_SEALS_CHAPTER_OF_DESCENT)) {
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_AVARICE, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_GNOSIS, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_STRIFE, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_VENGEANCE, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_AWEKENING, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_CALAMITY, 1);
-						takeItems(player, REVELATION_OF_THE_SEALS_CHAPTER_OF_DESCENT, 1);
-						
-						if (chance < 31) {
-							giveItems(player, SEALED_BOOTS_OF_NIGHTMARE_LINING, 1);
-						} else if (chance < 62) {
-							giveItems(player, SEALED_GAUNTLETS_OF_NIGHTMARE_DESIGN, 1);
-						} else if (chance < 75) {
-							giveItems(player, SEALED_HELM_OF_NIGHTMARE_DESIGN, 1);
-						} else if (chance < 83) {
-							giveItems(player, SEALED_BOOTS_OF_NIGHTMARE_LINING, 1);
-							giveItems(player, SEALED_GAUNTLETS_OF_NIGHTMARE_DESIGN, 1);
-							giveItems(player, SEALED_HELM_OF_NIGHTMARE_DESIGN, 1);
-						} else if (chance < 100) {
-							giveAdena(player, 4000, true);
-						}
+				case CLAUDIA_ATHEBALDT -> {
+					if (hasQuestItems(player, REVELATION_OF_THE_SEALS)) {
+						takeItems(player, 1, REVELATION_OF_THE_SEALS);
+						otherGiveRewards(player, REWARDS_NIGHTMARE, OTHER_CHANCES_HIGH_A);
 						htmltext = "31001-02.html";
 					} else {
 						htmltext = "31001-01.html";
 					}
-					break;
 				}
 			}
 		}
 		return htmltext;
 	}
+	
+	private void walderalGiveRewards(L2PcInstance player, RewardsGroup rewardsGroup, int[] chances) {
+		final int chance = getRandom(100);
+		
+		if (chance < chances[0]) {
+			giveItems(player, rewardsGroup.partBoots, 1);
+		} else if (chance < chances[1]) {
+			giveItems(player, rewardsGroup.partGloves, 1);
+		} else if (chance < chances[2]) {
+			giveItems(player, rewardsGroup.partHelmet, 1);
+		} else if (chance < chances[3]) {
+			giveItems(player, rewardsGroup.partBoots, 1);
+			giveItems(player, rewardsGroup.partGloves, 1);
+			giveItems(player, rewardsGroup.partHelmet, 1);
+		} else if (chance < chances[4]) {
+			giveItems(player, rewardsGroup.recipeBoots, 1);
+		} else if (chance < chances[5]) {
+			giveItems(player, rewardsGroup.recipeGloves, 1);
+		} else if (chance < chances[6]) {
+			giveItems(player, rewardsGroup.recipeHelmet, 1);
+		} else if (chance < chances[7]) {
+			giveItems(player, rewardsGroup.recipeBoots, 1);
+			giveItems(player, rewardsGroup.recipeGloves, 1);
+			giveItems(player, rewardsGroup.recipeHelmet, 1);
+		}
+	}
+	
+	private void otherGiveRewards(L2PcInstance player, RewardsGroup rewardsGroup, int[] chances) {
+		final int chance = getRandom(100);
+		
+		if (chance < chances[0]) {
+			giveItems(player, rewardsGroup.partBoots, 1);
+		} else if (chance < chances[1]) {
+			giveItems(player, rewardsGroup.partGloves, 1);
+		} else if (chance < chances[2]) {
+			giveItems(player, rewardsGroup.partHelmet, 1);
+		} else if (chance < chances[3]) {
+			giveItems(player, rewardsGroup.partBoots, 1);
+			giveItems(player, rewardsGroup.partGloves, 1);
+			giveItems(player, rewardsGroup.partHelmet, 1);
+		} else if (chance < chances[4]) {
+			giveAdena(player, 4000, true);
+		}
+	}
+	
+	private record RewardsGroup(
+			int partBoots, int partGloves, int partHelmet,
+			int recipeBoots, int recipeGloves, int recipeHelmet
+	) {}
 }
diff --git a/src/test/java/com/l2jserver/datapack/quests/Q00372_LegacyOfInsolence/Q00372LegacyOfInsolenceTest.java b/src/test/java/com/l2jserver/datapack/quests/Q00372_LegacyOfInsolence/Q00372LegacyOfInsolenceTest.java
new file mode 100644
index 0000000000..b5d44f933a
--- /dev/null
+++ b/src/test/java/com/l2jserver/datapack/quests/Q00372_LegacyOfInsolence/Q00372LegacyOfInsolenceTest.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright © 2004-2021 L2J DataPack
+ *
+ * This file is part of L2J DataPack.
+ *
+ * L2J DataPack is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J DataPack is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.datapack.quests.Q00372_LegacyOfInsolence;
+
+import com.l2jserver.gameserver.model.actor.L2Npc;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.events.ListenerRegisterType;
+import com.l2jserver.gameserver.model.itemcontainer.PcInventory;
+import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
+import com.l2jserver.gameserver.model.quest.Quest;
+import com.l2jserver.gameserver.model.quest.QuestState;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * TODO: Check NPC rewards (need to be able to stub AbstractScript#getRandom())
+ * TODO: Check Mob drops (need to be able to stub AbstractScript#giveItemRandomly())
+ * @author Noé Caratini aka Kita
+ */
+@ExtendWith(MockitoExtension.class)
+public class Q00372LegacyOfInsolenceTest {
+	private static final String QUEST_NAME = Q00372_LegacyOfInsolence.class.getSimpleName();
+	// Misc
+	private static final int MIN_LEVEL = 59;
+	// NPCs
+	private static final int WRONG_NPC = 1;
+	private static final int TRADER_HOLLY = 30839;
+	private static final int WAREHOUSE_KEEPER_WALDERAL = 30844;
+	private static final int MAGISTER_DESMOND = 30855;
+	private static final int ANTIQUE_DEALER_PATRIN = 30929;
+	private static final int CLAUDIA_ATHEBALDT = 31001;
+	// Monsters
+	private static final int CORRUPT_SAGE = 20817;
+	private static final int ERIN_EDIUNCE = 20821;
+	private static final int HALLATES_INSPECTOR = 20825;
+	private static final int PLATINUM_TRIBE_OVERLORD = 20829;
+	private static final int MESSENGER_ANGEL = 21062;
+	private static final int PLATINUM_GUARDIAN_PREFECT = 21069;
+	// Items
+	private static final List<Integer> REVELATION_OF_THE_SEALS = List.of(5972, 5973, 5974, 5975, 5976, 5977, 5978);
+	private static final List<Integer> ANCIENT_EPIC = List.of(5979, 5980, 5981, 5982, 5983);
+	private static final List<Integer> IMPERIAL_GENEALOGY = List.of(5984, 5985, 5986, 5987, 5988);
+	private static final List<Integer> BLUEPRINTS = List.of(5989, 5990, 5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999, 6000, 6001);
+	
+	@Mock
+	private L2PcInstance player;
+	@Mock
+	private L2Npc npc;
+	@Mock
+	private PcInventory inventory;
+	@Mock
+	private QuestState qs;
+	
+	@Spy
+	private Quest quest = new Q00372_LegacyOfInsolence();
+	
+	@BeforeEach
+	void setUp() {
+		lenient().when(player.getQuestState(QUEST_NAME)).thenReturn(qs);
+		lenient().when(player.getInventory()).thenReturn(inventory);
+	}
+	
+	@Test
+	public void shouldInitQuestCorrectly() {
+		assertThat(quest.getId()).isEqualTo(372);
+		assertThat(quest.getRegisteredIds(ListenerRegisterType.NPC)).containsExactlyInAnyOrder(
+				TRADER_HOLLY,
+				WAREHOUSE_KEEPER_WALDERAL,
+				MAGISTER_DESMOND,
+				ANTIQUE_DEALER_PATRIN,
+				CLAUDIA_ATHEBALDT,
+				CORRUPT_SAGE,
+				ERIN_EDIUNCE,
+				HALLATES_INSPECTOR,
+				PLATINUM_TRIBE_OVERLORD,
+				MESSENGER_ANGEL,
+				PLATINUM_GUARDIAN_PREFECT
+		);
+	}
+	
+	@Test
+	public void onAdvEventWithNoQuestStateShouldReturnNull() {
+		when(player.getQuestState(QUEST_NAME)).thenReturn(null);
+		
+		String result = quest.onAdvEvent("30844-04.htm", npc, player);
+		
+		assertThat(result).isNull();
+	}
+	
+	@Test
+	public void onAdvEventWithUnsupportedEventShouldReturnNull() {
+		String event = "00001-01.htm";
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isNull();
+	}
+	
+	@Test
+	public void onAdvEventWalderalStartQuest() {
+		String event = "30844-04.htm";
+		when(qs.isCreated()).thenReturn(true);
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		verify(qs).startQuest();
+		assertThat(result).isEqualTo(event);
+	}
+	
+	@Test
+	public void onAdvEventWalderalQuestAlreadyStarted() {
+		String event = "30844-04.htm";
+		when(qs.isCreated()).thenReturn(false);
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		verify(qs, never()).startQuest();
+		assertThat(result).isNull();
+	}
+	
+	@Test
+	public void onAdvEventWalderalOtherBooks() {
+		String event = "30844-05b.html";
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		verify(qs).setCond(2);
+		assertThat(result).isEqualTo(event);
+	}
+	
+	@Test
+	public void onAdvEventWalderalNotAllBlueprints() {
+		String event = "30844-07.html";
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo("30844-06.html");
+	}
+	
+	@Test
+	public void onAdvEventWalderalAllBlueprints() {
+		String event = "30844-07.html";
+		L2ItemInstance item = mock(L2ItemInstance.class);
+		BLUEPRINTS.forEach(itemId -> when(inventory.getItemByItemId(itemId)).thenReturn(item));
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo(event);
+	}
+	
+	@Test
+	public void onAdvEventWalderalDarkCrystalAllBlueprints() {
+		String event = "30844-07a.html";
+		BLUEPRINTS.forEach(this::stubInventoryItem);
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo(event);
+	}
+	
+	@Test
+	public void onAdvEventWalderalDarkCrystalNotAllBlueprints() {
+		String event = "30844-07a.html";
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo("30844-07e.html");
+	}
+	
+	@Test
+	public void onAdvEventWalderalTallumAllBlueprints() {
+		String event = "30844-07b.html";
+		BLUEPRINTS.forEach(this::stubInventoryItem);
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo(event);
+	}
+	
+	@Test
+	public void onAdvEventWalderalTallumNotAllBlueprints() {
+		String event = "30844-07b.html";
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo("30844-07e.html");
+	}
+	
+	@Test
+	public void onAdvEventWalderalNightmareAllBlueprints() {
+		String event = "30844-07c.html";
+		BLUEPRINTS.forEach(this::stubInventoryItem);
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo(event);
+	}
+	
+	@Test
+	public void onAdvEventWalderalNightmareNotAllBlueprints() {
+		String event = "30844-07c.html";
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo("30844-07e.html");
+	}
+	
+	@Test
+	public void onAdvEventWalderalMajesticAllBlueprints() {
+		String event = "30844-07c.html";
+		BLUEPRINTS.forEach(this::stubInventoryItem);
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo(event);
+	}
+	
+	@Test
+	public void onAdvEventWalderalMajesticNotAllBlueprints() {
+		String event = "30844-07c.html";
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		assertThat(result).isEqualTo("30844-07e.html");
+	}
+	
+	@Test
+	public void onAdvEventWalderalAbortQuest() {
+		String event = "30844-09.html";
+		
+		String result = quest.onAdvEvent(event, npc, player);
+		
+		verify(qs).exitQuest(true, true);
+		assertThat(result).isEqualTo(event);
+	}
+	
+	@Test
+	public void onAdvEventOtherSupportedEvents() {
+		List.of("30844-03.htm", "30844-05.html", "30844-05a.html", "30844-08.html", "30844-10.html", "30844-11.html")
+				.forEach(event -> {
+					String result = quest.onAdvEvent(event, npc, player);
+					
+					assertThat(result).isEqualTo(event);
+				});
+	}
+	
+	@Test
+	public void onTalkShouldInitQuestStateForPlayer() {
+		when(player.getQuestState(QUEST_NAME)).thenReturn(null);
+		
+		quest.onTalk(npc, player);
+		
+		verify(quest).newQuestState(player);
+	}
+	
+	@Test
+	public void onTalkToWrongNpcWithStateCreated() {
+		when(qs.isCreated()).thenReturn(true);
+		when(npc.getId()).thenReturn(WRONG_NPC);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(Quest.getNoQuestMsg(player));
+	}
+	
+	@Test
+	public void onTalkToWalderalWithStateCreatedAndLevelTooLow() {
+		when(qs.isCreated()).thenReturn(true);
+		when(npc.getId()).thenReturn(WAREHOUSE_KEEPER_WALDERAL);
+		when(player.getLevel()).thenReturn(MIN_LEVEL - 1);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(WAREHOUSE_KEEPER_WALDERAL + "-01.htm");
+	}
+	
+	@Test
+	public void onTalkToWalderalWithStateCreated() {
+		when(qs.isCreated()).thenReturn(true);
+		when(npc.getId()).thenReturn(WAREHOUSE_KEEPER_WALDERAL);
+		when(player.getLevel()).thenReturn(MIN_LEVEL);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(WAREHOUSE_KEEPER_WALDERAL + "-02.htm");
+	}
+	
+	@Test
+	public void onTalkToHollyWithStateCreated() {
+		when(qs.isCreated()).thenReturn(true);
+		when(npc.getId()).thenReturn(TRADER_HOLLY);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(Quest.getNoQuestMsg(player));
+	}
+	
+	@Test
+	public void onTalkToDesmondWithStateCreated() {
+		when(qs.isCreated()).thenReturn(true);
+		when(npc.getId()).thenReturn(MAGISTER_DESMOND);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(Quest.getNoQuestMsg(player));
+	}
+	
+	@Test
+	public void onTalkToPatrinWithStateCreated() {
+		when(qs.isCreated()).thenReturn(true);
+		when(npc.getId()).thenReturn(ANTIQUE_DEALER_PATRIN);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(Quest.getNoQuestMsg(player));
+	}
+	
+	@Test
+	public void onTalkToClaudiaWithStateCreated() {
+		when(qs.isCreated()).thenReturn(true);
+		when(npc.getId()).thenReturn(CLAUDIA_ATHEBALDT);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(Quest.getNoQuestMsg(player));
+	}
+	
+	@Test
+	public void onTalkToWrongNpcWithStateStarted() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(WRONG_NPC);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(Quest.getNoQuestMsg(player));
+	}
+	
+	@Test
+	public void onTalkToWalderalWithStateStarted() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(WAREHOUSE_KEEPER_WALDERAL);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(WAREHOUSE_KEEPER_WALDERAL + "-05.html");
+	}
+	
+	@Test
+	public void onTalkToHollyWithStateStarted() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(TRADER_HOLLY);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(TRADER_HOLLY + "-01.html");
+	}
+	
+	@Test
+	public void onTalkToHollyWithStateStartedAndAllItems() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(TRADER_HOLLY);
+		IMPERIAL_GENEALOGY.forEach(this::stubInventoryItem);
+		
+		String result = quest.onTalk(npc, player);
+		
+		List<Integer> removedItemIds = verifyItemsRemoved(player, IMPERIAL_GENEALOGY);
+		assertThat(removedItemIds).containsExactlyInAnyOrderElementsOf(IMPERIAL_GENEALOGY);
+		assertThat(result).isEqualTo(TRADER_HOLLY + "-02.html");
+	}
+	
+	@Test
+	public void onTalkToDesmondWithStateStarted() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(MAGISTER_DESMOND);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(MAGISTER_DESMOND + "-01.html");
+	}
+	
+	@Test
+	public void onTalkToDesmondWithStateStartedAndAllItems() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(MAGISTER_DESMOND);
+		REVELATION_OF_THE_SEALS.forEach(this::stubInventoryItem);
+		
+		String result = quest.onTalk(npc, player);
+		
+		List<Integer> removedItemIds = verifyItemsRemoved(player, REVELATION_OF_THE_SEALS);
+		assertThat(removedItemIds).containsExactlyInAnyOrderElementsOf(REVELATION_OF_THE_SEALS);
+		assertThat(result).isEqualTo(MAGISTER_DESMOND + "-02.html");
+	}
+	
+	@Test
+	public void onTalkToPatrinWithStateStarted() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(ANTIQUE_DEALER_PATRIN);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(ANTIQUE_DEALER_PATRIN + "-01.html");
+	}
+	
+	@Test
+	public void onTalkToPatrinWithStateStartedAndAllItems() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(ANTIQUE_DEALER_PATRIN);
+		ANCIENT_EPIC.forEach(this::stubInventoryItem);
+		
+		String result = quest.onTalk(npc, player);
+		
+		List<Integer> removedItemIds = verifyItemsRemoved(player, ANCIENT_EPIC);
+		assertThat(removedItemIds).containsExactlyInAnyOrderElementsOf(ANCIENT_EPIC);
+		assertThat(result).isEqualTo(ANTIQUE_DEALER_PATRIN + "-02.html");
+	}
+	
+	@Test
+	public void onTalkToClaudiaWithStateStarted() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(CLAUDIA_ATHEBALDT);
+		
+		String result = quest.onTalk(npc, player);
+		
+		assertThat(result).isEqualTo(CLAUDIA_ATHEBALDT + "-01.html");
+	}
+	
+	@Test
+	public void onTalkToClaudiaWithStateStartedAndAllItems() {
+		when(qs.isStarted()).thenReturn(true);
+		when(npc.getId()).thenReturn(CLAUDIA_ATHEBALDT);
+		REVELATION_OF_THE_SEALS.forEach(this::stubInventoryItem);
+		
+		String result = quest.onTalk(npc, player);
+		
+		List<Integer> removedItemIds = verifyItemsRemoved(player, REVELATION_OF_THE_SEALS);
+		assertThat(removedItemIds).containsExactlyInAnyOrderElementsOf(REVELATION_OF_THE_SEALS);
+		assertThat(result).isEqualTo(CLAUDIA_ATHEBALDT + "-02.html");
+	}
+	
+	@Test
+	public void onKillCorruptSageShouldGetQuestStateWithCorrectPlayerChance() {
+		when(npc.getId()).thenReturn(CORRUPT_SAGE);
+		
+		quest.onKill(npc, player, false);
+		
+		verify(quest).getRandomPartyMemberState(player, -1, 1, npc);
+	}
+	
+	@Test
+	public void onKillErinEdiunceShouldGetQuestStateWithCorrectPlayerChance() {
+		when(npc.getId()).thenReturn(ERIN_EDIUNCE);
+		
+		quest.onKill(npc, player, false);
+		
+		verify(quest).getRandomPartyMemberState(player, -1, 1, npc);
+	}
+	
+	@Test
+	public void onKillHallatesInspectorShouldGetQuestStateWithCorrectPlayerChance() {
+		when(npc.getId()).thenReturn(HALLATES_INSPECTOR);
+		
+		quest.onKill(npc, player, false);
+		
+		verify(quest).getRandomPartyMemberState(player, -1, 3, npc);
+	}
+	
+	@Test
+	public void onKillPlatinumOverlordShouldGetQuestStateWithCorrectPlayerChance() {
+		when(npc.getId()).thenReturn(PLATINUM_TRIBE_OVERLORD);
+		
+		quest.onKill(npc, player, false);
+		
+		verify(quest).getRandomPartyMemberState(player, -1, 3, npc);
+	}
+	
+	@Test
+	public void onKillMessengerAngelShouldGetQuestStateWithCorrectPlayerChance() {
+		when(npc.getId()).thenReturn(MESSENGER_ANGEL);
+		
+		quest.onKill(npc, player, false);
+		
+		verify(quest).getRandomPartyMemberState(player, -1, 1, npc);
+	}
+	
+	@Test
+	public void onKillPlatinumPrefectShouldGetQuestStateWithCorrectPlayerChance() {
+		when(npc.getId()).thenReturn(PLATINUM_GUARDIAN_PREFECT);
+		
+		quest.onKill(npc, player, false);
+		
+		verify(quest).getRandomPartyMemberState(player, -1, 1, npc);
+	}
+	
+	private void stubInventoryItem(Integer itemId) {
+		L2ItemInstance item = mock(L2ItemInstance.class);
+		when(item.getId()).thenReturn(itemId);
+		when(item.getCount()).thenReturn(2L);
+		when(inventory.getItemByItemId(itemId)).thenReturn(item);
+		when(inventory.getItemsByItemId(itemId)).thenReturn(List.of(item));
+	}
+	
+	private List<Integer> verifyItemsRemoved(L2PcInstance player, List<Integer> itemIds) {
+		ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);
+		verify(player, times(itemIds.size())).destroyItemByItemId(
+				eq("Quest"),
+				intCaptor.capture(),
+				eq(1L),
+				any(),
+				anyBoolean());
+		return intCaptor.getAllValues();
+	}
+}
-- 
GitLab