Prechádzať zdrojové kódy

BETA: Skill Trees rework:
* Complete rework for Skill Trees, done in XML, removed SQL part.
* Better implementation of Lucky Skill (194) (still hardcoded).
* Unhardcoded Expertise Skill (239).
* Unhardcoded Dwarven Craft Skill (1321).
* Unhardcoded Common Craft Skill (1322).
* Support for autoGet Skills.
* Complete rework for RequestAcquireSkill and RequestAcquireSkillInfo.
* Fixed bug for Clan Skills not being added until restart.
* Added multiple exploits check directly in packets.
'''NOTE: Require [DP8052]'''

Zoey76 14 rokov pred
rodič
commit
1272fec463
43 zmenil súbory, kde vykonal 2872 pridanie a 2557 odobranie
  1. 5 4
      L2J_Server_BETA/java/com/l2jserver/Config.java
  2. 2 9
      L2J_Server_BETA/java/com/l2jserver/gameserver/GameServer.java
  3. 0 95
      L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/ResidentialSkillTable.java
  4. 0 116
      L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillSpellbookTable.java
  5. 3 11
      L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillTable.java
  6. 0 850
      L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillTreeTable.java
  7. 1196 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillTreesData.java
  8. 0 235
      L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SubPledgeSkillTree.java
  9. 44 7
      L2J_Server_BETA/java/com/l2jserver/gameserver/instancemanager/TerritoryWarManager.java
  10. 14 15
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2Clan.java
  11. 1 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2Object.java
  12. 0 96
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2PledgeSkillLearn.java
  13. 2 3
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2Skill.java
  14. 173 58
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2SkillLearn.java
  15. 0 94
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2TransformSkillLearn.java
  16. 9 7
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2CastleMagicianInstance.java
  17. 1 1
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2ClassMasterInstance.java
  18. 36 21
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2FishermanInstance.java
  19. 24 16
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2FortSupportCaptainInstance.java
  20. 67 35
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2NpcInstance.java
  21. 110 191
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java
  22. 190 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerHealersInstance.java
  23. 2 10
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerInstance.java
  24. 243 30
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2TransformManagerInstance.java
  25. 13 15
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterInstance.java
  26. 4 3
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/stat/PcStat.java
  27. 4 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/base/SubClass.java
  28. 53 7
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/entity/Castle.java
  29. 16 3
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/entity/Fort.java
  30. 6 6
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/SystemMessageId.java
  31. 9 15
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/CharacterCreate.java
  32. 354 353
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestAcquireSkill.java
  33. 203 139
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestAcquireSkillInfo.java
  34. 6 10
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestSkillList.java
  35. 4 7
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/AcquireSkillDone.java
  36. 16 17
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/AcquireSkillInfo.java
  37. 31 41
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/AcquireSkillList.java
  38. 17 23
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/PledgeSkillList.java
  39. 2 2
      L2J_Server_BETA/java/com/l2jserver/gameserver/script/EngineInterface.java
  40. 2 6
      L2J_Server_BETA/java/com/l2jserver/gameserver/skills/conditions/ConditionPlayerGrade.java
  41. 1 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/templates/skills/L2EffectType.java
  42. 0 1
      L2J_Server_BETA/java/com/l2jserver/gameserver/templates/skills/L2SkillType.java
  43. 9 5
      L2J_Server_BETA/java/config/Character.properties

+ 5 - 4
L2J_Server_BETA/java/com/l2jserver/Config.java

@@ -105,6 +105,7 @@ public final class Config
 	public static boolean ENABLE_MODIFY_SKILL_REUSE;
 	public static TIntIntHashMap SKILL_REUSE_LIST;
 	public static boolean AUTO_LEARN_SKILLS;
+	public static boolean AUTO_LEARN_FS_SKILLS;
 	public static boolean AUTO_LOOT_HERBS;
 	public static byte BUFFS_MAX_AMOUNT;
 	public static byte DANCES_MAX_AMOUNT;
@@ -126,12 +127,12 @@ public final class Config
 	public static boolean ALLOW_ENTIRE_TREE;
 	public static boolean ALTERNATE_CLASS_MASTER;
 	public static boolean LIFE_CRYSTAL_NEEDED;
-	public static boolean SP_BOOK_NEEDED;
 	public static boolean ES_SP_BOOK_NEEDED;
 	public static boolean DIVINE_SP_BOOK_NEEDED;
 	public static boolean ALT_GAME_SKILL_LEARN;
 	public static boolean ALT_GAME_SUBCLASS_WITHOUT_QUESTS;
 	public static boolean ALT_GAME_SUBCLASS_EVERYWHERE;
+	public static boolean ALLOW_TRANSFORM_WITHOUT_QUEST;
 	public static boolean RESTORE_SERVITOR_ON_RECONNECT;
 	public static boolean RESTORE_PET_ON_RECONNECT;
 	public static int MAX_RUN_SPEED;
@@ -1525,7 +1526,8 @@ public final class Config
 						}
 					}
 					
-					AUTO_LEARN_SKILLS = Boolean.parseBoolean(Character.getProperty("AutoLearnSkills", "false"));
+					AUTO_LEARN_SKILLS = Boolean.parseBoolean(Character.getProperty("AutoLearnSkills", "False"));
+					AUTO_LEARN_FS_SKILLS = Boolean.parseBoolean(Character.getProperty("AutoLearnForgottenScrollSkills", "False"));
 					AUTO_LOOT_HERBS = Boolean.parseBoolean(Character.getProperty("AutoLootHerbs", "false"));
 					BUFFS_MAX_AMOUNT = Byte.parseByte(Character.getProperty("maxbuffamount","20"));
 					DANCES_MAX_AMOUNT = Byte.parseByte(Character.getProperty("maxdanceamount","12"));
@@ -1548,7 +1550,6 @@ public final class Config
 					if (ALLOW_CLASS_MASTERS || ALTERNATE_CLASS_MASTER)
 						CLASS_MASTER_SETTINGS = new ClassMasterSettings(Character.getProperty("ConfigClassMaster"));
 					LIFE_CRYSTAL_NEEDED = Boolean.parseBoolean(Character.getProperty("LifeCrystalNeeded", "true"));
-					SP_BOOK_NEEDED = Boolean.parseBoolean(Character.getProperty("SpBookNeeded", "false"));
 					ES_SP_BOOK_NEEDED = Boolean.parseBoolean(Character.getProperty("EnchantSkillSpBookNeeded","true"));
 					DIVINE_SP_BOOK_NEEDED = Boolean.parseBoolean(Character.getProperty("DivineInspirationSpBookNeeded", "true"));
 					ALT_GAME_SKILL_LEARN = Boolean.parseBoolean(Character.getProperty("AltGameSkillLearn", "false"));
@@ -1556,6 +1557,7 @@ public final class Config
 					ALT_GAME_SUBCLASS_EVERYWHERE = Boolean.parseBoolean(Character.getProperty("AltSubclassEverywhere", "False"));
 					RESTORE_SERVITOR_ON_RECONNECT = Boolean.parseBoolean(Character.getProperty("RestoreServitorOnReconnect", "True"));
 					RESTORE_PET_ON_RECONNECT = Boolean.parseBoolean(Character.getProperty("RestorePetOnReconnect", "True"));
+					ALLOW_TRANSFORM_WITHOUT_QUEST = Boolean.parseBoolean(Character.getProperty("AltTransfomarionWithoutQuest", "False"));
 					ENABLE_VITALITY = Boolean.parseBoolean(Character.getProperty("EnableVitality", "True"));
 					RECOVER_VITALITY_ON_RECONNECT = Boolean.parseBoolean(Character.getProperty("RecoverVitalityOnReconnect", "True"));
 					STARTING_VITALITY_POINTS = Integer.parseInt(Character.getProperty("StartingVitalityPoints", "20000"));
@@ -3214,7 +3216,6 @@ public final class Config
 		else if (pName.equalsIgnoreCase("CraftingEnabled")) IS_CRAFTING_ENABLED = Boolean.parseBoolean(pValue);
 		else if (pName.equalsIgnoreCase("CraftMasterwork")) CRAFT_MASTERWORK = Boolean.parseBoolean(pValue);
 		else if (pName.equalsIgnoreCase("LifeCrystalNeeded")) LIFE_CRYSTAL_NEEDED = Boolean.parseBoolean(pValue);
-		else if (pName.equalsIgnoreCase("SpBookNeeded")) SP_BOOK_NEEDED = Boolean.parseBoolean(pValue);
 		else if (pName.equalsIgnoreCase("AutoLoot")) AUTO_LOOT = Boolean.parseBoolean(pValue);
 		else if (pName.equalsIgnoreCase("AutoLootRaids")) AUTO_LOOT_RAIDS = Boolean.parseBoolean(pValue);
 		else if (pName.equalsIgnoreCase("AutoLootHerbs")) AUTO_LOOT_HERBS = Boolean.parseBoolean(pValue);

+ 2 - 9
L2J_Server_BETA/java/com/l2jserver/gameserver/GameServer.java

@@ -65,13 +65,10 @@ import com.l2jserver.gameserver.datatables.NpcTable;
 import com.l2jserver.gameserver.datatables.NpcWalkerRoutesTable;
 import com.l2jserver.gameserver.datatables.OfflineTradersTable;
 import com.l2jserver.gameserver.datatables.PetDataTable;
-import com.l2jserver.gameserver.datatables.ResidentialSkillTable;
-import com.l2jserver.gameserver.datatables.SkillSpellbookTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.datatables.SpawnTable;
 import com.l2jserver.gameserver.datatables.StaticObjects;
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree;
 import com.l2jserver.gameserver.datatables.SummonItemsData;
 import com.l2jserver.gameserver.datatables.SummonSkillsTable;
 import com.l2jserver.gameserver.datatables.TeleportLocationTable;
@@ -221,13 +218,10 @@ public class GameServer
 		printSection("Skills");
 		EnchantGroupsTable.getInstance();
 		SkillTable.getInstance();
-		SkillTreeTable.getInstance();
+		SkillTreesData.getInstance();
 		NobleSkillTable.getInstance();
 		GMSkillTable.getInstance();
 		HeroSkillTable.getInstance();
-		ResidentialSkillTable.getInstance();
-		SkillSpellbookTable.getInstance();
-		SubPledgeSkillTree.getInstance();
 		SummonSkillsTable.getInstance();
 		
 		printSection("Items");
@@ -240,7 +234,6 @@ public class GameServer
 		RecipeController.getInstance();
 		ArmorSetsTable.getInstance();
 		FishTable.getInstance();
-		SkillSpellbookTable.getInstance();
 		
 		printSection("Characters");
 		CharTemplateTable.getInstance();

+ 0 - 95
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/ResidentialSkillTable.java

@@ -1,95 +0,0 @@
-package com.l2jserver.gameserver.datatables;
-
-import gnu.trove.TIntObjectHashMap;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javolution.util.FastList;
-
-import com.l2jserver.L2DatabaseFactory;
-import com.l2jserver.gameserver.model.L2Skill;
-
-/**
- * Warning: must be loaded after loading SkillTable
- *
- * @author  DrHouse
- */
-public class ResidentialSkillTable
-{
-	private static Logger _log = Logger.getLogger(ResidentialSkillTable.class.getName());
-	
-	private static ResidentialSkillTable _instance = null;
-	private static TIntObjectHashMap<FastList<L2Skill>> _list;
-	
-	ResidentialSkillTable()
-	{
-		load();
-	}
-	
-	private void load()
-	{
-		_list = new TIntObjectHashMap<FastList<L2Skill>>();
-		Connection con = null;
-		
-		try
-		{
-			con = L2DatabaseFactory.getInstance().getConnection();
-			PreparedStatement statement = con.prepareStatement("SELECT * FROM skill_residential ORDER BY entityId");
-			ResultSet rs = statement.executeQuery();
-			
-			while (rs.next())
-			{
-				int entityId = rs.getInt("entityId");
-				int skillId = rs.getInt("skillId");
-				int skillLvl = rs.getInt("skillLevel");
-				
-				L2Skill sk = SkillTable.getInstance().getInfo(skillId, skillLvl);
-				
-				if (sk == null)
-				{
-					_log.warning("ResidentialSkillTable: SkillTable has returned null for ID/level: " +skillId+"/"+skillLvl);
-					continue;
-				}
-				if (!_list.containsKey(entityId))
-				{
-					FastList<L2Skill> aux = new FastList<L2Skill>();
-					aux.add(sk);
-					_list.put(entityId, aux);
-				}
-				else
-					_list.get(entityId).add(sk);
-			}
-			rs.close();
-			statement.close();
-		}
-		catch (Exception e)
-		{
-			_log.log(Level.WARNING, "ResidentialSkillTable: a problem occured while loading skills! " + e.getMessage(), e);
-		}
-		finally
-		{
-			L2DatabaseFactory.close(con);
-			_log.info("ResidentialSkillTable: Loaded " + _list.size() + " entities with associated skills.");
-		}
-	}
-	
-	public FastList<L2Skill> getSkills(int entityId)
-	{
-		if (_list.containsKey(entityId))
-			return _list.get(entityId);
-		
-		return null;
-	}
-	
-	public static ResidentialSkillTable getInstance()
-	{
-		if (_instance == null)
-			_instance = new ResidentialSkillTable();
-		
-		return _instance;
-	}
-}

+ 0 - 116
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillSpellbookTable.java

@@ -1,116 +0,0 @@
-/*
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- * 
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.datatables;
-
-import gnu.trove.TIntIntHashMap;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.l2jserver.Config;
-import com.l2jserver.L2DatabaseFactory;
-import com.l2jserver.gameserver.model.L2Skill;
-
-
-public class SkillSpellbookTable
-{
-	private static Logger _log = Logger.getLogger(SkillTreeTable.class.getName());
-	
-	private static TIntIntHashMap _skillSpellbooks;
-	
-	public static SkillSpellbookTable getInstance()
-	{
-		return SingletonHolder._instance;
-	}
-	
-	private SkillSpellbookTable()
-	{
-		if (!Config.SP_BOOK_NEEDED)
-			return;
-		
-		_skillSpellbooks = new TIntIntHashMap();
-		Connection con = null;
-		
-		try
-		{
-			con = L2DatabaseFactory.getInstance().getConnection();
-			PreparedStatement statement = con.prepareStatement("SELECT skill_id, item_id FROM skill_spellbooks");
-			ResultSet spbooks = statement.executeQuery();
-			
-			while (spbooks.next())
-				_skillSpellbooks.put(spbooks.getInt("skill_id"), spbooks.getInt("item_id"));
-			
-			spbooks.close();
-			statement.close();
-			
-			_log.info("SkillSpellbookTable: Loaded " + _skillSpellbooks.size() + " Spellbooks.");
-		}
-		catch (Exception e)
-		{
-			_log.log(Level.WARNING, "Error while loading spellbook data: " + e.getMessage(), e);
-		}
-		finally
-		{
-			L2DatabaseFactory.close(con);
-		}
-	}
-	
-	public int getBookForSkill(int skillId, int level)
-	{
-		if (skillId == L2Skill.SKILL_DIVINE_INSPIRATION && level != -1)
-		{
-			switch (level)
-			{
-				case 1:
-					return 8618; // Ancient Book - Divine Inspiration (Modern Language Version)
-				case 2:
-					return 8619; // Ancient Book - Divine Inspiration (Original Language Version)
-				case 3:
-					return 8620; // Ancient Book - Divine Inspiration (Manuscript)
-				case 4:
-					return 8621; // Ancient Book - Divine Inspiration (Original Version)
-				default:
-					return -1;
-			}
-		}
-		
-		if (!Config.SP_BOOK_NEEDED)
-			return (-1);
-		
-		if (!_skillSpellbooks.containsKey(skillId))
-			return -1;
-		
-		return _skillSpellbooks.get(skillId);
-	}
-	
-	public int getBookForSkill(L2Skill skill)
-	{
-		return getBookForSkill(skill.getId(), -1);
-	}
-	
-	public int getBookForSkill(L2Skill skill, int level)
-	{
-		return getBookForSkill(skill.getId(), level);
-	}
-	
-	@SuppressWarnings("synthetic-access")
-	private static class SingletonHolder
-	{
-		protected static final SkillSpellbookTable _instance = new SkillSpellbookTable();
-	}
-}

+ 3 - 11
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillTable.java

@@ -48,10 +48,8 @@ public class SkillTable
 	public void reload()
 	{
 		load();
-		
-		//reload some related too
-		SkillTreeTable.getInstance().reload();
-		SubPledgeSkillTree.getInstance().reload();
+		//Reload Skill Tree as well.
+		SkillTreesData.getInstance().load();
 	}
 	
 	private void load()
@@ -76,7 +74,6 @@ public class SkillTable
 			if (skillLvl > maxLvl)
 				_skillMaxLevel.put(skillId, skillLvl);
 		}
-		//SkillTreeTable.getInstance().reload();
 		
 		// Sorting for binarySearch
 		_enchantable.sort();
@@ -168,7 +165,6 @@ public class SkillTable
 	 * Enum to hold some important references to frequently used (hardcoded) skills in core
 	 * 
 	 * @author DrHouse
-	 *
 	 */
 	public static enum FrequentSkill
 	{
@@ -176,9 +172,6 @@ public class SkillTable
 		RAID_CURSE2(4515, 1),
 		SEAL_OF_RULER(246, 1),
 		BUILD_HEADQUARTERS(247, 1),
-		LUCKY(194, 1),
-		DWARVEN_CRAFT(1321, 1),
-		COMMON_CRAFT(1322, 1),
 		WYVERN_BREATH(4289, 1),
 		STRIDER_SIEGE_ASSAULT(325, 1),
 		FAKE_PETRIFICATION(4616, 1),
@@ -206,6 +199,5 @@ public class SkillTable
 		{
 			return _skill;
 		}
-		
 	}
-}
+}

+ 0 - 850
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillTreeTable.java

@@ -1,850 +0,0 @@
-/*
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- * 
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.datatables;
-
-import gnu.trove.TIntObjectHashMap;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javolution.util.FastList;
-import javolution.util.FastMap;
-
-import com.l2jserver.L2DatabaseFactory;
-import com.l2jserver.gameserver.model.L2PledgeSkillLearn;
-import com.l2jserver.gameserver.model.L2Skill;
-import com.l2jserver.gameserver.model.L2SkillLearn;
-import com.l2jserver.gameserver.model.L2TransformSkillLearn;
-import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jserver.gameserver.model.base.ClassId;
-import com.l2jserver.gameserver.model.base.Race;
-
-/**
- * This class ...
- *
- * @version $Revision: 1.13.2.2.2.8 $ $Date: 2005/04/06 16:13:25 $
- */
-public class SkillTreeTable
-{
-	private static Logger _log = Logger.getLogger(SkillTreeTable.class.getName());
-	
-	private Map<ClassId, Map<Integer, L2SkillLearn>> _skillTrees;
-	private List<L2SkillLearn> _fishingSkillTrees; //all common skills (teached by Fisherman)
-	private List<L2SkillLearn> _expandDwarfCraftSkillTrees; //list of special skill for dwarf (expand dwarf craft) learned by class teacher
-	private List<L2PledgeSkillLearn> _pledgeSkillTrees; //pledge skill list
-	private List<L2TransformSkillLearn> _TransformSkillTrees; // Transform Skills (Test)
-	private FastList<L2SkillLearn> _specialSkillTrees;
-	
-	// checker, sorted arrays of hashcodes
-	private TIntObjectHashMap<int[]> _skillsByClassIdHashCodes; // occupation skills
-	private TIntObjectHashMap<int[]> _skillsByRaceHashCodes; // race-specific transformations
-	private int[] _allSkillsHashCodes; // fishing, special and all races transformations
-	
-	private boolean _loading = true;
-	
-	public static SkillTreeTable getInstance()
-	{
-		return SingletonHolder._instance;
-	}
-	
-	
-	private SkillTreeTable()
-	{
-		load();
-	}
-	
-	/**
-	 * Return the minimum level needed to have this Expertise.<BR><BR>
-	 *
-	 * @param grade The grade level searched
-	 */
-	public int getExpertiseLevel(int grade)
-	{
-		if (grade <= 0)
-			return 0;
-		
-		// since expertise comes at same level for all classes we use paladin for now
-		Map<Integer, L2SkillLearn> learnMap = getSkillTrees().get(ClassId.paladin);
-		
-		int skillHashCode = SkillTable.getSkillHashCode(239, grade);
-		if (learnMap.containsKey(skillHashCode))
-		{
-			return learnMap.get(skillHashCode).getMinLevel();
-		}
-		
-		_log.severe("Expertise not found for grade " + grade);
-		return 0;
-	}
-	
-	/**
-	 * Each class receives new skill on certain levels, this methods allow the retrieval of the minimun character level
-	 * of given class required to learn a given skill
-	 * @param skillId The iD of the skill
-	 * @param classID The classId of the character
-	 * @param skillLvl The SkillLvl
-	 * @return The min level
-	 */
-	public int getMinSkillLevel(int skillId, ClassId classId, int skillLvl)
-	{
-		Map<Integer, L2SkillLearn> map = getSkillTrees().get(classId);
-		
-		int skillHashCode = SkillTable.getSkillHashCode(skillId, skillLvl);
-		
-		if (map.containsKey(skillHashCode))
-		{
-			return map.get(skillHashCode).getMinLevel();
-		}
-		
-		return 0;
-	}
-	
-	public int getMinSkillLevel(int skillId, int skillLvl)
-	{
-		int skillHashCode = SkillTable.getSkillHashCode(skillId, skillLvl);
-		
-		// Look on all classes for this skill (takes the first one found)
-		for (Map<Integer, L2SkillLearn> map : getSkillTrees().values())
-		{
-			// checks if the current class has this skill
-			if (map.containsKey(skillHashCode))
-			{
-				return map.get(skillHashCode).getMinLevel();
-			}
-		}
-		return 0;
-	}
-	
-	private void load()
-	{
-		_loading = true;
-		int classId = 0;
-		int count = 0;
-		Connection con = null;
-		
-		try
-		{
-			con = L2DatabaseFactory.getInstance().getConnection();
-			try
-			{
-				PreparedStatement statement = con.prepareStatement("SELECT * FROM class_list ORDER BY id");
-				ResultSet classlist = statement.executeQuery();
-				
-				Map<Integer, L2SkillLearn> map;
-				int parentClassId;
-				L2SkillLearn skillLearn;
-				
-				PreparedStatement statement2 = con.prepareStatement("SELECT class_id, skill_id, level, name, sp, min_level, learned_by_npc, learned_by_fs, is_transfer, is_autoget FROM skill_trees where class_id=? ORDER BY skill_id, level");
-				while (classlist.next())
-				{
-					map = new FastMap<Integer, L2SkillLearn>();
-					parentClassId = classlist.getInt("parent_id");
-					classId = classlist.getInt("id");
-					
-					statement2.setInt(1, classId);
-					ResultSet skilltree = statement2.executeQuery();
-					statement2.clearParameters();
-					
-					if (parentClassId != -1)
-					{
-						Map<Integer, L2SkillLearn> parentMap = getSkillTrees().get(ClassId.values()[parentClassId]);
-						map.putAll(parentMap);
-					}
-					
-					int prevSkillId = -1;
-					
-					while (skilltree.next())
-					{
-						int id = skilltree.getInt("skill_id");
-						int lvl = skilltree.getInt("level");
-						String name = skilltree.getString("name");
-						int minLvl = skilltree.getInt("min_level");
-						int cost = skilltree.getInt("sp");
-						boolean npc = skilltree.getBoolean("learned_by_npc");
-						boolean fs = skilltree.getBoolean("learned_by_fs");
-						boolean trans = skilltree.getBoolean("is_transfer");
-						boolean autoget = skilltree.getBoolean("is_autoget");
-						
-						if (prevSkillId != id)
-							prevSkillId = id;
-						
-						skillLearn = new L2SkillLearn(id, lvl, minLvl, name, cost, 0, 0, npc, fs, trans, autoget);
-						map.put(SkillTable.getSkillHashCode(id, lvl), skillLearn);
-					}
-					
-					getSkillTrees().put(ClassId.values()[classId], map);
-					skilltree.close();
-					
-					count += map.size();
-					_log.fine("SkillTreeTable: skill tree for class " + classId + " has " + map.size() + " skills");
-				}
-				
-				classlist.close();
-				statement.close();
-				statement2.close();
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, "Error while creating skill tree (Class ID " + classId + "): " + e.getMessage(), e);
-			}
-			
-			_log.info("SkillTreeTable: Loaded " + count + " skills.");
-			
-			//Skill tree for fishing skill (from Fisherman)
-			try
-			{
-				_fishingSkillTrees = new FastList<L2SkillLearn>();
-				_expandDwarfCraftSkillTrees = new FastList<L2SkillLearn>();
-				
-				PreparedStatement statement = con.prepareStatement("SELECT skill_id, level, name, sp, min_level, costid, cost, is_for_dwarf, learned_by_npc, learned_by_fs FROM fishing_skill_trees ORDER BY skill_id, level");
-				ResultSet skilltree2 = statement.executeQuery();
-				
-				int prevSkillId = -1;
-				
-				while (skilltree2.next())
-				{
-					int id = skilltree2.getInt("skill_id");
-					int lvl = skilltree2.getInt("level");
-					String name = skilltree2.getString("name");
-					int minLvl = skilltree2.getInt("min_level");
-					int cost = skilltree2.getInt("sp");
-					int costId = skilltree2.getInt("costid");
-					int costCount = skilltree2.getInt("cost");
-					boolean isDwarven = skilltree2.getBoolean("is_for_dwarf");
-					boolean npc = skilltree2.getBoolean("learned_by_npc");
-					boolean fs = skilltree2.getBoolean("learned_by_fs");
-					
-					if (prevSkillId != id)
-						prevSkillId = id;
-					
-					L2SkillLearn skill = new L2SkillLearn(id, lvl, minLvl, name, cost, costId, costCount, npc, fs, false, false);
-					
-					if (isDwarven)
-						_expandDwarfCraftSkillTrees.add(skill);
-					else
-						_fishingSkillTrees.add(skill);
-				}
-				
-				skilltree2.close();
-				statement.close();
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, "Error while creating fishing skill table: " + e.getMessage(), e);
-			}
-			
-			try
-			{
-				_pledgeSkillTrees = new FastList<L2PledgeSkillLearn>();
-				
-				PreparedStatement statement = con.prepareStatement("SELECT skill_id, level, name, clan_lvl, repCost, itemId, itemCount FROM pledge_skill_trees ORDER BY skill_id, level");
-				ResultSet skilltree4 = statement.executeQuery();
-				
-				int prevSkillId = -1;
-				
-				while (skilltree4.next())
-				{
-					int id = skilltree4.getInt("skill_id");
-					int lvl = skilltree4.getInt("level");
-					String name = skilltree4.getString("name");
-					int baseLvl = skilltree4.getInt("clan_lvl");
-					int sp = skilltree4.getInt("repCost");
-					int itemId = skilltree4.getInt("itemId");
-					int itemCount = skilltree4.getInt("itemCount");
-					
-					if (prevSkillId != id)
-						prevSkillId = id;
-					
-					L2PledgeSkillLearn skill = new L2PledgeSkillLearn(id, lvl, baseLvl, name, sp, itemId, itemCount);
-					
-					_pledgeSkillTrees.add(skill);
-				}
-				
-				skilltree4.close();
-				statement.close();
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, "Error while creating pledge skill table: " + e.getMessage(), e);
-			}
-			try
-			{
-				_TransformSkillTrees = new FastList<L2TransformSkillLearn>();
-				
-				PreparedStatement statement = con.prepareStatement("SELECT race_id, skill_id, item_id, level, name, sp, min_level FROM transform_skill_trees ORDER BY race_id, skill_id, level");
-				ResultSet skilltree5 = statement.executeQuery();
-				
-				int prevSkillId = -1;
-				
-				while (skilltree5.next())
-				{
-					int race_id = skilltree5.getInt("race_id");
-					int skill_id = skilltree5.getInt("skill_id");
-					int item_id = skilltree5.getInt("item_id");
-					int level = skilltree5.getInt("level");
-					String name = skilltree5.getString("name");
-					int sp = skilltree5.getInt("sp");
-					int min_level = skilltree5.getInt("min_level");
-					
-					if (prevSkillId != skill_id)
-						prevSkillId = skill_id;
-					
-					L2TransformSkillLearn skill = new L2TransformSkillLearn(race_id, skill_id, item_id, level, name, sp, min_level);
-					
-					_TransformSkillTrees.add(skill);
-				}
-				
-				skilltree5.close();
-				statement.close();
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, "Error while creating Transformation skill table ", e);
-			}
-			try
-			{
-				_specialSkillTrees = new FastList<L2SkillLearn>();
-				
-				PreparedStatement statement = con.prepareStatement("SELECT skill_id, level, name, costid, cost, learned_by_npc, learned_by_fs FROM special_skill_trees ORDER BY skill_id, level");
-				ResultSet skilltree6 = statement.executeQuery();
-				
-				int prevSkillId = -1;
-				
-				while (skilltree6.next())
-				{
-					int id = skilltree6.getInt("skill_id");
-					int lvl = skilltree6.getInt("level");
-					String name = skilltree6.getString("name");
-					int costId = skilltree6.getInt("costid");
-					int costCount = skilltree6.getInt("cost");
-					boolean npc = skilltree6.getBoolean("learned_by_npc");
-					boolean fs = skilltree6.getBoolean("learned_by_fs");
-					
-					if (prevSkillId != id)
-						prevSkillId = id;
-					
-					L2SkillLearn skill = new L2SkillLearn(id, lvl, 0, name, 0, costId, costCount, npc, fs, false, false);
-					
-					_specialSkillTrees.add(skill);
-				}
-				
-				skilltree6.close();
-				statement.close();
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, "Error while creating special skill table: " + e.getMessage(), e);
-			}
-		}
-		catch (Exception e)
-		{
-			_log.log(Level.SEVERE, "Error while skill tables ", e);
-		}
-		finally
-		{
-			L2DatabaseFactory.close(con);
-		}
-		
-		generateCheckArrays();
-		
-		_log.info("FishingSkillTreeTable: Loaded " + _fishingSkillTrees.size() + " general skills.");
-		_log.info("DwarvenCraftSkillTreeTable: Loaded " + _expandDwarfCraftSkillTrees.size() + " dwarven skills.");
-		_log.info("PledgeSkillTreeTable: Loaded " + _pledgeSkillTrees.size() + " pledge skills");
-		_log.info("TransformSkillTreeTable: Loaded " + _TransformSkillTrees.size() + " transform skills");
-		_log.info("SpecialSkillTreeTable: Loaded " + _specialSkillTrees.size() + " special skills");
-		_loading = false;
-	}
-	
-	private void generateCheckArrays()
-	{
-		int i;
-		int[] array;
-		
-		// class-specific skills
-		Map<Integer, L2SkillLearn> tempMap;
-		TIntObjectHashMap<int[]> result = new TIntObjectHashMap<int[]>(_skillTrees.keySet().size());
-		for (ClassId cls : _skillTrees.keySet())
-		{
-			i = 0;
-			tempMap = _skillTrees.get(cls);
-			array = new int[tempMap.size()];
-			for (int h : tempMap.keySet())
-				array[i++] = h;
-			Arrays.sort(array);
-			result.put(cls.ordinal(), array);
-		}
-		_skillsByClassIdHashCodes = result;
-		
-		// race-specific skills including dwarven (obtained by fishing)
-		FastList<Integer> list = FastList.newInstance();
-		result = new TIntObjectHashMap<int[]>(Race.values().length);
-		for (Race r : Race.values())
-		{
-			for (L2TransformSkillLearn s : _TransformSkillTrees)
-				if (s.getRace() == r.ordinal())
-					list.add(SkillTable.getSkillHashCode(s.getId(), s.getLevel()));
-			
-			if (r == Race.Dwarf)
-			{
-				for (L2SkillLearn s : _expandDwarfCraftSkillTrees)
-					list.add(SkillTable.getSkillHashCode(s.getId(), s.getLevel()));
-			}
-			
-			i = 0;
-			array = new int[list.size()];
-			for (int s : list)
-				array[i++] = s;
-			Arrays.sort(array);
-			result.put(r.ordinal(), array);
-			list.clear();
-		}
-		_skillsByRaceHashCodes = result;
-		
-		// skills available for all classes and races
-		for (L2SkillLearn s : _fishingSkillTrees)
-			list.add(SkillTable.getSkillHashCode(s.getId(), s.getLevel()));
-		
-		for (L2TransformSkillLearn s : _TransformSkillTrees)
-			if (s.getRace() == -1)
-				list.add(SkillTable.getSkillHashCode(s.getId(), s.getLevel()));
-		
-		for (L2SkillLearn s : _specialSkillTrees)
-			list.add(SkillTable.getSkillHashCode(s.getId(), s.getLevel()));
-		
-		i = 0;
-		array = new int[list.size()];
-		for (int s : list)
-			array[i++] = s;
-		Arrays.sort(array);
-		_allSkillsHashCodes = array;
-		
-		FastList.recycle(list);
-	}
-	
-	private Map<ClassId, Map<Integer, L2SkillLearn>> getSkillTrees()
-	{
-		if (_skillTrees == null)
-			_skillTrees = new FastMap<ClassId, Map<Integer, L2SkillLearn>>();
-		
-		return _skillTrees;
-	}
-	
-	public L2SkillLearn[] getAvailableSkills(L2PcInstance cha, ClassId classId)
-	{
-		List<L2SkillLearn> result = new FastList<L2SkillLearn>();
-		Collection<L2SkillLearn> skills = getSkillTrees().get(classId).values();
-		
-		if (skills == null)
-		{
-			// the skilltree for this class is undefined, so we give an empty list
-			_log.warning("Skilltree for class " + classId + " is not defined !");
-			return new L2SkillLearn[0];
-		}
-		
-		L2Skill[] oldSkills = cha.getAllSkills();
-		
-		for (L2SkillLearn temp : skills)
-		{
-			//Let's get all auto-get skills and all skill learn from npc, but transfer skills.
-			if ((temp.isAutoGetSkill() || (temp.isLearnedByNPC() && !temp.isTransferSkill())) && (temp.getMinLevel() <= cha.getLevel()))
-			{
-				boolean knownSkill = false;
-				
-				for (int j = 0; j < oldSkills.length && !knownSkill; j++)
-				{
-					if (oldSkills[j].getId() == temp.getId())
-					{
-						knownSkill = true;
-						
-						if (oldSkills[j].getLevel() == temp.getLevel() - 1)
-						{
-							// this is the next level of a skill that we know
-							result.add(temp);
-						}
-					}
-				}
-				
-				if (!knownSkill && temp.getLevel() == 1)
-				{
-					// this is a new skill
-					result.add(temp);
-				}
-			}
-		}
-		
-		return result.toArray(new L2SkillLearn[result.size()]);
-	}
-	
-	public L2SkillLearn[] getAvailableSkills(L2PcInstance cha)
-	{
-		List<L2SkillLearn> result = new FastList<L2SkillLearn>();
-		List<L2SkillLearn> skills = new FastList<L2SkillLearn>();
-		
-		skills.addAll(_fishingSkillTrees);
-		
-		if (skills.size() < 1)
-		{
-			// the skilltree for this class is undefined, so we give an empty list
-			_log.warning("Skilltree for fishing is not defined !");
-			return new L2SkillLearn[0];
-		}
-		
-		if (cha.hasDwarvenCraft() && _expandDwarfCraftSkillTrees != null)
-		{
-			skills.addAll(_expandDwarfCraftSkillTrees);
-		}
-		
-		L2Skill[] oldSkills = cha.getAllSkills();
-		
-		for (L2SkillLearn temp : skills)
-		{
-			if (temp.isLearnedByNPC() && temp.getMinLevel() <= cha.getLevel())
-			{
-				boolean knownSkill = false;
-				
-				for (int j = 0; j < oldSkills.length && !knownSkill; j++)
-				{
-					if (oldSkills[j].getId() == temp.getId())
-					{
-						knownSkill = true;
-						
-						if (oldSkills[j].getLevel() == temp.getLevel() - 1)
-						{
-							// this is the next level of a skill that we know
-							result.add(temp);
-						}
-					}
-				}
-				
-				if (!knownSkill && temp.getLevel() == 1)
-				{
-					// this is a new skill
-					result.add(temp);
-				}
-			}
-		}
-		
-		return result.toArray(new L2SkillLearn[result.size()]);
-	}
-	
-	public L2SkillLearn[] getAvailableSpecialSkills(L2PcInstance cha)
-	{
-		List<L2SkillLearn> result = new FastList<L2SkillLearn>();
-		List<L2SkillLearn> skills = new FastList<L2SkillLearn>();
-		
-		skills.addAll(_specialSkillTrees);
-		
-		if (skills.size() < 1)
-		{
-			// the skilltree for this class is undefined, so we give an empty list
-			_log.warning("Skilltree for special is not defined !");
-			return new L2SkillLearn[0];
-		}
-		
-		L2Skill[] oldSkills = cha.getAllSkills();
-		
-		for (L2SkillLearn temp : skills)
-		{
-			boolean knownSkill = false;
-			
-			for (int j = 0; j < oldSkills.length && !knownSkill; j++)
-			{
-				if (oldSkills[j].getId() == temp.getId())
-				{
-					knownSkill = true;
-					
-					if (oldSkills[j].getLevel() == temp.getLevel() - 1)
-					{
-						// this is the next level of a skill that we know
-						result.add(temp);
-					}
-				}
-			}
-			
-			if (!knownSkill && temp.getLevel() == 1)
-			{
-				// this is a new skill
-				result.add(temp);
-			}
-		}
-		
-		return result.toArray(new L2SkillLearn[result.size()]);
-	}
-	
-	public L2TransformSkillLearn[] getAvailableTransformSkills(L2PcInstance cha)
-	{
-		List<L2TransformSkillLearn> result = new FastList<L2TransformSkillLearn>();
-		List<L2TransformSkillLearn> skills = _TransformSkillTrees;
-		
-		if (skills == null)
-		{
-			// the skilltree for this class is undefined, so we give an empty list
-			
-			_log.warning("No Transform skills defined!");
-			return new L2TransformSkillLearn[0];
-		}
-		
-		L2Skill[] oldSkills = cha.getAllSkills();
-		
-		for (L2TransformSkillLearn temp : skills)
-		{
-			if (temp.getMinLevel() <= cha.getLevel() && (temp.getRace() == cha.getRace().ordinal() || temp.getRace() == -1))
-			{
-				boolean knownSkill = false;
-				
-				for (int j = 0; j < oldSkills.length && !knownSkill; j++)
-				{
-					if (oldSkills[j].getId() == temp.getId())
-					{
-						knownSkill = true;
-						
-						if (oldSkills[j].getLevel() == temp.getLevel() - 1)
-						{
-							// this is the next level of a skill that we know
-							result.add(temp);
-						}
-					}
-				}
-				
-				if (!knownSkill && temp.getLevel() == 1)
-				{
-					// this is a new skill
-					result.add(temp);
-				}
-			}
-		}
-		
-		return result.toArray(new L2TransformSkillLearn[result.size()]);
-	}
-	
-	public L2PledgeSkillLearn[] getAvailablePledgeSkills(L2PcInstance cha)
-	{
-		List<L2PledgeSkillLearn> result = new FastList<L2PledgeSkillLearn>();
-		List<L2PledgeSkillLearn> skills = _pledgeSkillTrees;
-		
-		if (skills == null)
-		{
-			// the skilltree for this class is undefined, so we give an empty list
-			
-			_log.warning("No clan skills defined!");
-			return new L2PledgeSkillLearn[0];
-		}
-		
-		L2Skill[] oldSkills = cha.getClan().getAllSkills();
-		
-		for (L2PledgeSkillLearn temp : skills)
-		{
-			if (temp.getBaseLevel() <= cha.getClan().getLevel())
-			{
-				boolean knownSkill = false;
-				
-				for (int j = 0; j < oldSkills.length && !knownSkill; j++)
-				{
-					if (oldSkills[j].getId() == temp.getId())
-					{
-						knownSkill = true;
-						
-						if (oldSkills[j].getLevel() == temp.getLevel() - 1)
-						{
-							// this is the next level of a skill that we know
-							result.add(temp);
-						}
-					}
-				}
-				
-				if (!knownSkill && temp.getLevel() == 1)
-				{
-					// this is a new skill
-					result.add(temp);
-				}
-			}
-		}
-		
-		return result.toArray(new L2PledgeSkillLearn[result.size()]);
-	}
-	
-	/**
-	 * Returns all allowed skills for a given class.
-	 * @param classId
-	 * @return all allowed skills for a given class.
-	 */
-	public Collection<L2SkillLearn> getAllowedSkills(ClassId classId)
-	{
-		return getSkillTrees().get(classId).values();
-	}
-	
-	public int getMinLevelForNewSkill(L2PcInstance cha, ClassId classId)
-	{
-		int minLevel = 0;
-		Collection<L2SkillLearn> skills = getSkillTrees().get(classId).values();
-		
-		if (skills == null)
-		{
-			// the skilltree for this class is undefined, so we give an empty list
-			_log.warning("Skilltree for class " + classId + " is not defined !");
-			return minLevel;
-		}
-		
-		for (L2SkillLearn temp : skills)
-		{
-			if (temp.getMinLevel() > cha.getLevel() && temp.getSpCost() != 0)
-				if (minLevel == 0 || temp.getMinLevel() < minLevel)
-					minLevel = temp.getMinLevel();
-		}
-		
-		return minLevel;
-	}
-	
-	public int getMinLevelForNewSkill(L2PcInstance cha)
-	{
-		int minLevel = 0;
-		List<L2SkillLearn> skills = new FastList<L2SkillLearn>();
-		
-		skills.addAll(_fishingSkillTrees);
-		
-		if (skills.size() < 1)
-		{
-			// the skilltree for this class is undefined, so we give an empty list
-			_log.warning("SkillTree for fishing is not defined !");
-			return minLevel;
-		}
-		
-		if (cha.hasDwarvenCraft() && _expandDwarfCraftSkillTrees != null)
-		{
-			skills.addAll(_expandDwarfCraftSkillTrees);
-		}
-		
-		for (L2SkillLearn s : skills)
-		{
-			if (s.getMinLevel() > cha.getLevel())
-				if (minLevel == 0 || s.getMinLevel() < minLevel)
-					minLevel = s.getMinLevel();
-		}
-		
-		return minLevel;
-	}
-	
-	public int getMinLevelForNewTransformSkill(L2PcInstance cha)
-	{
-		int minLevel = 0;
-		List<L2TransformSkillLearn> skills = new FastList<L2TransformSkillLearn>();
-		
-		skills.addAll(_TransformSkillTrees);
-		
-		if (skills.size() < 1)
-		{
-			// the skilltree for this class is undefined, so we give an empty list
-			_log.warning("SkillTree for fishing is not defined !");
-			return minLevel;
-		}
-		
-		for (L2TransformSkillLearn s : skills)
-		{
-			if ((s.getMinLevel() > cha.getLevel()) && (s.getRace() == cha.getRace().ordinal()))
-				if (minLevel == 0 || s.getMinLevel() < minLevel)
-					minLevel = s.getMinLevel();
-		}
-		
-		return minLevel;
-	}
-	
-	public int getSkillCost(L2PcInstance player, L2Skill skill)
-	{
-		int skillCost = 100000000;
-		ClassId classId = player.getSkillLearningClassId();
-		int skillHashCode = SkillTable.getSkillHashCode(skill);
-		
-		if (getSkillTrees().get(classId).containsKey(skillHashCode))
-		{
-			L2SkillLearn skillLearn = getSkillTrees().get(classId).get(skillHashCode);
-			if (skillLearn.getMinLevel() <= player.getLevel())
-			{
-				skillCost = skillLearn.getSpCost();
-				if (!player.getClassId().equalsOrChildOf(classId))
-					return skillCost;
-			}
-		}
-		
-		return skillCost;
-	}
-	
-	public L2SkillLearn getSkillLearnBySkillIdLevel(ClassId classId, int skillId, int skillLvl)
-	{
-		for (L2SkillLearn sl : getAllowedSkills(classId))
-		{
-			if (sl.getId() == skillId && sl.getLevel() == skillLvl)
-			{
-				return sl; // found skill learn
-			}
-		}
-		return null;
-	}
-	
-	public List<Integer> getAllAllowedSkillId(L2PcInstance player)
-	{
-		FastList<Integer> skills = new FastList<Integer>();
-		
-		for (L2SkillLearn tmp : getAllowedSkills(player.getClassId()))
-		{
-			if (skills.contains(tmp.getId()))
-				skills.add(tmp.getId());
-		}
-		
-		return skills;
-	}
-	
-	public boolean isSkillAllowed(L2PcInstance player, L2Skill skill)
-	{
-		if (skill.isExcludedFromCheck())
-			return true;
-		
-		if (player.isGM() && skill.isGMSkill())
-			return true;
-		
-		if (_loading) // prevent accidental skill remove during reload
-			return true;
-		
-		final int maxLvl = SkillTable.getInstance().getMaxLevel(skill.getId());
-		final int hashCode = SkillTable.getSkillHashCode(skill.getId(), Math.min(skill.getLevel(), maxLvl));
-		
-		if (Arrays.binarySearch(_skillsByClassIdHashCodes.get(player.getClassId().ordinal()), hashCode) >= 0)
-			return true;
-		
-		if (Arrays.binarySearch(_skillsByRaceHashCodes.get(player.getRace().ordinal()), hashCode) >= 0)
-			return true;
-		
-		if (Arrays.binarySearch(_allSkillsHashCodes, hashCode) >= 0)
-			return true;
-		
-		return false;
-	}
-	
-	@SuppressWarnings("synthetic-access")
-	private static class SingletonHolder
-	{
-		protected static final SkillTreeTable _instance = new SkillTreeTable();
-	}
-	
-	public void reload()
-	{
-		load();
-	}
-}

+ 1196 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillTreesData.java

@@ -0,0 +1,1196 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.datatables;
+
+import gnu.trove.TIntObjectHashMap;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import javolution.util.FastList;
+import javolution.util.FastMap;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.model.L2Clan;
+import com.l2jserver.gameserver.model.L2Skill;
+import com.l2jserver.gameserver.model.L2SkillLearn;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.base.ClassId;
+import com.l2jserver.gameserver.model.base.Race;
+import com.l2jserver.gameserver.model.base.SubClass;
+import com.l2jserver.gameserver.templates.StatsSet;
+import com.l2jserver.gameserver.util.Util;
+
+/**
+ * @author Zoey76
+ */
+public final class SkillTreesData
+{
+	private static Logger _log = Logger.getLogger(SkillTreesData.class.getSimpleName());
+	
+	//ClassId, FastMap of Skill Hash Code, L2LearkSkill
+	private FastMap<ClassId, FastMap<Integer, L2SkillLearn>> _classSkillTrees;
+	private FastMap<ClassId, FastMap<Integer, L2SkillLearn>> _transferSkillTrees;
+	//Skill Hash Code, L2LearkSkill
+	private FastMap<Integer, L2SkillLearn> _collectSkillTree;
+	private FastMap<Integer, L2SkillLearn> _fishingSkillTree;
+	private FastMap<Integer, L2SkillLearn> _pledgeSkillTree;
+	private FastMap<Integer, L2SkillLearn> _subClassSkillTree;
+	private FastMap<Integer, L2SkillLearn> _subPledgeSkillTree;
+	private FastMap<Integer, L2SkillLearn> _transformSkillTree;
+	
+	//TODO: Unhardcode?
+	//Checker, sorted arrays of hash codes
+	private TIntObjectHashMap<int[]> _skillsByClassIdHashCodes; //Occupation skills
+	private TIntObjectHashMap<int[]> _skillsByRaceHashCodes; // race-specific transformations
+	private int[] _allSkillsHashCodes; // fishing, collection and all races transformations
+	
+	private boolean _loading = true;
+	
+	/**
+	 * Parent class IDs are read from XML and stored in this map, to allow easy customization.
+	 */
+	private FastMap<ClassId, ClassId> _parentClassMap = new FastMap<ClassId, ClassId>();
+	
+	private SkillTreesData()
+	{
+		load();
+	}
+	
+	public void load()
+	{
+		_loading = true;
+		_classSkillTrees = new FastMap<ClassId, FastMap<Integer, L2SkillLearn>>();
+		_collectSkillTree = new FastMap<Integer, L2SkillLearn>();
+		_fishingSkillTree = new FastMap<Integer, L2SkillLearn>();
+		_pledgeSkillTree = new FastMap<Integer, L2SkillLearn>();
+		_subClassSkillTree = new FastMap<Integer, L2SkillLearn>();
+		_subPledgeSkillTree = new FastMap<Integer, L2SkillLearn>();
+		_transferSkillTrees = new FastMap<ClassId, FastMap<Integer, L2SkillLearn>>();
+		_transformSkillTree = new FastMap<Integer, L2SkillLearn>();
+		
+		//Load files.
+		_loading = loadFiles();
+		
+		int classSkillTreeCount = 0;
+		for (ClassId classId : _classSkillTrees.keySet())
+		{
+			classSkillTreeCount += _classSkillTrees.get(classId).size();
+		}
+		
+		int trasferSkillTreeCount = 0;
+		for (ClassId classId : _transferSkillTrees.keySet())
+		{
+			trasferSkillTreeCount += _transferSkillTrees.get(classId).size();
+		}
+		
+		int fishingDwarvenSkillCount = 0;
+		for (L2SkillLearn fishSkill : _fishingSkillTree.values())
+		{
+			if ((fishSkill.getRaces() != null) && Util.contains(fishSkill.getRaces(), 4))
+			{
+				fishingDwarvenSkillCount++;
+			}
+		}
+		
+		int residentialSkillCount = 0;
+		for (L2SkillLearn pledgeSkill : _pledgeSkillTree.values())
+		{
+			if (pledgeSkill.isResidencialSkill())
+			{
+				residentialSkillCount++;
+			}
+		}
+		
+		_log.info(getClass().getSimpleName() + ": Loaded " + classSkillTreeCount + "  Class Skills for " + _classSkillTrees.size() + " Class Skill Trees.");
+		_log.info(getClass().getSimpleName() + ": Loaded " + _subClassSkillTree.size() + " Sub-Class Skills.");
+		_log.info(getClass().getSimpleName() + ": Loaded " + trasferSkillTreeCount + " Transfer Skills for " + _transferSkillTrees.size() + " Transfer Skill Trees.");
+		_log.info(getClass().getSimpleName() + ": Loaded " + _fishingSkillTree.size() + " Fishing Skills, " + fishingDwarvenSkillCount + " Dwarven only Fishing Skills.");
+		_log.info(getClass().getSimpleName() + ": Loaded " + _collectSkillTree.size() + " Collect Skills.");
+		_log.info(getClass().getSimpleName() + ": Loaded " + _pledgeSkillTree.size() + " Pledge Skills, " + (_pledgeSkillTree.size() - residentialSkillCount) + " for Pledge and " + residentialSkillCount + " Residential.");
+		_log.info(getClass().getSimpleName() + ": Loaded " + _subPledgeSkillTree.size() + " Sub-Pledge Skills.");
+		_log.info(getClass().getSimpleName() + ": Loaded " + _transformSkillTree.size() + " Transform Skills.");
+	}
+	
+	/**
+	 * Loads all files type xml from data/skillTrees/ and call the parser for each one of them.
+	 * @return {@code false} when the files are loaded.
+	 */
+	private boolean loadFiles()
+	{
+		File folder = new File(Config.DATAPACK_ROOT, "data/skillTrees/");
+		File[] listOfFiles = folder.listFiles();
+		for (File f : listOfFiles)
+		{
+			if (f.getName().endsWith(".xml"))
+			{
+				loadSkillTree(f);
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * Parse a skill tree file and store it into the correct skill tree.
+	 * @param file the xml file to be parsed.
+	 */
+	private void loadSkillTree(File file)
+	{
+		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+		dbf.setValidating(false);
+		dbf.setIgnoringComments(true);
+		Document doc = null;
+		
+		if (file.exists())
+		{
+			try
+			{
+				doc = dbf.newDocumentBuilder().parse(file);
+			}
+			catch (Exception e)
+			{
+				_log.warning(getClass().getSimpleName() + ": Could not parse " + file.getName() + " file: " + e.getMessage());
+			}
+			
+			NamedNodeMap attributes;
+			Node attribute;
+			String type = null;
+			int cId = -1;
+			int parentClassId = -1;
+			ClassId classId = null;
+			
+			for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
+			{
+				if ("list".equalsIgnoreCase(n.getNodeName()))
+				{
+					for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
+					{
+						FastMap<Integer, L2SkillLearn> classSkillTree = new FastMap<Integer, L2SkillLearn>();
+						FastMap<Integer, L2SkillLearn> trasferSkillTree = new FastMap<Integer, L2SkillLearn>();
+						if ("skillTree".equalsIgnoreCase(d.getNodeName()))
+						{
+							attribute = d.getAttributes().getNamedItem("type");
+							if (attribute == null)
+							{
+								_log.warning(getClass().getSimpleName() + ": Skill Tree without type!");
+								continue;
+							}
+							type = attribute.getNodeValue();
+							
+							attribute = d.getAttributes().getNamedItem("classId");
+							if (attribute != null)
+							{
+								cId = Integer.parseInt(attribute.getNodeValue());
+								classId = ClassId.values()[cId];
+							}
+							
+							attribute = d.getAttributes().getNamedItem("parentClassId");
+							if (attribute != null)
+							{
+								parentClassId = Integer.parseInt(attribute.getNodeValue());
+								
+								if ((cId != parentClassId) && (parentClassId > -1))
+								{
+									_parentClassMap.putIfAbsent(classId, ClassId.values()[parentClassId]);
+								}
+							}
+							
+							for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
+							{
+								if ("skill".equalsIgnoreCase(c.getNodeName()))
+								{
+									final StatsSet learnSkillSet = new StatsSet();
+									
+									int skillId;
+									int skillLvl;
+									
+									attributes = c.getAttributes();
+									
+									attribute = attributes.getNamedItem("skillName");
+									if (attribute == null)
+									{
+										_log.severe(getClass().getSimpleName() + ": Missing skillName, skipping!");
+										continue;
+									}
+									learnSkillSet.set("skillName", attribute.getNodeValue());
+									
+									attribute = attributes.getNamedItem("skillIdLvl");
+									if (attribute == null)
+									{
+										_log.severe(getClass().getSimpleName() + ": Missing skillIdLvl, skipping!");
+										continue;
+									}
+									
+									try
+									{
+										skillId = Integer.parseInt(attribute.getNodeValue().split(",")[0]);
+										skillLvl = Integer.parseInt(attribute.getNodeValue().split(",")[1]);
+										learnSkillSet.set("skillId", skillId);
+										learnSkillSet.set("skillLvl", skillLvl);
+									}
+									catch (Exception e)
+									{
+										_log.severe(getClass().getSimpleName() + ": Malformed skillIdLvl, skipping!");
+										continue;
+									}
+									
+									attribute = attributes.getNamedItem("getLevel");
+									if (attribute != null)
+									{
+										learnSkillSet.set("getLevel", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("autoGet");
+									if (attribute != null)
+									{
+										learnSkillSet.set("autoGet", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("levelUpSp");
+									if (attribute != null)
+									{
+										learnSkillSet.set("levelUpSp", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("itemsIdCount");
+									if (attribute != null)
+									{
+										learnSkillSet.set("itemsIdCount", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("race");
+									if (attribute != null)
+									{
+										learnSkillSet.set("race", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("preReqSkillIdLvl");
+									if (attribute != null)
+									{
+										learnSkillSet.set("preReqSkillIdLvl", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("socialClass");
+									if (attribute != null)
+									{
+										learnSkillSet.set("socialClass", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("subClassLvlNumber");
+									if (attribute != null)
+									{
+										learnSkillSet.set("subClassLvlNumber", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("residenceSkill");
+									if (attribute != null)
+									{
+										learnSkillSet.set("residenceSkill", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("residenceIds");
+									if (attribute != null)
+									{
+										learnSkillSet.set("residenceIds", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("learnedByNpc");
+									if (attribute != null)
+									{
+										learnSkillSet.set("learnedByNpc", attribute.getNodeValue());
+									}
+									
+									attribute = attributes.getNamedItem("learnedByFS");
+									if (attribute != null)
+									{
+										learnSkillSet.set("learnedByFS", attribute.getNodeValue());
+									}
+									
+									final L2SkillLearn skillLearn = new L2SkillLearn(learnSkillSet);
+									if (type.equals("classSkillTree"))
+									{
+										classSkillTree.put(SkillTable.getSkillHashCode(skillId, skillLvl), skillLearn);
+									}
+									else if (type.equals("transferSkillTree"))
+									{
+										trasferSkillTree.put(SkillTable.getSkillHashCode(skillId, skillLvl), skillLearn);
+									}
+									else
+									{
+										if (type.equals("collectSkillTree"))
+										{
+											_collectSkillTree.put(SkillTable.getSkillHashCode(skillId, skillLvl), skillLearn);
+										}
+										else if (type.equals("fishingSkillTree"))
+										{
+											_fishingSkillTree.put(SkillTable.getSkillHashCode(skillId, skillLvl), skillLearn);
+										}
+										else if (type.equals("pledgeSkillTree"))
+										{
+											_pledgeSkillTree.put(SkillTable.getSkillHashCode(skillId, skillLvl), skillLearn);
+										}
+										else if (type.equals("subClassSkillTree"))
+										{
+											_subClassSkillTree.put(SkillTable.getSkillHashCode(skillId, skillLvl), skillLearn);
+										}
+										else if (type.equals("subPledgeSkillTree"))
+										{
+											_subPledgeSkillTree.put(SkillTable.getSkillHashCode(skillId, skillLvl), skillLearn);
+										}
+										else if (type.equals("transformSkillTree"))
+										{
+											_transformSkillTree.put(SkillTable.getSkillHashCode(skillId, skillLvl), skillLearn);
+										}
+									}
+								}
+							}
+							
+							if (type.equals("classSkillTree"))
+							{
+								if (_classSkillTrees.get(classId) == null)
+								{
+									_classSkillTrees.put(classId, classSkillTree);
+								}
+								else
+								{
+									_classSkillTrees.get(classId).putAll(classSkillTree);
+								}
+							}
+							else if (type.equals("transferSkillTree"))
+							{
+								_transferSkillTrees.put(classId, trasferSkillTree);
+							}
+						}
+					}
+				}
+			}
+		}
+		generateCheckArrays();
+	}
+	
+	/**
+	 * Wrapper for class skill trees.
+	 * @return the {@code _classSkillTrees}, if it's null allocate a new map and returns it.
+	 */
+	private FastMap<ClassId, FastMap<Integer, L2SkillLearn>> getClassSkillTrees()
+	{
+		if (_classSkillTrees == null)
+		{
+			_classSkillTrees = new FastMap<ClassId, FastMap<Integer, L2SkillLearn>>();
+		}
+		return _classSkillTrees;
+	}
+	
+	/**
+	 * Method to get the complete skill tree for a given class id.<br>
+	 * Includes all parent skill trees.
+	 * @param classId the class skill tree ID.
+	 * @return the complete Class Skill Tree including skill trees from parent class for a given {@code classId}.
+	 */
+	public FastMap<Integer, L2SkillLearn> getCompleteClassSkillTree(ClassId classId)
+	{
+		final FastMap<Integer, L2SkillLearn> skillTree = new FastMap<Integer, L2SkillLearn>();
+		
+		while ((classId != null) && (getClassSkillTrees().get(classId) != null))
+		{
+			skillTree.putAll(getClassSkillTrees().get(classId));
+			classId = _parentClassMap.get(classId);
+		}
+		return skillTree;
+	}
+	
+	/**
+	 * @param classId the transfer skill tree ID.
+	 * @return the complete Transfer Skill Tree for a given {@code classId}.
+	 */
+	public FastMap<Integer, L2SkillLearn> getTransferSkillTree(ClassId classId)
+	{
+		//If new classes are implemented over 3rd class, we use a recursive call.
+		if (classId.level() >= 3)
+		{
+			classId = classId.getParent();
+			return getTransferSkillTree(classId);
+		}
+		return _transferSkillTrees.get(classId);
+	}
+	
+	/**
+	 * @return the complete Collect Skill Tree. 
+	 */
+	public FastMap<Integer, L2SkillLearn> getCollectSkillTree()
+	{
+		return _collectSkillTree;
+	}
+	
+	/**
+	 * @return the complete Fishing Skill Tree. 
+	 */
+	public FastMap<Integer, L2SkillLearn> getFishingSkillTree()
+	{
+		return _fishingSkillTree;
+	}
+	
+	/**
+	 * @return the complete Pledge Skill Tree. 
+	 */
+	public FastMap<Integer, L2SkillLearn> getPledgeSkillTree()
+	{
+		return _pledgeSkillTree;
+	}
+	
+	/**
+	 * @return the complete Sub-Class Skill Tree. 
+	 */
+	public FastMap<Integer, L2SkillLearn> getSubClassSkillTree()
+	{
+		return _subClassSkillTree;
+	}
+	
+	/**
+	 * @return the complete Sub-Pledge Skill Tree. 
+	 */
+	public FastMap<Integer, L2SkillLearn> getSubPledgeSkillTree()
+	{
+		return _subPledgeSkillTree;
+	}
+	
+	/**
+	 * @return the complete Transform Skill Tree. 
+	 */
+	public FastMap<Integer, L2SkillLearn> getTransformSkillTree()
+	{
+		return _transformSkillTree;
+	}
+	
+	/**
+	 * @param player the learning skill player.
+	 * @param classId the learning skill class ID.
+	 * @param includeByFs if {@code true} skills from Forgotten Scroll will be included.
+	 * @param includeAutoGet if {@code true} Auto-Get skills will be included.
+	 * @return all available skills for a given {@code player}, {@code classId}, {@code includeByFs} and {@code includeAutoGet}.
+	 */
+	public FastList<L2SkillLearn> getAvailableSkills(L2PcInstance player, ClassId classId, boolean includeByFs, boolean includeAutoGet)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = getCompleteClassSkillTree(classId);
+		
+		if (skills.isEmpty())
+		{
+			//The Skill Tree for this class is undefined.
+			_log.warning(getClass().getSimpleName() + ": Skilltree for class " + classId + " is not defined!");
+			return result;
+		}
+		
+		final L2Skill[] oldSkills = player.getAllSkills();
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			if (((includeAutoGet && temp.isAutoGet()) || temp.isLearnedByNpc() || (includeByFs && temp.isLearnedByFS())) && (player.getLevel() >= temp.getGetLevel()))
+			{
+				boolean knownSkill = false;
+				
+				for (int j = 0; (j < oldSkills.length) && !knownSkill; j++)
+				{
+					if (oldSkills[j].getId() == temp.getSkillId())
+					{
+						if (oldSkills[j].getLevel() == (temp.getSkillLevel() - 1))
+						{
+							//This is the next level of a known skill:
+							result.add(temp);
+						}
+						knownSkill = true;
+					}
+				}
+				
+				if (!knownSkill && (temp.getSkillLevel() == 1))
+				{
+					//This is a new skill:
+					result.add(temp);
+				}
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * @param player the player requesting the Auto-Get skills.
+	 * @return all the available Auto-Get skills for a given {@code player}.
+	 */
+	public FastList<L2SkillLearn> getAvailableAutoGetSkills(L2PcInstance player)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = getCompleteClassSkillTree(player.getClassId());
+		
+		if (skills.size() < 1)
+		{
+			//The Skill Tree for this class is undefined, so we return an empty list.
+			_log.warning(getClass().getSimpleName() + ": Skill Tree for this classId(" + player.getClassId() + ") is not defined!");
+			return new FastList<L2SkillLearn>();
+		}
+		
+		final L2Skill[] oldSkills = player.getAllSkills();
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			if ((temp.getRaces() != null) && Util.contains(temp.getRaces(), 4) && !player.hasDwarvenCraft())
+			{
+				continue;
+			}
+			
+			if (temp.isAutoGet() && (player.getLevel() >= temp.getGetLevel()))
+			{
+				boolean knownSkill = false;
+				
+				for (int j = 0; (j < oldSkills.length) && !knownSkill; j++)
+				{
+					if (oldSkills[j].getId() == temp.getSkillId())
+					{
+						if (oldSkills[j].getLevel() < temp.getSkillLevel())
+						{
+							result.add(temp);
+						}
+						knownSkill = true;
+					}
+				}
+				
+				if (!knownSkill)
+				{
+					result.add(temp);
+				}
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * Dwarvens will get additional dwarven only fishing skills.
+	 * @param player
+	 * @return all the available Fishing skills for a given {@code player}.
+	 */
+	public FastList<L2SkillLearn> getAvailableFishingSkills(L2PcInstance player)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = new FastMap<Integer, L2SkillLearn>();
+		
+		skills.putAll(_fishingSkillTree);
+		
+		if (skills.size() < 1)
+		{
+			//The Skill Tree for fishing skills is undefined.
+			_log.warning(getClass().getSimpleName() + ": Skilltree for fishing is not defined !");
+			return new FastList<L2SkillLearn>();
+		}
+		
+		final L2Skill[] oldSkills = player.getAllSkills();
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			//If skill is Dwarven only and player is not Dwarven.
+			if ((temp.getRaces() != null) && Util.contains(temp.getRaces(), 4) && !player.hasDwarvenCraft())
+			{
+				continue;
+			}
+			
+			if (temp.isLearnedByNpc() && (player.getLevel() >= temp.getGetLevel()))
+			{
+				boolean knownSkill = false;
+				
+				for (int j = 0; (j < oldSkills.length) && !knownSkill; j++)
+				{
+					if (oldSkills[j].getId() == temp.getSkillId())
+					{
+						if (oldSkills[j].getLevel() == (temp.getSkillLevel() - 1))
+						{
+							//This is the next level of a known skill:
+							result.add(temp);
+						}
+						knownSkill = true;
+					}
+				}
+				
+				if (!knownSkill && (temp.getSkillLevel() == 1))
+				{
+					//This is a new skill:
+					result.add(temp);
+				}
+			}
+		}
+		
+		return result;
+	}
+	
+	/**
+	 * Used in Gracia continent.
+	 * @param player the collecting skill learning player.
+	 * @return all the available Collecting skills for a given {@code player}.
+	 */
+	public FastList<L2SkillLearn> getAvailableCollectSkills(L2PcInstance player)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = new FastMap<Integer, L2SkillLearn>();
+		
+		skills.putAll(_collectSkillTree);
+		
+		if (skills.size() < 1)
+		{
+			//The Skill Tree for Collecting skills is undefined.
+			_log.warning(getClass().getSimpleName() + ": Skilltree for collecting skills is not defined !");
+			return new FastList<L2SkillLearn>();
+		}
+		
+		final L2Skill[] oldSkills = player.getAllSkills();
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			boolean knownSkill = false;
+			
+			for (int j = 0; (j < oldSkills.length) && !knownSkill; j++)
+			{
+				if (oldSkills[j].getId() == temp.getSkillId())
+				{
+					if (oldSkills[j].getLevel() == (temp.getSkillLevel() - 1))
+					{
+						//This is the next level of a known skill:
+						result.add(temp);
+					}
+					knownSkill = true;
+				}
+			}
+			
+			if (!knownSkill && (temp.getSkillLevel() == 1))
+			{
+				//This is a new skill:
+				result.add(temp);
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * @param player the transfer skill learning player.
+	 * @return all the available Transfer skills for a given {@code player}.
+	 */
+	public FastList<L2SkillLearn> getAvailableTransferSkills(L2PcInstance player)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		
+		ClassId classId = player.getClassId();
+		
+		//If new classes are implemented over 3rd class, a different way should be implemented.
+		if (classId.level() == 3)
+		{
+			classId = classId.getParent();
+		}
+		
+		if (_transferSkillTrees.get(classId) == null)
+		{
+			return result;
+		}
+		
+		for (L2SkillLearn temp : _transferSkillTrees.get(classId).values())
+		{
+			//If player doesn't know this transfer skill:
+			if (player.getKnownSkill(temp.getSkillId()) == null)
+			{
+				result.add(temp);
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * Some transformations are not available for some races.
+	 * @param playerthe transformation skill learning player.
+	 * @return all the available Transformation skills for a given {@code player}.
+	 */
+	public FastList<L2SkillLearn> getAvailableTransformSkills(L2PcInstance player)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = _transformSkillTree;
+		
+		if (skills == null)
+		{
+			//The Skill Tree for Transformation skills is undefined.
+			_log.warning(getClass().getSimpleName() + ": No Transform skills defined!");
+			return new FastList<L2SkillLearn>();
+		}
+		
+		final L2Skill[] oldSkills = player.getAllSkills();
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			if ((player.getLevel() >= temp.getGetLevel()) && ((temp.getRaces() != null) || Util.contains(temp.getRaces(), player.getRace().ordinal())))
+			{
+				boolean knownSkill = false;
+				
+				for (int j = 0; (j < oldSkills.length) && !knownSkill; j++)
+				{
+					if (oldSkills[j].getId() == temp.getSkillId())
+					{
+						if (oldSkills[j].getLevel() == (temp.getSkillLevel() - 1))
+						{
+							//This is the next level of a known skill:
+							result.add(temp);
+						}
+						knownSkill = true;
+					}
+				}
+				
+				if (!knownSkill && (temp.getSkillLevel() == 1))
+				{
+					//This is a new skill:
+					result.add(temp);
+				}
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * @param clan the pledge skill learning clan.
+	 * @return all the available Pledge skills for a given {@code clan}.
+	 */
+	public FastList<L2SkillLearn> getAvailablePledgeSkills(L2Clan clan)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = _pledgeSkillTree;
+		
+		if (skills == null)
+		{
+			//The Skill Tree for Pledge skills is undefined.
+			_log.warning(getClass().getSimpleName() + ": No clan skills defined!");
+			return new FastList<L2SkillLearn>();
+		}
+		
+		L2Skill[] oldSkills = clan.getAllSkills();
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			if (!temp.isResidencialSkill() && (clan.getLevel() >= temp.getGetLevel()))
+			{
+				boolean knownSkill = false;
+				
+				for (int j = 0; (j < oldSkills.length) && !knownSkill; j++)
+				{
+					if (oldSkills[j].getId() == temp.getSkillId())
+					{
+						if (oldSkills[j].getLevel() == (temp.getSkillLevel() - 1))
+						{
+							//This is the next level of a known skill:
+							result.add(temp);
+						}
+						knownSkill = true;
+					}
+				}
+				
+				if (!knownSkill && (temp.getSkillLevel() == 1))
+				{
+					//This is a new skill:
+					result.add(temp);
+				}
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * @param clan the sub-pledge skill learning clan.
+	 * @return all the available Sub-Pledge skills for a given {@code clan}.
+	 */
+	public FastList<L2SkillLearn> getAvailableSubPledgeSkills(L2Clan clan)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = _subPledgeSkillTree;
+		
+		if (skills == null)
+		{
+			//The Skill Tree for Sub-Pledge skills is undefined.
+			_log.warning(getClass().getSimpleName() + ": No sub-clan skills defined!");
+			return new FastList<L2SkillLearn>();
+		}
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			if ((clan.getLevel() >= temp.getGetLevel()) && clan.isLearnableSubSkill(temp.getSkillId(), temp.getSkillLevel()))
+			{
+				result.add(temp);
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * @param player the sub-class skill learning player.
+	 * @return all the available Sub-Class skills for a given {@code player}.
+	 */
+	public FastList<L2SkillLearn> getAvailableSubClassSkills(L2PcInstance player)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = _subClassSkillTree;
+		
+		if (skills == null)
+		{
+			//The Skill Tree for Sub-Class skills is undefined.
+			_log.warning(getClass().getSimpleName() + ": No Sub-Class skills defined!");
+			return new FastList<L2SkillLearn>();
+		}
+		
+		final L2Skill[] oldSkills = player.getAllSkills();
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			if (player.getLevel() >= temp.getGetLevel())
+			{
+				for (SubClass subClass : player.getSubClasses().values())
+				{
+					if ((temp.getSubClassConditions() != null) && (subClass.getClassIndex() == temp.getSubClassConditions()[subClass.getClassIndex() - 1][1]) && (temp.getSubClassConditions()[subClass.getClassIndex() - 1][0] <= subClass.getLevel()))
+					{
+						boolean knownSkill = false;
+						
+						for (int j = 0; (j < oldSkills.length) && !knownSkill; j++)
+						{
+							if (oldSkills[j].getId() == temp.getSkillId())
+							{
+								if (oldSkills[j].getLevel() == (temp.getSkillLevel() - 1))
+								{
+									//This is the next level of a known skill:
+									result.add(temp);
+								}
+								knownSkill = true;
+							}
+						}
+						
+						if (!knownSkill && (temp.getSkillLevel() == 1))
+						{
+							//This is a new skill:
+							result.add(temp);
+						}
+					}
+				}
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * @param residenceId the id of the Castle, Fort, Territory.
+	 * @return all the available Residential skills for a given {@code residenceId}.
+	 */
+	public FastList<L2SkillLearn> getAvailableResidentialSkills(int residenceId)
+	{
+		final FastList<L2SkillLearn> result = new FastList<L2SkillLearn>();
+		final FastMap<Integer, L2SkillLearn> skills = _pledgeSkillTree;
+		
+		if (skills == null)
+		{
+			//The Skill Tree for Residential skills is undefined?
+			_log.warning(getClass().getSimpleName() + ": No residential skills defined!");
+			return new FastList<L2SkillLearn>();
+		}
+		
+		for (L2SkillLearn temp : skills.values())
+		{
+			if (temp.isResidencialSkill() && (temp.getRecidenceIds() != null) && Util.contains(temp.getRecidenceIds(), residenceId))
+			{
+				result.add(temp);
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * @param id the transformation skill ID.
+	 * @param lvl the transformation skill level.
+	 * @return the transform skill from the Transform Skill Tree for a given {@code id} and {@code lvl}.
+	 */
+	public L2SkillLearn getTransformSkill(int id, int lvl)
+	{
+		return _transformSkillTree.get(SkillTable.getSkillHashCode(id, lvl));
+	}
+	
+	/**
+	 * @param id the class skill ID.
+	 * @param lvl the class skill level.
+	 * @param classId the class skill tree ID.
+	 * @return the class skill from the Class Skill Trees for a given {@code classId}, {@code id} and {@code lvl}.
+	 */
+	public L2SkillLearn getClassSkill(int id, int lvl, ClassId classId)
+	{
+		final FastMap<Integer, L2SkillLearn> skills = getCompleteClassSkillTree(classId);
+		
+		return skills.get(SkillTable.getSkillHashCode(id, lvl));
+	}
+	
+	/**
+	 * @param id the fishing skill ID.
+	 * @param lvl the fishing skill level.
+	 * @return Fishing skill from the Fishing Skill Tree for a given {@code id} and {@code lvl}.
+	 */
+	public L2SkillLearn getFishingSkill(int id, int lvl)
+	{
+		return _fishingSkillTree.get(SkillTable.getSkillHashCode(id, lvl));
+	}
+	
+	/**
+	 * @param id the pledge skill ID.
+	 * @param lvl the pledge skill level.
+	 * @return the pledge skill from the Pledge Skill Tree for a given {@code id} and {@code lvl}.
+	 */
+	public L2SkillLearn getPledgeSkill(int id, int lvl)
+	{
+		return _pledgeSkillTree.get(SkillTable.getSkillHashCode(id, lvl));
+	}
+	
+	/**
+	 * @param id the sub-pledge skill ID.
+	 * @param lvl the sub-pledge skill level.
+	 * @return the sub-pledge skill from the Sub-Pledge Skill Tree for a given {@code id} and {@code lvl}.
+	 */
+	public L2SkillLearn getSubPledgeSkill(int id, int lvl)
+	{
+		return _subPledgeSkillTree.get(SkillTable.getSkillHashCode(id, lvl));
+	}
+	
+	/**
+	 * @param id the transfer skill ID.
+	 * @param lvl the transfer skill level.
+	 * @param classId the transfer skill tree ID.
+	 * @return the transfer skill from the Transfer Skill Trees for a given {@code classId}, {@code id} and {@code lvl}.
+	 */
+	public L2SkillLearn getTransferSkill(int id, int lvl, ClassId classId)
+	{
+		if (classId.getParent() != null)
+		{
+			final ClassId parentId = classId.getParent();
+			if (_transferSkillTrees.get(parentId) != null)
+			{
+				return _transferSkillTrees.get(parentId).get(SkillTable.getSkillHashCode(id, lvl));
+			}
+		}
+		return null;
+	}
+	
+	/**
+	 * @param id the sub-class skill ID.
+	 * @param lvl the sub-class skill level.
+	 * @return the sub-class skill from the Sub-Class Skill Tree for a given {@code id} and {@code lvl}.
+	 */
+	public L2SkillLearn getSubClassSkill(int id, int lvl)
+	{
+		return _subClassSkillTree.get(SkillTable.getSkillHashCode(id, lvl));
+	}
+	
+	/**
+	 * @param id the collect skill ID.
+	 * @param lvl the collect skill level.
+	 * @return the collect skill from the Collect Skill Tree for a given {@code id} and {@code lvl}.
+	 */
+	public L2SkillLearn getCollectSkill(int id, int lvl)
+	{
+		return _collectSkillTree.get(SkillTable.getSkillHashCode(id, lvl));
+	}
+	
+	/**
+	 * @param player the player that requires the minimum level.
+	 * @param skillTree the skill tree to search the minimum get level.
+	 * @return the minimum level for a new skill for a given {@code player} and {@code skillTree}.
+	 */
+	public int getMinLevelForNewSkill(L2PcInstance player, FastMap<Integer, L2SkillLearn> skillTree)
+	{
+		int minLevel = 0;
+		if (skillTree.size() < 1)
+		{
+			_log.warning(getClass().getSimpleName() + ": SkillTree is not defined for getMinLevelForNewSkill!");
+			return minLevel;
+		}
+		
+		for (L2SkillLearn s : skillTree.values())
+		{
+			if (player.getLevel() >= s.getGetLevel())
+			{
+				if ((minLevel == 0) || (s.getGetLevel() < minLevel))
+				{
+					minLevel = s.getGetLevel();
+				}
+			}
+		}
+		return minLevel;
+	}
+	
+	/**
+	 * Create and store hash values for skills for easy and fast checks.
+	 */
+	private void generateCheckArrays()
+	{
+		int i;
+		int[] array;
+		
+		//Class specific skills:
+		FastMap<Integer, L2SkillLearn> tempMap;
+		TIntObjectHashMap<int[]> result = new TIntObjectHashMap<int[]>(getClassSkillTrees().keySet().size());
+		for (ClassId cls : getClassSkillTrees().keySet())
+		{
+			i = 0;
+			tempMap = getCompleteClassSkillTree(cls);
+			array = new int[tempMap.size()];
+			for (int h : tempMap.keySet())
+			{
+				array[i++] = h;
+			}
+			Arrays.sort(array);
+			result.put(cls.ordinal(), array);
+		}
+		_skillsByClassIdHashCodes = result;
+		
+		//Race specific skills from Fishing and Transformation skill trees.
+		final FastList<Integer> list = FastList.newInstance();
+		result = new TIntObjectHashMap<int[]>(Race.values().length);
+		for (Race r : Race.values())
+		{
+			for (L2SkillLearn s : _fishingSkillTree.values())
+			{
+				if ((s.getRaces() != null) && Util.contains(s.getRaces(), r.ordinal()))
+				{
+					list.add(SkillTable.getSkillHashCode(s.getSkillId(), s.getSkillLevel()));
+				}
+			}
+			
+			for (L2SkillLearn s : _transformSkillTree.values())
+			{
+				if ((s.getRaces() != null) && Util.contains(s.getRaces(), r.ordinal()))
+				{
+					list.add(SkillTable.getSkillHashCode(s.getSkillId(), s.getSkillLevel()));
+				}
+			}
+			
+			i = 0;
+			array = new int[list.size()];
+			for (int s : list)
+			{
+				array[i++] = s;
+			}
+			Arrays.sort(array);
+			result.put(r.ordinal(), array);
+			list.clear();
+		}
+		_skillsByRaceHashCodes = result;
+		
+		//Skills available for all classes and races
+		for (L2SkillLearn s : _fishingSkillTree.values())
+		{
+			if (s.getRaces() == null)
+			{
+				list.add(SkillTable.getSkillHashCode(s.getSkillId(), s.getSkillLevel()));
+			}
+		}
+		
+		for (L2SkillLearn s : _transformSkillTree.values())
+		{
+			if (s.getRaces() == null)
+			{
+				list.add(SkillTable.getSkillHashCode(s.getSkillId(), s.getSkillLevel()));
+			}
+		}
+		
+		for (L2SkillLearn s : _collectSkillTree.values())
+		{
+			list.add(SkillTable.getSkillHashCode(s.getSkillId(), s.getSkillLevel()));
+		}
+		
+		i = 0;
+		array = new int[list.size()];
+		for (int s : list)
+		{
+			array[i++] = s;
+		}
+		Arrays.sort(array);
+		_allSkillsHashCodes = array;
+		
+		FastList.recycle(list);
+	}
+	
+	/**
+	 * Verify if the give skill is valid for the givem player.
+	 * GM's skills are excluded for GM players.
+	 * @param player the player to verify the skill.
+	 * @param skill the skill to be verified.
+	 * @return {@code true} if the skill is allowed to the given player. 
+	 */
+	public boolean isSkillAllowed(L2PcInstance player, L2Skill skill)
+	{
+		if (skill.isExcludedFromCheck())
+		{
+			return true;
+		}
+		
+		if (player.isGM() && skill.isGMSkill())
+		{
+			return true;
+		}
+		
+		//Prevent accidental skill remove during reload
+		if (_loading)
+		{
+			return true;
+		}
+		
+		final int maxLvl = SkillTable.getInstance().getMaxLevel(skill.getId());
+		final int hashCode = SkillTable.getSkillHashCode(skill.getId(), Math.min(skill.getLevel(), maxLvl));
+		
+		if (Arrays.binarySearch(_skillsByClassIdHashCodes.get(player.getClassId().ordinal()), hashCode) >= 0)
+		{
+			return true;
+		}
+		
+		if (Arrays.binarySearch(_skillsByRaceHashCodes.get(player.getRace().ordinal()), hashCode) >= 0)
+		{
+			return true;
+		}
+		
+		if (Arrays.binarySearch(_allSkillsHashCodes, hashCode) >= 0)
+		{
+			return true;
+		}
+		
+		//Exclude Transfer Skills from this check.
+		if (getTransferSkill(skill.getId(), skill.getLevel(), player.getClassId()) != null)
+		{
+			return true;
+		}
+		
+		return false;
+	}
+	
+	public static SkillTreesData getInstance()
+	{
+		return SingletonHolder._instance;
+	}
+	
+	/**
+	 * Singleton holder for the SkillTreesData class.
+	 */
+	@SuppressWarnings("synthetic-access")
+	private static class SingletonHolder
+	{
+		protected static final SkillTreesData _instance = new SkillTreesData();
+	}
+}

+ 0 - 235
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SubPledgeSkillTree.java

@@ -1,235 +0,0 @@
-/*
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- * 
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.datatables;
-
-import gnu.trove.TIntObjectHashMap;
-
-import java.io.File;
-import java.util.Iterator;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import javolution.util.FastList;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-
-import com.l2jserver.Config;
-import com.l2jserver.gameserver.model.L2Clan;
-import com.l2jserver.gameserver.model.L2Skill;
-
-/**
- * @author JIV
- *
- */
-public class SubPledgeSkillTree
-{
-	private static final Logger _log = Logger.getLogger(SubPledgeSkillTree.class.getName());
-	
-	private TIntObjectHashMap<SubUnitSkill> skilltree = new TIntObjectHashMap<SubPledgeSkillTree.SubUnitSkill>();
-	
-	public SubPledgeSkillTree()
-	{
-		load();
-	}
-	
-	public static SubPledgeSkillTree getInstance()
-	{
-		return SingletonHolder._instance;
-	}
-	
-	public static class SubUnitSkill
-	{
-		private L2Skill skill;
-		private int clanLvl;
-		private int reputation;
-		private int itemId;
-		private int count;
-		
-		public SubUnitSkill(L2Skill skill, int clanLvl, int reputation, int itemId, int count)
-		{
-			super();
-			this.skill = skill;
-			this.clanLvl = clanLvl;
-			this.reputation = reputation;
-			this.itemId = itemId;
-			this.count = count;
-		}
-		
-		public L2Skill getSkill()
-		{
-			return skill;
-		}
-		public int getClanLvl()
-		{
-			return clanLvl;
-		}
-		public int getReputation()
-		{
-			return reputation;
-		}
-		public int getItemId()
-		{
-			return itemId;
-		}
-		public int getCount()
-		{
-			return count;
-		}
-	}
-	
-	public void reload()
-	{
-		load();
-	}
-	
-	private void load()
-	{
-		skilltree.clear();
-		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-		factory.setValidating(false);
-		factory.setIgnoringComments(true);
-		File file = new File(Config.DATAPACK_ROOT, "data/skillTrees/subPledgeSkillTree.xml");
-		Document doc = null;
-		if (file.exists())
-		{
-			try
-			{
-				doc = factory.newDocumentBuilder().parse(file);
-			}
-			catch(Exception e)
-			{
-				_log.log(Level.WARNING, "Could not parse subPledgeSkillTree.xml file: " + e.getMessage(), e);
-			}
-			
-			for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
-			{
-				if ("skill_tree".equalsIgnoreCase(n.getNodeName()))
-				{
-					for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
-					{
-						if ("skill".equalsIgnoreCase(d.getNodeName()))
-						{
-							NamedNodeMap attrs = d.getAttributes();
-							Node att;
-							int skillId;
-							int skillLvl;
-							int clanLvl;
-							int reputation;
-							int itemId;
-							int count;
-							
-							att = attrs.getNamedItem("id");
-							if (att == null)
-							{
-								_log.severe("[SubPledgeSkillTree] Missing id, skipping");
-								continue;
-							}
-							skillId = Integer.parseInt(att.getNodeValue());
-							
-							att = attrs.getNamedItem("level");
-							if (att == null)
-							{
-								_log.severe("[SubPledgeSkillTree] Missing level, skipping");
-								continue;
-							}
-							skillLvl = Integer.parseInt(att.getNodeValue());
-							
-							att = attrs.getNamedItem("reputation");
-							if (att == null)
-							{
-								_log.severe("[SubPledgeSkillTree] Missing reputation, skipping");
-								continue;
-							}
-							reputation = Integer.parseInt(att.getNodeValue());
-							
-							att = attrs.getNamedItem("clan_level");
-							if (att == null)
-							{
-								_log.severe("[SubPledgeSkillTree] Missing clan_level, skipping");
-								continue;
-							}
-							clanLvl = Integer.parseInt(att.getNodeValue());
-							
-							att = attrs.getNamedItem("itemId");
-							if (att == null)
-							{
-								_log.severe("[SubPledgeSkillTree] Missing itemId, skipping");
-								continue;
-							}
-							itemId = Integer.parseInt(att.getNodeValue());
-							
-							att = attrs.getNamedItem("count");
-							if (att == null)
-							{
-								_log.severe("[SubPledgeSkillTree] Missing count, skipping");
-								continue;
-							}
-							count = Integer.parseInt(att.getNodeValue());
-							
-							L2Skill skill = SkillTable.getInstance().getInfo(skillId, skillLvl);
-							if (skill == null)
-							{
-								_log.severe("[SubPledgeSkillTree] Skill "+skillId+" not exist, skipping");
-								continue;
-							}
-							
-							skilltree.put(SkillTable.getSkillHashCode(skill), new SubUnitSkill(skill, clanLvl, reputation, itemId, count));
-						}
-					}
-				}
-			}
-		}
-		_log.info(getClass().getSimpleName()+": Loaded "+skilltree.size()+" SubUnit Skills");
-	}
-	
-	public SubUnitSkill getSkill(int skillhash)
-	{
-		return skilltree.get(skillhash);
-	}
-	
-	public SubUnitSkill[] getAvailableSkills(L2Clan clan)
-	{
-		FastList<SubUnitSkill> list = FastList.newInstance();
-		for (Object obj : skilltree.getValues())
-		{
-			SubUnitSkill skill = (SubUnitSkill) obj;
-			if (skill.getClanLvl() <= clan.getLevel())
-				list.add(skill);
-		}
-		
-		Iterator<SubUnitSkill> it = list.iterator();
-		while (it.hasNext())
-		{
-			SubUnitSkill sus = it.next();
-			if (!clan.isLearnableSubSkill(sus.getSkill()))
-				it.remove();
-		}
-		
-		SubUnitSkill[] result = list.toArray(new SubUnitSkill[list.size()]);
-		FastList.recycle(list);
-		return result;
-	}
-	
-	
-	@SuppressWarnings("synthetic-access")
-	private static class SingletonHolder
-	{
-		protected static final SubPledgeSkillTree _instance = new SubPledgeSkillTree();
-	}
-}

+ 44 - 7
L2J_Server_BETA/java/com/l2jserver/gameserver/instancemanager/TerritoryWarManager.java

@@ -37,10 +37,12 @@ import com.l2jserver.gameserver.Announcements;
 import com.l2jserver.gameserver.ThreadPoolManager;
 import com.l2jserver.gameserver.datatables.ClanTable;
 import com.l2jserver.gameserver.datatables.NpcTable;
-import com.l2jserver.gameserver.datatables.ResidentialSkillTable;
+import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.model.L2Clan;
 import com.l2jserver.gameserver.model.L2SiegeClan;
 import com.l2jserver.gameserver.model.L2Skill;
+import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.model.L2Spawn;
 import com.l2jserver.gameserver.model.L2World;
 import com.l2jserver.gameserver.model.Location;
@@ -345,12 +347,26 @@ public class TerritoryWarManager implements Siegable
 				if (!isTWInProgress() && !SPAWN_WARDS_WHEN_TW_IS_NOT_IN_PROGRESS)
 					ret.decayMe();
 				if (terNew.getOwnerClan() != null && terNew.getOwnedWardIds().contains(newOwnerId + 80))
+				{
 					for(int wardId : terNew.getOwnedWardIds())
-						if (ResidentialSkillTable.getInstance().getSkills(wardId) != null)
-							for (L2Skill sk : ResidentialSkillTable.getInstance().getSkills(wardId))
+					{
+						final FastList<L2SkillLearn> residentialSkills = SkillTreesData.getInstance().getAvailableResidentialSkills(wardId);
+						for (L2SkillLearn s : residentialSkills)
+						{
+							final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+							if (sk != null)
+							{
 								for (L2PcInstance member : terNew.getOwnerClan().getOnlineMembers(0))
+								{
 									if (!member.isInOlympiadMode())
+									{
 										member.addSkill(sk, false);
+									}
+								}
+							}
+						}
+					}
+				}
 			}
 			if (_territoryList.containsKey(oldOwnerId))
 			{
@@ -367,16 +383,37 @@ public class TerritoryWarManager implements Siegable
 				}
 				if (terOld.getOwnerClan() != null)
 				{
-					if (ResidentialSkillTable.getInstance().getSkills(territoryId) != null)
-						for (L2Skill sk : ResidentialSkillTable.getInstance().getSkills(territoryId))
+					final FastList<L2SkillLearn> territorySkills = SkillTreesData.getInstance().getAvailableResidentialSkills(territoryId);
+					for (L2SkillLearn s : territorySkills)
+					{
+						final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+						if (sk != null)
+						{
 							for (L2PcInstance member : terOld.getOwnerClan().getOnlineMembers(0))
+							{
 								member.removeSkill(sk, false);
+							}
+						}
+					}
+					
 					if (!terOld.getOwnedWardIds().isEmpty() && !terOld.getOwnedWardIds().contains(oldOwnerId + 80))
+					{
 						for(int wardId : terOld.getOwnedWardIds())
-							if (ResidentialSkillTable.getInstance().getSkills(wardId) != null)
-								for (L2Skill sk : ResidentialSkillTable.getInstance().getSkills(wardId))
+						{
+							final FastList<L2SkillLearn> wardSkills = SkillTreesData.getInstance().getAvailableResidentialSkills(wardId);
+							for (L2SkillLearn s : wardSkills)
+							{
+								final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+								if (sk != null)
+								{
 									for (L2PcInstance member : terOld.getOwnerClan().getOnlineMembers(0))
+									{
 										member.removeSkill(sk, false);
+									}
+								}
+							}
+						}
+					}
 				}
 			}
 		}

+ 14 - 15
L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2Clan.java

@@ -2737,20 +2737,19 @@ public class L2Clan
 	}
 	
 	/**
-	 * Check if clan learn this squad skill
-	 * @param skill
-	 * @param clan
-	 * @return true if can be added
+	 * Check if this clan can learn the skill for the given skill ID, level.
+	 * @param skillId
+	 * @param skillLevel
+	 * @return {@code true} if skill can be learned.
 	 */
-	public boolean isLearnableSubSkill(L2Skill skill)
+	public boolean isLearnableSubSkill(int skillId, int skillLevel)
 	{
-		int id = skill.getId();
-		L2Skill current = _subPledgeSkills.get(id);
+		L2Skill current = _subPledgeSkills.get(skillId);
 		// is next level?
-		if (current != null && current.getLevel() + 1 == skill.getLevel())
+		if ((current != null) && ((current.getLevel() + 1) == skillLevel))
 			return true;
 		// is first level?
-		if (current == null && skill.getLevel() == 1)
+		if ((current == null) && (skillLevel == 1))
 			return true;
 		// other subpledges
 		for (SubPledge subunit : _subPledges.values())
@@ -2758,18 +2757,18 @@ public class L2Clan
 			//disable academy
 			if (subunit._id == -1)
 				continue;
-			current = subunit._subPledgeSkills.get(id);
+			current = subunit._subPledgeSkills.get(skillId);
 			// is next level?
-			if (current != null && current.getLevel() + 1 == skill.getLevel())
+			if ((current != null) && ((current.getLevel() + 1) == skillLevel))
 				return true;
 			// is first level?
-			if (current == null && skill.getLevel() == 1)
+			if ((current == null) && (skillLevel == 1))
 				return true;
 		}
 		return false;
 	}
 	
-	public boolean isLearnableSubSkill(L2Skill skill, int subType)
+	public boolean isLearnableSubPledgeSkill(L2Skill skill, int subType)
 	{
 		//academy
 		if (subType == -1)
@@ -2786,10 +2785,10 @@ public class L2Clan
 			current = _subPledges.get(subType)._subPledgeSkills.get(id);
 		}
 		// is next level?
-		if (current != null && current.getLevel() + 1 == skill.getLevel())
+		if ((current != null) && (current.getLevel() + 1) == skill.getLevel())
 			return true;
 		// is first level?
-		if (current == null && skill.getLevel() == 1)
+		if ((current == null) && (skill.getLevel() == 1))
 			return true;
 		
 		return false;

+ 1 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2Object.java

@@ -167,6 +167,7 @@ public abstract class L2Object
 		L2TeleporterInstance(L2Npc),
 		L2TownPetInstance(L2Npc),
 		L2TrainerInstance(L2NpcInstance),
+		L2TrainerHealersInstance(L2TrainerInstance),
 		L2TransformManagerInstance(L2MerchantInstance),
 		L2VillageMasterInstance(L2NpcInstance),
 		L2WyvernManagerInstance(L2NpcInstance),

+ 0 - 96
L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2PledgeSkillLearn.java

@@ -1,96 +0,0 @@
-/*
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- * 
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.model;
-
-/**
- * This class ...
- *
- * @version $Revision: 1.2.4.2 $ $Date: 2005/03/27 15:29:33 $
- */
-public final class L2PledgeSkillLearn
-{
-	// these two build the primary key
-	private final int _id;
-	private final int _level;
-	
-	// not needed, just for easier debug
-	private final String _name;
-	
-	private final int _repCost;
-	private final int _baseLvl;
-	private final int _itemId;
-	private final int _itemCount;
-	
-	public L2PledgeSkillLearn(int id, int lvl, int baseLvl, String name, int cost, int itemId, int itemCount)
-	{
-		_id = id;
-		_level = lvl;
-		_baseLvl = baseLvl;
-		_name = name.intern();
-		_repCost = cost;
-		_itemId = itemId;
-		_itemCount = itemCount;
-	}
-	
-	/**
-	 * @return Returns the id.
-	 */
-	public int getId()
-	{
-		return _id;
-	}
-	
-	/**
-	 * @return Returns the level.
-	 */
-	public int getLevel()
-	{
-		return _level;
-	}
-	
-	/**
-	 * @return Returns the minLevel.
-	 */
-	public int getBaseLevel()
-	{
-		return _baseLvl;
-	}
-	
-	/**
-	 * @return Returns the name.
-	 */
-	public String getName()
-	{
-		return _name;
-	}
-	
-	/**
-	 * @return Returns the spCost.
-	 */
-	public int getRepCost()
-	{
-		return _repCost;
-	}
-	
-	public int getItemId()
-	{
-		return _itemId;
-	}
-	
-	public int getItemCount()
-	{
-		return _itemCount;
-	}
-}

+ 2 - 3
L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2Skill.java

@@ -30,7 +30,6 @@ import com.l2jserver.gameserver.datatables.GMSkillTable;
 import com.l2jserver.gameserver.datatables.HeroSkillTable;
 import com.l2jserver.gameserver.datatables.ItemTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
 import com.l2jserver.gameserver.model.actor.L2Attackable;
 import com.l2jserver.gameserver.model.actor.L2Character;
 import com.l2jserver.gameserver.model.actor.L2Npc;
@@ -296,7 +295,7 @@ public abstract class L2Skill implements IChanceSkillTrigger
 	private final boolean _excludedFromCheck;
 	private final boolean _simultaneousCast;
 	
-	private L2ExtractableSkill _extractableItems = null;;
+	private L2ExtractableSkill _extractableItems = null;
 	
 	protected L2Skill(StatsSet set)
 	{
@@ -462,7 +461,7 @@ public abstract class L2Skill implements IChanceSkillTrigger
 		_power = set.getFloat("power", 0.f);
 		_pvpPower = set.getFloat("pvpPower", (float)getPower());
 		_pvePower = set.getFloat("pvePower", (float)getPower());
-		_magicLevel = set.getInteger("magicLvl", SkillTreeTable.getInstance().getMinSkillLevel(_id, _level));
+		_magicLevel = set.getInteger("magicLvl", 0);
 		_levelDepend = set.getInteger("lvlDepend", 0);
 		_ignoreResists = set.getBool("ignoreResists", false);
 		_minChance = set.getInteger("minChance", Config.MIN_DEBUFF_CHANCE);

+ 173 - 58
L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2SkillLearn.java

@@ -14,118 +14,233 @@
  */
 package com.l2jserver.gameserver.model;
 
+import java.util.logging.Logger;
+
+import com.l2jserver.gameserver.templates.StatsSet;
+
 /**
- * This class ...
- *
- * @version $Revision: 1.2.4.2 $ $Date: 2005/03/27 15:29:33 $
+ * @author Zoey76
  */
 public final class L2SkillLearn
 {
-	// these two build the primary key
-	private final int _id;
-	private final int _level;
+	private static final Logger _log = Logger.getLogger(L2SkillLearn.class.getName());
 	
-	// not needed, just for easier debug
-	private final String _name;
+	private final String _skillName;
+	private final int _skillId;
+	private final int _skillLvl;
+	private final int _getLevel;
+	private final boolean _autoGet;
+	private final int _levelUpSp;
+	private final int[][] _itemsIdCount;
+	private final int[] _races;
+	private final int[] _preReqSkillIdLvl;
+	private final int _socialClass;
+	private final boolean _residenceSkill;
+	private final int[] _residenceIds;
+	private final int[][] _subClassLvlNumber;
+	private final boolean _learnedByNpc;
+	private final boolean _learnedByFS;
 	
-	private final int _spCost;
-	private final int _minLevel;
-	private final int _costid;
-	private final int _costcount;
+	/**
+	 * Constructor for L2SkillLearn. 
+	 * @param set the set with the L2SkillLearn data.
+	 */
+	public L2SkillLearn(StatsSet set)
+	{
+		_skillName = set.getString("skillName");
+		_skillId = set.getInteger("skillId");
+		_skillLvl = set.getInteger("skillLvl");
+		_getLevel = set.getInteger("getLevel");
+		_autoGet = set.getBool("autoGet", false);
+		_levelUpSp = set.getInteger("levelUpSp", 0);
+		if (!set.getString("itemsIdCount", "").isEmpty())
+		{
+			final String[] items = set.getString("itemsIdCount").split(";");
+			_itemsIdCount = new int[items.length][2];
+			int i = 0;
+			for (String itemIdCount : items)
+			{
+				_itemsIdCount[i][0] = Integer.parseInt(itemIdCount.split(",")[0]);//Id
+				_itemsIdCount[i][1] = Integer.parseInt(itemIdCount.split(",")[1]);//Count
+				i++;
+			}
+		}
+		else
+		{
+			_itemsIdCount = null;
+		}
+		if (!set.getString("race", "").isEmpty())
+		{
+			_races = set.getIntegerArray("race");
+		}
+		else
+		{
+			_races = null;
+		}
+		if (!set.getString("preReqSkillIdLvl", "").isEmpty())
+		{
+			_preReqSkillIdLvl = new int[2];
+			try
+			{
+				_preReqSkillIdLvl[0] = Integer.parseInt(set.getString("preReqSkillIdLvl").split(",")[0]);
+				_preReqSkillIdLvl[1] = Integer.parseInt(set.getString("preReqSkillIdLvl").split(",")[1]);
+			}
+			catch (Exception e)
+			{
+				_log.severe(getClass().getSimpleName() + ": Malformed preReqSkillIdLvl for Learn Skill Id " + _skillId + " and level " + _skillLvl + "!");
+			}
+		}
+		else
+		{
+			_preReqSkillIdLvl = null;
+		}
+		_socialClass = set.getInteger("socialClass", 0);
+		_residenceSkill = set.getBool("residenceSkill", false);
+		if (!set.getString("residenceIds", "").isEmpty())
+		{
+			_residenceIds = set.getIntegerArray("residenceIds");
+		}
+		else
+		{
+			_residenceIds = null;
+		}
+		if (!set.getString("subClassLvlNumber", "").isEmpty())
+		{
+			final String[] subLvLNumList = set.getString("subClassLvlNumber").split(";");
+			_subClassLvlNumber = new int[subLvLNumList.length][2];
+			int i = 0;
+			for (String subLvlNum : subLvLNumList)
+			{
+				_subClassLvlNumber[i][0] = Integer.parseInt(subLvlNum.split(",")[0]);
+				_subClassLvlNumber[i][1] = Integer.parseInt(subLvlNum.split(",")[1]);
+				i++;
+			}
+		}
+		else
+		{
+			_subClassLvlNumber = null;
+		}
+		_learnedByNpc = set.getBool("learnedByNpc", false);
+		_learnedByFS = set.getBool("learnedByFS", false);
+	}
 	
-	private final boolean _learnedByNpc;
-	private final boolean _learnedByFs;
-	private final boolean _isTransfer;
-	private final boolean _isAutoGet;
+	/**
+	 * @return the name of this skill.
+	 */
+	public String getName()
+	{
+		return _skillName;
+	}
 	
-	public L2SkillLearn(int id, int lvl, int minLvl, String name, int cost, int costid, int costcount, boolean npc, boolean fs, boolean transfer, boolean autoget)
+	/**
+	 * @return the ID of this skill.
+	 */
+	public int getSkillId()
 	{
-		_id = id;
-		_level = lvl;
-		_minLevel = minLvl;
-		_name = name.intern();
-		_spCost = cost;
-		_costid = costid;
-		_costcount = costcount;
-		_learnedByNpc = npc;
-		_learnedByFs = fs;
-		_isTransfer = transfer;
-		_isAutoGet = autoget;
+		return _skillId;
 	}
 	
 	/**
-	 * @return Returns the id.
+	 * @return the level of this skill.
 	 */
-	public int getId()
+	public int getSkillLevel()
 	{
-		return _id;
+		return _skillLvl;
 	}
 	
 	/**
-	 * @return Returns the level.
+	 * @return the minimum level required to acquire this skill.
 	 */
-	public int getLevel()
+	public int getGetLevel()
 	{
-		return _level;
+		return _getLevel;
 	}
 	
 	/**
-	 * @return Returns the minLevel.
+	 * @return the amount of SP/Clan Reputation to acquire this skill.
 	 */
-	public int getMinLevel()
+	public int getLevelUpSp()
 	{
-		return _minLevel;
+		return _levelUpSp;
 	}
 	
 	/**
-	 * @return Returns the name.
+	 * @return {@code true} if the skill is auto-get, this skill is automatically delivered.
 	 */
-	public String getName()
+	public boolean isAutoGet()
+	{
+		return _autoGet;
+	}
+	
+	/**
+	 * @return the multidimensional array with the item IDs and amounts required to acquire this skill.
+	 */
+	public int[][] getItemsIdCount()
 	{
-		return _name;
+		return _itemsIdCount;
 	}
 	
 	/**
-	 * @return Returns the spCost.
+	 * @return the array with the races that can acquire this skill.
 	 */
-	public int getSpCost()
+	public int[] getRaces()
 	{
-		return _spCost;
+		return _races;
 	}
 	
-	public int getIdCost()
+	/**
+	 * @return the array with required skill IDs and levels to acquire this skill.
+	 */
+	public int[] getPreReqSkillIdLvl()
 	{
-		return _costid;
+		return _preReqSkillIdLvl;
 	}
 	
-	public int getCostCount()
+	/**
+	 * @return the social class required to get this skill.
+	 */
+	public int getSocialClass()
 	{
-		return _costcount;
+		return _socialClass;
 	}
 	
 	/**
-	 * Return true if skill can be learned by teachers
+	 * @return {@code true} if this skill is a Residence skill.
 	 */
-	public boolean isLearnedByNPC()
+	public boolean isResidencialSkill()
 	{
-		return _learnedByNpc;
+		return _residenceSkill;
 	}
 	
 	/**
-	 * Return true if skill can be learned by forgotten scroll
+	 * @return the array with the IDs where this skill is available.
 	 */
-	public boolean isLearnedByFS()
+	public int[] getRecidenceIds()
 	{
-		return _learnedByFs;
+		return _residenceIds;
 	}
 	
-	public boolean isTransferSkill()
+	/**
+	 * @return the array with Sub-Class conditions, amount of subclasses and level. 
+	 */
+	public int[][] getSubClassConditions()
 	{
-		return _isTransfer;
+		return _subClassLvlNumber;
 	}
 	
-	public boolean isAutoGetSkill()
+	/**
+	 * @return {@code true} if this skill is learned from Npc.
+	 */
+	public boolean isLearnedByNpc()
+	{
+		return _learnedByNpc;
+	}
+	
+	/**
+	 * @return {@code true} if this skill is learned by Forgotten Scroll.
+	 */
+	public boolean isLearnedByFS()
 	{
-		return _isAutoGet;
+		return _learnedByFS;
 	}
-}
+}

+ 0 - 94
L2J_Server_BETA/java/com/l2jserver/gameserver/model/L2TransformSkillLearn.java

@@ -1,94 +0,0 @@
-/*
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- * 
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.model;
-
-/**
- * This class ...
- *
- * @version $Revision: 0.0.0.1 $ $Date: 2008/03/19 15:10:30 $
- */
-public final class L2TransformSkillLearn
-{
-	// these two build the primary key
-	private final int _race_id;
-	private final int _skill_id;
-	private final int _item_id;
-	private final int _level;
-	
-	// not needed, just for easier debug
-	private final String _name;
-	
-	private final int _sp;
-	private final int _min_level;
-	
-	public L2TransformSkillLearn(int race_id, int skill_id, int item_id, int level, String name, int sp, int min_level)
-	{
-		_race_id = race_id;
-		_skill_id = skill_id;
-		_item_id = item_id;
-		_level = level;
-		_name = name.intern();
-		_sp = sp;
-		_min_level = min_level;
-	}
-	
-	/**
-	 * @return Returns the skill_id.
-	 */
-	public int getId()
-	{
-		return _skill_id;
-	}
-	
-	/**
-	 * @return Returns the level.
-	 */
-	public int getLevel()
-	{
-		return _level;
-	}
-	
-	/**
-	 * @return Returns the minLevel.
-	 */
-	public int getMinLevel()
-	{
-		return _min_level;
-	}
-	
-	/**
-	 * @return Returns the name.
-	 */
-	public String getName()
-	{
-		return _name;
-	}
-	
-	/**
-	 * @return Returns the spCost.
-	 */
-	public int getSpCost()
-	{
-		return _sp;
-	}
-	public int getRace()
-	{
-		return _race_id;
-	}
-	public int getItemId()
-	{
-		return _item_id;
-	}
-}

+ 9 - 7
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2CastleMagicianInstance.java

@@ -14,11 +14,13 @@
  */
 package com.l2jserver.gameserver.model.actor.instance;
 
+import javolution.util.FastList;
+
 import com.l2jserver.Config;
 import com.l2jserver.gameserver.SevenSigns;
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree;
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree.SubUnitSkill;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.instancemanager.InstanceManager;
+import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.model.L2SquadTrainer;
 import com.l2jserver.gameserver.model.actor.L2Character;
 import com.l2jserver.gameserver.model.entity.TvTEvent;
@@ -343,16 +345,16 @@ public class L2CastleMagicianInstance extends L2NpcInstance implements L2SquadTr
 			{
 				if (player.isClanLeader())
 				{
-					AcquireSkillList skilllist = new AcquireSkillList(SkillType.SubUnit);
-					SubUnitSkill[] array = SubPledgeSkillTree.getInstance().getAvailableSkills(player.getClan());
-					if (array.length == 0)
+					AcquireSkillList skilllist = new AcquireSkillList(SkillType.SubClass);
+					FastList<L2SkillLearn> list = SkillTreesData.getInstance().getAvailableSubPledgeSkills(player.getClan());
+					if (list.isEmpty())
 					{
 						player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN);
 						return;
 					}
-					for (SubUnitSkill sus : array)
+					for (L2SkillLearn skillLearn : list)
 					{
-						skilllist.addSkill(sus.getSkill().getId(), sus.getSkill().getLevel(), sus.getSkill().getLevel(), sus.getReputation(), 0);
+						skilllist.addSkill(skillLearn.getSkillId(), skillLearn.getSkillLevel(), skillLearn.getSkillLevel(), skillLearn.getLevelUpSp(), 0);
 					}
 					player.sendPacket(skilllist);
 				}

+ 1 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2ClassMasterInstance.java

@@ -102,7 +102,7 @@ public final class L2ClassMasterInstance extends L2MerchantInstance
 		}
 		else if(command.startsWith("learn_skills"))
 		{
-			player.giveAvailableSkills();
+			player.giveAvailableSkills(Config.AUTO_LEARN_FS_SKILLS, true);
 		}
 		else if(command.startsWith("increase_clan_level"))
 		{

+ 36 - 21
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2FishermanInstance.java

@@ -14,22 +14,20 @@
  */
 package com.l2jserver.gameserver.model.actor.instance;
 
+import javolution.util.FastList;
+
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.model.L2Skill;
 import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList;
-import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
 
-public class L2FishermanInstance extends L2MerchantInstance
+public final class L2FishermanInstance extends L2MerchantInstance
 {
-	/**
-	 * @param objectId
-	 * @param template
-	 */
 	public L2FishermanInstance(int objectId, L2NpcTemplate template)
 	{
 		super(objectId, template);
@@ -42,50 +40,67 @@ public class L2FishermanInstance extends L2MerchantInstance
 		String pom = "";
 		
 		if (val == 0)
+		{
 			pom = "" + npcId;
+		}
 		else
+		{
 			pom = npcId + "-" + val;
+		}
 		
 		return "data/html/fisherman/" + pom + ".htm";
 	}
 	
+	@Override
+	public void onBypassFeedback(L2PcInstance player, String command)
+	{
+		if (command.equalsIgnoreCase("FishSkillList"))
+		{
+			showFishSkillList(player);
+		}
+		else
+		{
+			super.onBypassFeedback(player, command);
+		}
+	}
+	
 	public static void showFishSkillList(L2PcInstance player)
 	{
-		L2SkillLearn[] skills = SkillTreeTable.getInstance().getAvailableSkills(player);
-		AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.Fishing);
+		final FastList<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailableFishingSkills(player);
+		final AcquireSkillList asl = new AcquireSkillList(SkillType.Fishing);
 		
-		int counts = 0;
+		int count = 0;
 		
 		for (L2SkillLearn s : skills)
 		{
-			L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
+			final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
 			
 			if (sk == null)
+			{
 				continue;
-			
-			counts++;
-			asl.addSkill(s.getId(), s.getLevel(), s.getLevel(), s.getSpCost(), 1);
+			}
+			count++;
+			asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 1);
 		}
 		
-		if (counts == 0)
+		if (count == 0)
 		{
-			int minlevel = SkillTreeTable.getInstance().getMinLevelForNewSkill(player);
+			final int minlLevel = SkillTreesData.getInstance().getMinLevelForNewSkill(player, SkillTreesData.getInstance().getFishingSkillTree());
 			
-			if (minlevel > 0)
+			if (minlLevel > 0)
 			{
-				// No more skills to learn, come back when you level.
 				SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1);
-				sm.addNumber(minlevel);
+				sm.addNumber(minlLevel);
 				player.sendPacket(sm);
 			}
 			else
+			{
 				player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN));
+			}
 		}
 		else
 		{
 			player.sendPacket(asl);
 		}
-		
-		player.sendPacket(ActionFailed.STATIC_PACKET);
 	}
 }

+ 24 - 16
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2FortSupportCaptainInstance.java

@@ -16,13 +16,16 @@ package com.l2jserver.gameserver.model.actor.instance;
 
 import java.util.StringTokenizer;
 
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree;
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree.SubUnitSkill;
+import javolution.util.FastList;
+
+import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
+import com.l2jserver.gameserver.model.L2Skill;
+import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.model.L2SquadTrainer;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList;
 import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
-import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
 import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
@@ -103,18 +106,28 @@ public class L2FortSupportCaptainInstance extends L2MerchantInstance implements
 			{
 				if (player.isClanLeader())
 				{
-					AcquireSkillList skilllist = new AcquireSkillList(SkillType.SubUnit);
-					SubUnitSkill[] array = SubPledgeSkillTree.getInstance().getAvailableSkills(player.getClan());
-					if (array.length == 0)
+					final FastList<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailableSubPledgeSkills(player.getClan());
+					final AcquireSkillList asl = new AcquireSkillList(SkillType.SubPledge);
+					
+					int count = 0;
+					for (L2SkillLearn s: skills)
+					{
+						final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+						if (sk != null)
+						{
+							asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 0);
+							count++;
+						}
+					}
+					//TODO: Missing some system message?
+					if (count == 0)
 					{
-						player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN);
-						return;
+						player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN));
 					}
-					for (SubUnitSkill sus : array)
+					else
 					{
-						skilllist.addSkill(sus.getSkill().getId(), sus.getSkill().getLevel(), sus.getSkill().getLevel(), sus.getReputation(), 0);
+						player.sendPacket(asl);
 					}
-					player.sendPacket(skilllist);
 				}
 				else
 				{
@@ -144,8 +157,6 @@ public class L2FortSupportCaptainInstance extends L2MerchantInstance implements
 	
 	private void showMessageWindow(L2PcInstance player, int val)
 	{
-		player.sendPacket(ActionFailed.STATIC_PACKET);
-		
 		String filename;
 		
 		if (val == 0)
@@ -170,9 +181,6 @@ public class L2FortSupportCaptainInstance extends L2MerchantInstance implements
 		return false;
 	}
 	
-	/* (non-Javadoc)
-	 * @see com.l2jserver.gameserver.model.actor.L2SquadTrainer#showSubUnitSkillList(com.l2jserver.gameserver.model.actor.instance.L2PcInstance)
-	 */
 	@Override
 	public void showSubUnitSkillList(L2PcInstance player)
 	{

+ 67 - 35
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2NpcInstance.java

@@ -14,9 +14,12 @@
  */
 package com.l2jserver.gameserver.model.actor.instance;
 
+import javolution.util.FastList;
+import javolution.util.FastMap;
+
 import com.l2jserver.Config;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.model.L2Effect;
 import com.l2jserver.gameserver.model.L2Skill;
 import com.l2jserver.gameserver.model.L2SkillLearn;
@@ -25,7 +28,7 @@ import com.l2jserver.gameserver.model.actor.status.FolkStatus;
 import com.l2jserver.gameserver.model.base.ClassId;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList;
-import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
 import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.skills.effects.EffectBuff;
@@ -72,40 +75,56 @@ public class L2NpcInstance extends L2Npc
 	}
 	
 	/**
-	 * this displays SkillList to the player.
-	 * @param player
+	 * Displays Skill Tree for a given player, npc and class Id.
+	 * @param player the active character.
+	 * @param npc the last folk.
+	 * @param classId player's active class id.
 	 */
 	public static void showSkillList(L2PcInstance player, L2Npc npc, ClassId classId)
 	{
 		if (Config.DEBUG)
+		{
 			_log.fine("SkillList activated on: "+npc.getObjectId());
+		}
 		
-		int npcId = npc.getTemplate().npcId;
+		final int npcId = npc.getTemplate().npcId;
 		
-		if (npcId == 32611)
+		if (npcId == 32611) //Tolonis (Officer)
 		{
-			L2SkillLearn[] skills = SkillTreeTable.getInstance().getAvailableSpecialSkills(player);
-			AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.Special);
+			final FastList<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailableCollectSkills(player);
+			final AcquireSkillList asl = new AcquireSkillList(SkillType.Collect);
 			
 			int counts = 0;
 			
 			for (L2SkillLearn s : skills)
 			{
-				L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
+				final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
 				
-				if (sk == null)
-					continue;
-				
-				counts++;
-				asl.addSkill(s.getId(), s.getLevel(), s.getLevel(), 0, 1);
+				if (sk != null)
+				{
+					counts++;
+					asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), 0, 1);
+				}
 			}
 			
 			if (counts == 0) // No more skills to learn, come back when you level.
-				player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN));
+			{
+				final int minLevel = SkillTreesData.getInstance().getMinLevelForNewSkill(player, SkillTreesData.getInstance().getCollectSkillTree());
+				if (minLevel > 0)
+				{
+					SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1);
+					sm.addNumber(minLevel);
+					player.sendPacket(sm);
+				}
+				else
+				{
+					player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN));
+				}
+			}
 			else
+			{
 				player.sendPacket(asl);
-			
-			player.sendPacket(ActionFailed.STATIC_PACKET);
+			}
 			return;
 		}
 		
@@ -132,37 +151,50 @@ public class L2NpcInstance extends L2Npc
 			return;
 		}
 		
-		L2SkillLearn[] skills = SkillTreeTable.getInstance().getAvailableSkills(player, classId);
-		AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.Usual);
-		int counts = 0;
+		//Normal skills, No LearnedByFS, no AutoGet skills.
+		final FastList<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailableSkills(player, classId, false, false);
+		final AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.ClassTransform);
+		int count = 0;
 		
 		for (L2SkillLearn s: skills)
 		{
-			L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
-			if (sk == null)
-				continue;
-			
-			int cost = SkillTreeTable.getInstance().getSkillCost(player, sk);
-			counts++;
-			
-			asl.addSkill(s.getId(), s.getLevel(), s.getLevel(), cost, 0);
+			final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+			if (sk != null)
+			{
+				asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 0);
+				count++;
+			}
 		}
 		
-		if (counts == 0)
+		if (count == 0)
 		{
-			int minlevel = SkillTreeTable.getInstance().getMinLevelForNewSkill(player, classId);
-			if (minlevel > 0)
+			final FastMap<Integer, L2SkillLearn> skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(classId);
+			
+			final int minLevel = SkillTreesData.getInstance().getMinLevelForNewSkill(player, skillTree);
+			if (minLevel > 0)
 			{
 				SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1);
-				sm.addNumber(minlevel);
+				sm.addNumber(minLevel);
 				player.sendPacket(sm);
 			}
 			else
-				player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN));
+			{
+				//TODO: Is this SysMsg really used here?
+				if (player.getClassId().level() == 1)
+				{
+					SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.NO_SKILLS_TO_LEARN_RETURN_AFTER_S1_CLASS_CHANGE);
+					sm.addNumber(2);
+					player.sendPacket(sm);
+				}
+				else
+				{
+					player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN));
+				}
+			}
 		}
 		else
+		{
 			player.sendPacket(asl);
-		
-		player.sendPacket(ActionFailed.STATIC_PACKET);
+		}
 	}
 }

+ 110 - 191
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java

@@ -72,7 +72,7 @@ import com.l2jserver.gameserver.datatables.NobleSkillTable;
 import com.l2jserver.gameserver.datatables.NpcTable;
 import com.l2jserver.gameserver.datatables.PetDataTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.handler.IItemHandler;
 import com.l2jserver.gameserver.handler.ItemHandler;
 import com.l2jserver.gameserver.instancemanager.AntiFeedManager;
@@ -326,21 +326,6 @@ public final class L2PcInstance extends L2Playable
 	public static final int STORE_PRIVATE_MANUFACTURE = 5;
 	public static final int STORE_PRIVATE_PACKAGE_SELL = 8;
 	
-	/** The table containing all minimum level needed for each Expertise (None, D, C, B, A, S, S80, S84)*/
-	private static final int[] EXPERTISE_LEVELS =
-	{
-		SkillTreeTable.getInstance().getExpertiseLevel(0), //NONE
-		SkillTreeTable.getInstance().getExpertiseLevel(1), //D
-		SkillTreeTable.getInstance().getExpertiseLevel(2), //C
-		SkillTreeTable.getInstance().getExpertiseLevel(3), //B
-		SkillTreeTable.getInstance().getExpertiseLevel(4), //A
-		SkillTreeTable.getInstance().getExpertiseLevel(5), //S
-		SkillTreeTable.getInstance().getExpertiseLevel(6), //S80
-		SkillTreeTable.getInstance().getExpertiseLevel(7)  //S84
-	};
-	
-	private static final int[] COMMON_CRAFT_LEVELS = { 5, 20, 28, 36, 43, 49, 55, 62, 70 };
-	
 	public class AIAccessor extends L2Character.AIAccessor
 	{
 		protected AIAccessor()
@@ -598,8 +583,6 @@ public final class L2PcInstance extends L2Playable
 	private final List<L2PcInstance> _snoopListener = new FastList<L2PcInstance>();
 	private final List<L2PcInstance> _snoopedPlayer = new FastList<L2PcInstance>();
 	
-	private ClassId _skillLearningClassId;
-	
 	// hennas
 	private final L2HennaInstance[] _henna = new L2HennaInstance[3];
 	private int _hennaSTR;
@@ -708,8 +691,6 @@ public final class L2PcInstance extends L2Playable
 	
 	//private byte _updateKnownCounter = 0;
 	
-	/** The current higher Expertise of the L2PcInstance (None=0, D=1, C=2, B=3, A=4, S=5, S80=6, S84=7)*/
-	private int _expertiseIndex; // index in EXPERTISE_LEVELS
 	private int _expertiseArmorPenalty = 0;
 	private int _expertiseWeaponPenalty = 0;
 	
@@ -1368,7 +1349,6 @@ public final class L2PcInstance extends L2Playable
 		_newbie = newbieRewards;
 	}
 	
-	
 	public void setBaseClass(int baseClass)
 	{
 		_baseClass = baseClass;
@@ -2338,7 +2318,7 @@ public final class L2PcInstance extends L2Playable
 			{
 				int crystaltype = item.getItem().getCrystalType();
 				
-				if (crystaltype > getExpertiseIndex())
+				if (crystaltype > getExpertiseLevel())
 				{
 					if (item.isWeapon() && crystaltype > weaponPenalty)
 						weaponPenalty = crystaltype;
@@ -2351,7 +2331,7 @@ public final class L2PcInstance extends L2Playable
 		boolean changed = false;
 		
 		// calc armor penalty
-		armorPenalty = armorPenalty - getExpertiseIndex();
+		armorPenalty = armorPenalty - getExpertiseLevel();
 		
 		if (armorPenalty < 0)
 			armorPenalty = 0;
@@ -2371,7 +2351,7 @@ public final class L2PcInstance extends L2Playable
 		}
 		
 		// calc weapon penalty
-		weaponPenalty = weaponPenalty - getExpertiseIndex();
+		weaponPenalty = weaponPenalty - getExpertiseLevel();
 		if (weaponPenalty < 0)
 			weaponPenalty = 0;
 		else if (weaponPenalty > 4)
@@ -2617,8 +2597,9 @@ public final class L2PcInstance extends L2Playable
 			if (getClan() != null)
 				getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this));
 			
-			if (Config.AUTO_LEARN_SKILLS)
-				rewardSkills();
+			//Add AutoGet skills and normal skills and/or learnByFS depending on configurations.
+			rewardSkills();
+			
 			if (!isGM() && Config.DECREASE_SKILL_LEVEL)
 				checkPlayerSkills();
 		}
@@ -2772,78 +2753,26 @@ public final class L2PcInstance extends L2Playable
 	}
 	
 	/**
-	 * Give Expertise skill of this level and remove beginner Lucky skill.<BR><BR>
-	 *
-	 * <B><U> Actions</U> :</B><BR><BR>
-	 * <li>Get the Level of the L2PcInstance </li>
-	 * <li>Add the Expertise skill corresponding to its Expertise level</li>
-	 * <li>Update the overloaded status of the L2PcInstance</li><BR><BR>
-	 *
-	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T give other free skills (SP needed = 0)</B></FONT><BR><BR>
-	 *
+	 * This method reward all AutoGet skills and Normal skills if Auto-Learn configuration is true.<br>
 	 */
 	public void rewardSkills()
 	{
-		// Get the Level of the L2PcInstance
-		int lvl = getLevel();
-		
-		// Calculate the current higher Expertise of the L2PcInstance
-		for (int i=0; i < EXPERTISE_LEVELS.length; i++)
-		{
-			if (lvl >= EXPERTISE_LEVELS[i])
-				setExpertiseIndex(i);
-		}
-		
-		// Add the Expertise skill corresponding to its Expertise level
-		if (getExpertiseIndex() > 0)
+		//Give all normal skills if activated Auto-Learn is activated, included AutoGet skills.
+		if (Config.AUTO_LEARN_SKILLS)
 		{
-			L2Skill skill = SkillTable.getInstance().getInfo(239, getExpertiseIndex());
-			addSkill(skill, true);
-			
-			if (Config.DEBUG) _log.fine("awarded "+getName()+" with new expertise.");
-			
+			giveAvailableSkills(Config.AUTO_LEARN_FS_SKILLS, true);
 		}
 		else
 		{
-			if (Config.DEBUG) _log.fine("No skills awarded at lvl: "+lvl);
-		}
-		
-		//Active skill dwarven craft
-		
-		if (getSkillLevel(1321) < 1 && getClassId().equalsOrChildOf(ClassId.dwarvenFighter))
-		{
-			L2Skill skill = SkillTable.FrequentSkill.DWARVEN_CRAFT.getSkill();
-			addSkill(skill, true);
+			giveAvailableAutoGetSkills();
 		}
 		
-		//Active skill common craft
-		if (getSkillLevel(1322) < 1)
-		{
-			L2Skill skill = SkillTable.FrequentSkill.COMMON_CRAFT.getSkill();
-			addSkill(skill, true);
-		}
-		
-		for(int i = 0; i < COMMON_CRAFT_LEVELS.length; i++)
-		{
-			if(lvl >= COMMON_CRAFT_LEVELS[i] && getSkillLevel(1320) < (i+1))
-			{
-				L2Skill skill = SkillTable.getInstance().getInfo(1320, (i+1));
-				addSkill(skill, true);
-			}
-		}
-		
-		// Auto-Learn skills if activated
-		if (Config.AUTO_LEARN_SKILLS)
-		{
-			giveAvailableSkills();
-		}
 		checkItemRestriction();
 		sendSkillList();
 	}
 	
 	/**
-	 * Regive all skills which aren't saved to database, like Noble, Hero, Clan Skills<BR><BR>
-	 *
+	 * Re-give all skills which aren't saved to database, like Noble, Hero, Clan Skills.<br>
 	 */
 	public void regiveTemporarySkills()
 	{
@@ -2879,21 +2808,24 @@ public final class L2PcInstance extends L2Playable
 	}
 	
 	/**
-	 * Give all available skills to the player.<br><br>
-	 *
+	 * Give all available skills to the player.<br>
+	 * @param includedByFs
+	 * @param includeAutoGet
+	 * @return skillCounter, the amount of new skills added.
 	 */
-	public int giveAvailableSkills()
+	public int giveAvailableSkills(boolean includedByFs, boolean includeAutoGet)
 	{
 		int unLearnable = 0;
 		int skillCounter = 0;
 		
 		// Get available skills
-		L2SkillLearn[] skills = SkillTreeTable.getInstance().getAvailableSkills(this, getClassId());
-		while (skills.length > unLearnable)
+		FastList<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailableSkills(this, getClassId(), includedByFs, includeAutoGet);
+		
+		while (skills.size() > unLearnable)
 		{
 			for (L2SkillLearn s: skills)
 			{
-				L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
+				L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
 				if (sk == null || (sk.getId() == L2Skill.SKILL_DIVINE_INSPIRATION && !Config.AUTO_LEARN_DIVINE_INSPIRATION && !isGM()))
 				{
 					unLearnable++;
@@ -2901,7 +2833,9 @@ public final class L2PcInstance extends L2Playable
 				}
 				
 				if (getSkillLevel(sk.getId()) == -1)
+				{
 					skillCounter++;
+				}
 				
 				// fix when learning toggle skills
 				if (sk.isToggle())
@@ -2914,18 +2848,42 @@ public final class L2PcInstance extends L2Playable
 						sk.getEffects(this, this);
 					}
 				}
-				
 				addSkill(sk, true);
 			}
 			
-			// Get new available skills
-			skills = SkillTreeTable.getInstance().getAvailableSkills(this, getClassId());
+			//Get new available skills, some skills depend of previous skills to be available.
+			skills = SkillTreesData.getInstance().getAvailableSkills(this, getClassId(), includedByFs, includeAutoGet);
 		}
 		
 		sendMessage("You have learned " + skillCounter + " new skills.");
 		return skillCounter;
 	}
 	
+	/**
+	 * Give all available AutoGet skills to the player.<br>
+	 */
+	public void giveAvailableAutoGetSkills()
+	{
+		// Get available skills
+		final FastList<L2SkillLearn> autoGetSkills = SkillTreesData.getInstance().getAvailableAutoGetSkills(this);
+		
+		if ((autoGetSkills != null) && !autoGetSkills.isEmpty())
+		{
+			for (L2SkillLearn s: autoGetSkills)
+			{
+				final L2Skill skill = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+				if (skill != null)
+				{
+					addSkill(skill, true);
+				}
+				else
+				{
+					_log.warning("Skipped null autoGet Skill for player:" + getName() + "[" + getObjectId() + "]");
+				}
+			}
+		}
+	}
+	
 	/** Set the Experience value of the L2PcInstance. */
 	public void setExp(long exp)
 	{
@@ -5452,24 +5410,19 @@ public final class L2PcInstance extends L2Playable
 							}
 						}
 					}
-					if (Config.ALT_GAME_DELEVEL)
+					//If player is Lucky shouldn't get penalized.
+					if (Config.ALT_GAME_DELEVEL && !isLucky())
 					{
 						// Reduce the Experience of the L2PcInstance in function of the calculated Death Penalty
 						// NOTE: deathPenalty +- Exp will update karma
 						// Penalty is lower if the player is at war with the pk (war has to be declared)
-						if (getSkillLevel(L2Skill.SKILL_LUCKY) < 0 || getStat().getLevel() > 9)
-						{
-							boolean siege_npc = false;
-							if (killer instanceof L2DefenderInstance || killer instanceof L2FortCommanderInstance)
-								siege_npc = true;
-							deathPenalty(pk != null && getClan() != null && getClan().isAtWarWith(pk.getClanId()), pk != null, siege_npc);
-						}
-						
+						final boolean siegeNpc = (killer instanceof L2DefenderInstance) || (killer instanceof L2FortCommanderInstance);
+						final boolean atWar = (pk != null) && (getClan() != null) && (getClan().isAtWarWith(pk.getClanId()));
+						deathPenalty(atWar, (pk != null), siegeNpc);
 					}
-					else
+					else if (!(isInsideZone(ZONE_PVP) && !isInSiege()) || (pk == null))
 					{
-						if (!(isInsideZone(ZONE_PVP) && !isInSiege()) || pk == null)
-							onDieUpdateKarma(); // Update karma if delevel is not allowed
+						onDieUpdateKarma(); // Update karma if delevel is not allowed
 					}
 				}
 			}
@@ -5834,6 +5787,15 @@ public final class L2PcInstance extends L2Playable
 		}
 	}
 	
+	/**
+	 * TODO: Unhardcode by implementing Lucky effect (Support for effects on passive skills required).
+	 * @return Returns {@code true} if player has Lucky skill and is level 9 or less.
+	 */
+	public boolean isLucky()
+	{
+		return ((getLevel() <= 9) && (getKnownSkill(194) != null));
+	}
+	
 	/**
 	 * Restore the specified % of experience this L2PcInstance has
 	 * lost and sends a Server->Client StatusUpdate packet.<BR><BR>
@@ -5967,32 +5929,12 @@ public final class L2PcInstance extends L2Playable
 		return _partyroom > 0;
 	}
 	
-	/**
-	 * Manage the increase level task of a L2PcInstance (Max MP, Max MP, Recommandation, Expertise and beginner skills...).<BR><BR>
-	 *
-	 * <B><U> Actions</U> :</B><BR><BR>
-	 * <li>Send a Server->Client System Message to the L2PcInstance : YOU_INCREASED_YOUR_LEVEL </li>
-	 * <li>Send a Server->Client packet StatusUpdate to the L2PcInstance with new LEVEL, MAX_HP and MAX_MP </li>
-	 * <li>Set the current HP and MP of the L2PcInstance, Launch/Stop a HP/MP/CP Regeneration Task and send StatusUpdate packet to all other L2PcInstance to inform (exclusive broadcast)</li>
-	 * <li>Recalculate the party level</li>
-	 * <li>Recalculate the number of Recommandation that the L2PcInstance can give</li>
-	 * <li>Give Expertise skill of this level and remove beginner Lucky skill</li><BR><BR>
-	 *
-	 */
-	public void increaseLevel()
-	{
-		// Set the current HP and MP of the L2Character, Launch/Stop a HP/MP/CP Regeneration Task and send StatusUpdate packet to all other L2PcInstance to inform (exclusive broadcast)
-		setCurrentHpMp(getMaxHp(),getMaxMp());
-		setCurrentCp(getMaxCp());
-	}
-	
 	/**
 	 * Stop the HP/MP/CP Regeneration task.<BR><BR>
 	 *
 	 * <B><U> Actions</U> :</B><BR><BR>
 	 * <li>Set the RegenActive flag to False </li>
 	 * <li>Stop the HP/MP/CP Regeneration task </li><BR><BR>
-	 *
 	 */
 	public void stopAllTimers()
 	{
@@ -6319,22 +6261,6 @@ public final class L2PcInstance extends L2Playable
 		return _privatestore;
 	}
 	
-	/**
-	 * Set the _skillLearningClassId object of the L2PcInstance.<BR><BR>
-	 */
-	public void setSkillLearningClassId(ClassId classId)
-	{
-		_skillLearningClassId = classId;
-	}
-	
-	/**
-	 * Return the _skillLearningClassId object of the L2PcInstance.<BR><BR>
-	 */
-	public ClassId getSkillLearningClassId()
-	{
-		return _skillLearningClassId;
-	}
-	
 	/**
 	 * Set the _clan object, _clanId, _clanLeader Flag and title of the L2PcInstance.<BR><BR>
 	 */
@@ -7934,8 +7860,10 @@ public final class L2PcInstance extends L2Playable
 		L2Skill oldSkill = super.addSkill(newSkill);
 		
 		// Add or update a L2PcInstance skill in the character_skills table of the database
-		if (store) storeSkill(newSkill, oldSkill, -1);
-		
+		if (store)
+		{
+			storeSkill(newSkill, oldSkill, -1);
+		}
 		return oldSkill;
 	}
 	
@@ -7981,7 +7909,6 @@ public final class L2PcInstance extends L2Playable
 		L2Skill oldSkill = super.removeSkill(skill);
 		
 		Connection con = null;
-		
 		try
 		{
 			// Remove or update a L2PcInstance skill from the character_skills table of the database
@@ -8021,8 +7948,7 @@ public final class L2PcInstance extends L2Playable
 	}
 	
 	/**
-	 * Add or update a L2PcInstance skill in the character_skills table of the database.
-	 * <BR><BR>
+	 * Add or update a L2PcInstance skill in the character_skills table of the database.<br>
 	 * If newClassIndex > -1, the skill will be stored with that class index, not the current one.
 	 */
 	private void storeSkill(L2Skill newSkill, L2Skill oldSkill, int newClassIndex)
@@ -8033,7 +7959,6 @@ public final class L2PcInstance extends L2Playable
 			classIndex = newClassIndex;
 		
 		Connection con = null;
-		
 		try
 		{
 			con = L2DatabaseFactory.getInstance().getConnection();
@@ -8075,51 +8000,50 @@ public final class L2PcInstance extends L2Playable
 	}
 	
 	/**
-	 * Retrieve from the database all skills of this L2PcInstance and add them to _skills.<BR><BR>
+	 * Retrieve from the database all skills of this L2PcInstance and add them to _skills.
 	 */
 	private void restoreSkills()
 	{
 		Connection con = null;
-		
 		try
 		{
 			// Retrieve all skills of this L2PcInstance from the database
 			con = L2DatabaseFactory.getInstance().getConnection();
-			PreparedStatement statement = con.prepareStatement(RESTORE_SKILLS_FOR_CHAR);
+			final PreparedStatement statement = con.prepareStatement(RESTORE_SKILLS_FOR_CHAR);
 			
 			statement.setInt(1, getObjectId());
 			statement.setInt(2, getClassIndex());
-			ResultSet rset = statement.executeQuery();
+			final ResultSet rset = statement.executeQuery();
 			
 			// Go though the recordset of this SQL query
 			while (rset.next())
 			{
-				int id = rset.getInt("skill_id");
-				int level = rset.getInt("skill_level");
-				
-				if (id > 9000 && id < 9007)
-					continue; // fake skills for base stats
+				final int id = rset.getInt("skill_id");
+				final int level = rset.getInt("skill_level");
 				
 				// Create a L2Skill object for each record
-				L2Skill skill = SkillTable.getInstance().getInfo(id, level);
+				final L2Skill skill = SkillTable.getInstance().getInfo(id, level);
+				
+				if (skill == null)
+				{
+					_log.warning("Skipped null skill Id: " + id + " Level: " + level + " while restoring player skills for playerObjId: " + getObjectId());
+					continue;
+				}
 				
 				// Add the L2Skill object to the L2Character _skills and its Func objects to the calculator set of the L2Character
 				super.addSkill(skill);
 				
 				if (Config.SKILL_CHECK_ENABLE && (!isGM() || Config.SKILL_CHECK_GM))
 				{
-					if (!SkillTreeTable.getInstance().isSkillAllowed(this, skill))
+					if (!SkillTreesData.getInstance().isSkillAllowed(this, skill))
 					{
-						Util.handleIllegalPlayerAction(this, "Player " + getName() +
-								" has invalid skill " + skill.getName() +
-								" ("+skill.getId() + "/" + skill.getLevel() + "), class:" +
-								getTemplate().className, 1);
+						Util.handleIllegalPlayerAction(this, "Player " + getName() + " has invalid skill " + skill.getName() +
+								" ("+skill.getId() + "/" + skill.getLevel() + "), class:" + getTemplate().className, 1);
 						if (Config.SKILL_CHECK_REMOVE)
 							removeSkill(skill);
 					}
 				}
 			}
-			
 			rset.close();
 			statement.close();
 		}
@@ -8134,7 +8058,7 @@ public final class L2PcInstance extends L2Playable
 	}
 	
 	/**
-	 * Retrieve from the database all skill effects of this L2PcInstance and add them to the player.<BR><BR>
+	 * Retrieve from the database all skill effects of this L2PcInstance and add them to the player.
 	 */
 	@Override
 	public void restoreEffects()
@@ -10532,20 +10456,16 @@ public final class L2PcInstance extends L2Playable
 			if (Config.DEBUG)
 				_log.info(getName() + " added class ID " + classId + " as a sub class at index " + classIndex + ".");
 			
-			ClassId subTemplate = ClassId.values()[classId];
-			Collection<L2SkillLearn> skillTree = SkillTreeTable.getInstance().getAllowedSkills(subTemplate);
-			
-			if (skillTree == null)
-				return true;
-			
-			Map<Integer, L2Skill> prevSkillList = new FastMap<Integer, L2Skill>();
+			final ClassId subTemplate = ClassId.values()[classId];
+			final FastMap<Integer, L2SkillLearn> skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(subTemplate);
+			final FastMap<Integer, L2Skill> prevSkillList = new FastMap<Integer, L2Skill>();
 			
-			for (L2SkillLearn skillInfo : skillTree)
+			for (L2SkillLearn skillInfo : skillTree.values())
 			{
-				if (skillInfo.getMinLevel() <= 40)
+				if (skillInfo.getGetLevel() <= 40)
 				{
-					L2Skill prevSkill = prevSkillList.get(skillInfo.getId());
-					L2Skill newSkill = SkillTable.getInstance().getInfo(skillInfo.getId(), skillInfo.getLevel());
+					L2Skill prevSkill = prevSkillList.get(skillInfo.getSkillId());
+					L2Skill newSkill = SkillTable.getInstance().getInfo(skillInfo.getSkillId(), skillInfo.getSkillLevel());
 					
 					if (prevSkill != null && (prevSkill.getLevel() > newSkill.getLevel()))
 						continue;
@@ -11132,19 +11052,15 @@ public final class L2PcInstance extends L2Playable
 	}
 	
 	/**
-	 * @param expertiseIndex The expertiseIndex to set.
+	 * Expertise of the L2PcInstance (None=0, D=1, C=2, B=3, A=4, S=5, S80=6, S84=7)
+	 * @return int Expertise skill level.
 	 */
-	public void setExpertiseIndex(int expertiseIndex)
+	public int getExpertiseLevel()
 	{
-		_expertiseIndex = expertiseIndex;
-	}
-	
-	/**
-	 * @return Returns the expertiseIndex.
-	 */
-	public int getExpertiseIndex()
-	{
-		return _expertiseIndex;
+		int level = getSkillLevel(239);
+		if (level < 0)
+			level = 0;
+		return level;
 	}
 	
 	@Override
@@ -13009,6 +12925,7 @@ public final class L2PcInstance extends L2Playable
 				&& !(killer instanceof L2PcInstance) && !(this.isGM())
 				&& !(this.getCharmOfLuck() && killer.isRaid())
 				&& !isPhoenixBlessed()
+				&& !isLucky()
 				&& !(TvTEvent.isStarted() && TvTEvent.isPlayerParticipant(getObjectId()))
 				&& !(this.isInsideZone(L2Character.ZONE_PVP)||this.isInsideZone(L2Character.ZONE_SIEGE)))
 			
@@ -14775,7 +14692,7 @@ public final class L2PcInstance extends L2Playable
 			int level = getSkillLevel(id);
 			if (level >= 100) // enchanted skill
 				level = SkillTable.getInstance().getMaxLevel(id);
-			L2SkillLearn learn = SkillTreeTable.getInstance().getSkillLearnBySkillIdLevel(getClassId(), id, level);
+			final L2SkillLearn learn = SkillTreesData.getInstance().getClassSkill(id, level, getClassId());
 			// not found - not a learn skill?
 			if (learn == null)
 			{
@@ -14784,7 +14701,7 @@ public final class L2PcInstance extends L2Playable
 			else
 			{
 				// player level is too low for such skill level
-				if (getLevel() < (learn.getMinLevel() - 9))
+				if (getLevel() < (learn.getGetLevel() - 9))
 					deacreaseSkillLevel(id);
 			}
 		}
@@ -14793,12 +14710,14 @@ public final class L2PcInstance extends L2Playable
 	private void deacreaseSkillLevel(int id)
 	{
 		int nextLevel = -1;
-		for (L2SkillLearn sl : SkillTreeTable.getInstance().getAllowedSkills(getClassId()))
+		final FastMap<Integer, L2SkillLearn> skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(getClassId());
+		
+		for (L2SkillLearn sl : skillTree.values())
 		{
-			if (sl.getId() == id && nextLevel < sl.getLevel() && getLevel() >= (sl.getMinLevel() - 9))
+			if (sl.getSkillId() == id && nextLevel < sl.getSkillLevel() && getLevel() >= (sl.getGetLevel() - 9))
 			{
 				// next possible skill level
-				nextLevel = sl.getLevel();
+				nextLevel = sl.getSkillLevel();
 			}
 		}
 		

+ 190 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerHealersInstance.java

@@ -0,0 +1,190 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.instance;
+
+import java.util.Collection;
+
+import javolution.util.FastList;
+
+import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
+import com.l2jserver.gameserver.model.L2Skill;
+import com.l2jserver.gameserver.model.L2SkillLearn;
+import com.l2jserver.gameserver.network.SystemMessageId;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
+import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
+import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
+import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
+
+/**
+ * @author Zoey76
+ */
+public final class L2TrainerHealersInstance extends L2TrainerInstance
+{
+	private static final int feeDeleteTransferSkills = 10000000;
+	
+	public L2TrainerHealersInstance(int objectId, L2NpcTemplate template)
+	{
+		super(objectId, template);
+		setInstanceType(InstanceType.L2TrainerHealersInstance);
+	}
+	
+	@Override
+	public String getHtmlPath(int npcId, int val)
+	{
+		String pom = "";
+		if (val == 0)
+		{
+			pom = "" + npcId;
+		}
+		else
+		{
+			pom = npcId + "-" + val;
+		}
+		
+		return "data/html/trainer/skilltransfer/" + pom + ".htm";
+	}
+	
+	@Override
+	public void onBypassFeedback(L2PcInstance player, String command)
+	{
+		NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
+		
+		if (command.equals("SkillTransfer_Learn"))
+		{
+			if (!getTemplate().canTeach(player.getClassId()))
+			{
+				showNoTeachHtml(player);
+				return;
+			}
+			else if ((player.getLevel() < 76) || (player.getClassId().level() < 3))
+			{
+				html.setFile(player.getHtmlPrefix(), "data/html/trainer/skilltransfer/learn-lowlevel.htm");
+				player.sendPacket(html);
+				return;
+			}
+			showTransferSkillList(player);
+		}
+		else if (command.equals("SkillTransfer_Cleanse"))
+		{
+			if (!getTemplate().canTeach(player.getClassId()))
+			{
+				showNoTeachHtml(player);
+				return;
+			}
+			else if ((player.getLevel() < 76) || (player.getClassId().level() < 3))
+			{
+				html.setFile(player.getHtmlPrefix(), "data/html/trainer/skilltransfer/cleanse-no.htm");
+				player.sendPacket(html);
+				return;
+			}
+			else if (player.getAdena() < feeDeleteTransferSkills)
+			{
+				player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_RESET_SKILL_LINK_BECAUSE_NOT_ENOUGH_ADENA));
+				return;
+			}
+			
+			boolean hasSkills = false;
+			if (!hasTransferSkillItems(player))
+			{
+				final Collection<L2SkillLearn> skills = SkillTreesData.getInstance().getTransferSkillTree(player.getClassId()).values();
+				
+				for (L2SkillLearn s : skills)
+				{
+					L2Skill sk = player.getKnownSkill(s.getSkillId());
+					if (sk != null)
+					{
+						player.removeSkill(sk);
+						if (s.getItemsIdCount() != null)
+						{
+							player.addItem("Cleanse", s.getItemsIdCount()[0][0], s.getItemsIdCount()[0][1], this, true);
+						}
+						else
+						{
+							_log.warning(L2TrainerHealersInstance.class.getSimpleName() + ": Transfer skill Id: " + s.getSkillId() + " doesn't have required items defined!");
+						}
+						hasSkills = true;
+					}
+				}
+				
+				//Adena gets reduced once.
+				if (hasSkills)
+				{
+					player.reduceAdena("Cleanse", feeDeleteTransferSkills, this, true);
+					return;
+				}
+			}
+			else
+			{
+				//Come back when you have used all transfer skill items for this class.
+				html.setFile(player.getHtmlPrefix(), "data/html/trainer/skilltransfer/cleanse-no_skills.htm");
+				player.sendPacket(html);
+			}
+		}
+		else
+		{
+			super.onBypassFeedback(player, command);
+		}
+	}
+	
+	/**
+	 * This displays Transfer Skill List to the player.
+	 * @param player the active character.
+	 */
+	public static void showTransferSkillList(L2PcInstance player)
+	{
+		final FastList<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailableTransferSkills(player);
+		final AcquireSkillList asl = new AcquireSkillList(SkillType.Transfer);
+		int count = 0;
+		
+		for (L2SkillLearn s : skills)
+		{
+			L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+			if (sk != null)
+			{
+				count++;
+				asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 0);
+			}
+		}
+		
+		if (count > 0)
+		{
+			player.sendPacket(asl);
+		}
+		else
+		{
+			player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN));
+		}
+	}
+	
+	private boolean hasTransferSkillItems(L2PcInstance player)
+	{
+		int itemId;
+		switch (player.getClassId())
+		{
+			case cardinal:
+				itemId = 15307;
+			case evaSaint:
+				itemId = 15308;
+			case shillienSaint:
+				itemId = 15309;
+			default:
+				itemId = -1;
+		}
+		
+		return (player.getInventory().getInventoryItemCount(itemId, -1) > 0);
+	}
+}

+ 2 - 10
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerInstance.java

@@ -16,16 +16,8 @@ package com.l2jserver.gameserver.model.actor.instance;
 
 import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
 
-/**
- * This class ...
- *
- * @version $Revision: 1.5.4.8 $ $Date: 2005/04/02 15:57:52 $
- */
-public final class L2TrainerInstance extends L2NpcInstance
+public class L2TrainerInstance extends L2NpcInstance
 {
-	/**
-	 * @param template
-	 */
 	public L2TrainerInstance(int objectId, L2NpcTemplate template)
 	{
 		super(objectId, template);
@@ -47,4 +39,4 @@ public final class L2TrainerInstance extends L2NpcInstance
 		
 		return "data/html/trainer/" + pom + ".htm";
 	}
-}
+}

+ 243 - 30
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2TransformManagerInstance.java

@@ -14,22 +14,43 @@
  */
 package com.l2jserver.gameserver.model.actor.instance;
 
+import javolution.util.FastList;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.datatables.MultiSell;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
+import com.l2jserver.gameserver.instancemanager.QuestManager;
+import com.l2jserver.gameserver.model.L2ItemInstance;
 import com.l2jserver.gameserver.model.L2Skill;
-import com.l2jserver.gameserver.model.L2TransformSkillLearn;
+import com.l2jserver.gameserver.model.L2SkillLearn;
+import com.l2jserver.gameserver.model.quest.QuestState;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList;
-import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
+import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
 
-public class L2TransformManagerInstance extends L2MerchantInstance
+/**
+ * @author Zoey76
+ */
+public final class L2TransformManagerInstance extends L2MerchantInstance
 {
-	/**
-	 * @param objectId
-	 * @param template
-	 */
+	private static final int feeDeleteSubClassSkills = 10000000;
+	
+	private static final String htmlFolder = "data/html/masterTransformation/";
+	
+	public static final String[] _questVarNames =
+	{
+		"EmergentAbility65-",
+		"EmergentAbility70-",
+		"ClassAbility75-",
+		"ClassAbility80-"
+	};
+	
+	public static final int[] _itemsIds = { 10280, 10281, 10282, 10283, 10284, 10285, 10286, 10287, 10288, 10289, 10290, 10291, 10292, 10293, 10294, 10612 };
+	
 	public L2TransformManagerInstance(int objectId, L2NpcTemplate template)
 	{
 		super(objectId, template);
@@ -39,53 +60,245 @@ public class L2TransformManagerInstance extends L2MerchantInstance
 	@Override
 	public String getHtmlPath(int npcId, int val)
 	{
-		String pom = "";
-		
-		if (val == 0)
-			pom = "" + npcId;
-		else
-			pom = npcId + "-" + val;
-		
-		return "data/html/default/" + pom + ".htm";
+		return htmlFolder + "master_transformation001.htm";
+	}
+	
+	@Override
+	public void onBypassFeedback(L2PcInstance player, String command)
+	{
+		if (command.startsWith("LearnTransformationSkill"))
+		{
+			if (canTransform(player))
+			{
+				L2TransformManagerInstance.showTransformSkillList(player);
+			}
+			else
+			{
+				NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
+				html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation003.htm");
+				player.sendPacket(html);
+			}
+			return;
+		}
+		else if (command.startsWith("BuyTransformationItems"))
+		{
+			if (canTransform(player))
+			{
+				MultiSell.getInstance().separateAndSend(32323001, player, this, false);
+			}
+			else
+			{
+				NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
+				html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation004.htm");
+				player.sendPacket(html);
+			}
+			return;
+		}
+		else if (command.startsWith("LearnSubClassSkill"))
+		{
+			if (player.isSubClassActive())
+			{
+				NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
+				html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation008.htm");
+				player.sendPacket(html);
+			}
+			else
+			{
+				boolean hasItems = false;
+				for (int i : _itemsIds)
+				{
+					if (player.getInventory().getItemByItemId(i) != null)
+					{
+						hasItems = true;
+						break;
+					}
+				}
+				if (hasItems)
+				{
+					showSubClassSkillList(player);
+				}
+				else
+				{
+					NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
+					html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation008.htm");
+					player.sendPacket(html);
+				}
+			}
+			return;
+		}
+		else if (command.startsWith("CancelCertification"))
+		{
+			NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
+			if (player.getSubClasses().size() == 0)
+			{
+				html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation007.htm");
+			}
+			else if (player.isSubClassActive())
+			{
+				html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation008.htm");
+			}
+			else if (player.getAdena() < feeDeleteSubClassSkills)
+			{
+				html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation008no.htm");
+			}
+			else
+			{
+				QuestState st = player.getQuestState("SubClassSkills");
+				if (st == null)
+				{
+					st = QuestManager.getInstance().getQuest("SubClassSkills").newQuestState(player);
+				}
+				
+				int activeCertifications = 0;
+				
+				for (String varName : _questVarNames)
+				{
+					for (int i = 1; i <= Config.MAX_SUBCLASS; i++)
+					{
+						String qvar = st.getGlobalQuestVar(varName + i);
+						if (!qvar.isEmpty() && (qvar.endsWith(";") || !qvar.equals("0")))
+						{
+							activeCertifications++;
+						}
+					}
+				}
+				if (activeCertifications == 0)
+				{
+					html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation010no.htm");
+				}
+				else
+				{
+					for (String varName : _questVarNames)
+					{
+						for (int i = 1; i <= Config.MAX_SUBCLASS; i++)
+						{
+							String qvarName = varName + i;
+							String qvar = st.getGlobalQuestVar(qvarName);
+							if (qvar.endsWith(";"))
+							{
+								int skillId = Integer.valueOf(qvar.replace(";", ""));
+								L2Skill sk = SkillTable.getInstance().getInfo(skillId, 1);
+								if (sk != null)
+								{
+									player.removeSkill(sk);
+									st.saveGlobalQuestVar(qvarName, "0");
+								}
+							}
+							else if (!qvar.isEmpty() && !qvar.equals("0"))
+							{
+								L2ItemInstance itemInstance = player.getInventory().getItemByObjectId(Integer.parseInt(qvar));
+								if (itemInstance != null)
+								{
+									player.destroyItem("SubClassSkills", Integer.parseInt(qvar), 1, player, false);
+								}
+								else
+								{
+									itemInstance = player.getWarehouse().getItemByObjectId(Integer.parseInt(qvar));
+									if (itemInstance != null)
+									{
+										_log.warning("Somehow " + player.getName() + " put a certification book into warehouse!");
+										player.getWarehouse().destroyItem("SubClassSkills", itemInstance, 1, player, false);
+									}
+									else
+									{
+										_log.warning("Somehow " + player.getName() + " deleted a certification book!");
+									}
+								}
+								st.saveGlobalQuestVar(varName + String.valueOf(i + 1), "0");
+							}
+						}
+					}
+					st.takeItems(57, 10000000);
+					html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation009no.htm");
+					player.sendSkillList();
+				}
+			}
+			player.sendPacket(html);
+			return;
+		}
+		super.onBypassFeedback(player, command);
 	}
 	
+	//Transformations:
 	/**
-	 * this displays TransformationSkillList to the player.
+	 * Returns true if the player meets the required conditions to learn a transformation.
 	 * @param player
+	 * @return boolean
+	 */
+	public static boolean canTransform(L2PcInstance player)
+	{
+		QuestState st = player.getQuestState("136_MoreThanMeetsTheEye");
+		if (Config.ALLOW_TRANSFORM_WITHOUT_QUEST || ((st != null) && st.isCompleted()))
+		{
+			return true;
+		}
+		return false;
+	}
+	
+	/**
+	 * This displays Transformation Skill List to the player.
+	 * @param player the active character.
 	 */
 	public static void showTransformSkillList(L2PcInstance player)
 	{
-		L2TransformSkillLearn[] skills = SkillTreeTable.getInstance().getAvailableTransformSkills(player);
-		AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.Usual);
+		final FastList<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailableTransformSkills(player);
+		final AcquireSkillList asl = new AcquireSkillList(SkillType.ClassTransform);
 		int counts = 0;
 		
-		for (L2TransformSkillLearn s: skills)
+		for (L2SkillLearn s : skills)
 		{
-			L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
-			if (sk == null)
-				continue;
-			
-			counts++;
-			
-			asl.addSkill(s.getId(), s.getLevel(), s.getLevel(), s.getSpCost(), 0);
+			L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+			if (sk != null)
+			{
+				counts++;
+				asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 0);
+			}
 		}
 		
 		if (counts == 0)
 		{
-			int minlevel = SkillTreeTable.getInstance().getMinLevelForNewTransformSkill(player);
+			int minlevel = SkillTreesData.getInstance().getMinLevelForNewSkill(player, SkillTreesData.getInstance().getTransformSkillTree());
 			if (minlevel > 0)
 			{
-				// No more skills to learn, come back when you level.
+				//No more skills to learn, come back when you level.
 				SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1);
 				sm.addNumber(minlevel);
 				player.sendPacket(sm);
 			}
 			else
+			{
 				player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN));
+			}
 		}
 		else
+		{
 			player.sendPacket(asl);
+		}
+	}
+	
+	//SubClass:
+	public static void showSubClassSkillList(L2PcInstance player)
+	{
+		final FastList<L2SkillLearn> subClassSkills = SkillTreesData.getInstance().getAvailableSubClassSkills(player);
+		final AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.SubClass);
+		int count = 0;
 		
-		player.sendPacket(ActionFailed.STATIC_PACKET);
+		for (L2SkillLearn s : subClassSkills)
+		{
+			L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+			if (sk != null)
+			{
+				count++;
+				asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), 0, 0);
+			}
+		}
+		if (count > 0)
+		{
+			player.sendPacket(asl);
+		}
+		else
+		{
+			player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN);
+		}
 	}
 }

+ 13 - 15
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterInstance.java

@@ -17,10 +17,12 @@ package com.l2jserver.gameserver.model.actor.instance;
 import java.util.Iterator;
 import java.util.Set;
 
+import javolution.util.FastList;
+
 import com.l2jserver.Config;
 import com.l2jserver.gameserver.datatables.CharTemplateTable;
 import com.l2jserver.gameserver.datatables.ClanTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.instancemanager.CastleManager;
 import com.l2jserver.gameserver.instancemanager.FortManager;
 import com.l2jserver.gameserver.instancemanager.FortSiegeManager;
@@ -28,7 +30,7 @@ import com.l2jserver.gameserver.instancemanager.SiegeManager;
 import com.l2jserver.gameserver.model.L2Clan;
 import com.l2jserver.gameserver.model.L2Clan.SubPledge;
 import com.l2jserver.gameserver.model.L2ClanMember;
-import com.l2jserver.gameserver.model.L2PledgeSkillLearn;
+import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.model.base.ClassId;
 import com.l2jserver.gameserver.model.base.PlayerClass;
 import com.l2jserver.gameserver.model.base.Race;
@@ -38,6 +40,7 @@ import com.l2jserver.gameserver.model.entity.Fort;
 import com.l2jserver.gameserver.model.quest.QuestState;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
 import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
 import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo;
 import com.l2jserver.gameserver.network.serverpackets.MagicSkillLaunched;
@@ -49,7 +52,6 @@ import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
 import com.l2jserver.gameserver.util.Util;
 import com.l2jserver.util.StringUtil;
 
-
 /**
  * This class ...
  *
@@ -539,8 +541,6 @@ public class L2VillageMasterInstance extends L2NpcInstance
 		}
 		else
 		{
-			// this class dont know any other commands, let forward
-			// the command to the parent class
 			super.onBypassFeedback(player, command);
 		}
 	}
@@ -561,7 +561,7 @@ public class L2VillageMasterInstance extends L2NpcInstance
 	
 	protected boolean checkQuests(L2PcInstance player)
 	{
-		// Noble players can add subbclasses without quests
+		// Noble players can add Sub-Classes without quests
 		if (player.isNoble())
 			return true;
 		
@@ -1068,19 +1068,16 @@ public class L2VillageMasterInstance extends L2NpcInstance
 			return;
 		}
 		
-		L2PledgeSkillLearn[] skills = SkillTreeTable.getInstance().getAvailablePledgeSkills(player);
-		AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.Clan);
+		final FastList<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailablePledgeSkills(player.getClan());
+		final AcquireSkillList asl = new AcquireSkillList(SkillType.Pledge);
 		int counts = 0;
 		
-		for (L2PledgeSkillLearn s: skills)
+		for (L2SkillLearn s: skills)
 		{
-			int cost = s.getRepCost();
-			int itemCount = s.getItemCount();
+			asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), s.getSocialClass());
 			counts++;
-			
-			asl.addSkill(s.getId(), s.getLevel(), s.getLevel(), cost, itemCount);
 		}
-		
+		//TODO: Missing some system message?
 		if (counts == 0)
 		{
 			if (player.getClan().getLevel() < 8)
@@ -1100,8 +1097,9 @@ public class L2VillageMasterInstance extends L2NpcInstance
 			}
 		}
 		else
+		{
 			player.sendPacket(asl);
-		
+		}
 		player.sendPacket(ActionFailed.STATIC_PACKET);
 	}
 }

+ 4 - 3
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/stat/PcStat.java

@@ -237,7 +237,9 @@ public class PcStat extends PlayableStat
 			L2ClassMasterInstance.showQuestionMark(getActiveChar());
 		}
 		
-		getActiveChar().rewardSkills(); // Give Expertise skill of this level
+		//Give AutoGet skills and all normal skills if Auto-Learn is activated.
+		getActiveChar().rewardSkills();
+		
 		if (getActiveChar().getClan() != null)
 		{
 			getActiveChar().getClan().updateClanMember(getActiveChar());
@@ -547,8 +549,7 @@ public class PcStat extends PlayableStat
 		
 		if (useRates)
 		{
-			byte level = getLevel();
-			if (level < 10)
+			if (getActiveChar().isLucky())
 				return;
 			
 			if (points < 0) // vitality consumed

+ 4 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/base/SubClass.java

@@ -80,6 +80,10 @@ public final class SubClass
 		return _level;
 	}
 	
+	/**
+	 * First Sub-Class is index 1.
+	 * @return int _classIndex
+	 */
 	public int getClassIndex()
 	{
 		return _classIndex;

+ 53 - 7
L2J_Server_BETA/java/com/l2jserver/gameserver/model/entity/Castle.java

@@ -36,7 +36,8 @@ import com.l2jserver.gameserver.SevenSigns;
 import com.l2jserver.gameserver.ThreadPoolManager;
 import com.l2jserver.gameserver.datatables.ClanTable;
 import com.l2jserver.gameserver.datatables.DoorTable;
-import com.l2jserver.gameserver.datatables.ResidentialSkillTable;
+import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.instancemanager.CastleManager;
 import com.l2jserver.gameserver.instancemanager.CastleManorManager;
 import com.l2jserver.gameserver.instancemanager.CastleManorManager.CropProcure;
@@ -49,6 +50,7 @@ import com.l2jserver.gameserver.model.L2Clan;
 import com.l2jserver.gameserver.model.L2Manor;
 import com.l2jserver.gameserver.model.L2Object;
 import com.l2jserver.gameserver.model.L2Skill;
+import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.model.actor.instance.L2ArtefactInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
@@ -261,7 +263,19 @@ public class Castle
 		load();
 		loadDoor();
 		_function = new FastMap<Integer, CastleFunction>();
-		_residentialSkills = ResidentialSkillTable.getInstance().getSkills(castleId);
+		final FastList<L2SkillLearn> residentialSkills = SkillTreesData.getInstance().getAvailableResidentialSkills(castleId);
+		for (L2SkillLearn s : residentialSkills)
+		{
+			final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+			if (sk != null)
+			{
+				_residentialSkills.add(sk);
+			}
+			else
+			{
+				_log.warning("Castle Id: " + castleId + " has a null residential skill Id: " + s.getSkillId() + " level: " + s.getSkillLevel() + "!");
+			}
+		}
 		if (getOwnerId() != 0)
 		{
 			loadFunctions();
@@ -1539,17 +1553,33 @@ public class Castle
 	
 	public void giveResidentialSkills(L2PcInstance player)
 	{
-		if (_residentialSkills != null && !_residentialSkills.isEmpty())
+		if ((_residentialSkills != null) && !_residentialSkills.isEmpty())
 		{
 			for (L2Skill sk : _residentialSkills)
+			{
 				player.addSkill(sk, false);
+			}
 		}
 		Territory territory = TerritoryWarManager.getInstance().getTerritory(getCastleId());
 		if (territory != null && territory.getOwnedWardIds().contains(getCastleId() + 80))
+		{
 			for(int wardId : territory.getOwnedWardIds())
-				if (ResidentialSkillTable.getInstance().getSkills(wardId) != null)
-					for (L2Skill sk : ResidentialSkillTable.getInstance().getSkills(wardId))
+			{
+				final FastList<L2SkillLearn> territorySkills = SkillTreesData.getInstance().getAvailableResidentialSkills(wardId);
+				for (L2SkillLearn s : territorySkills)
+				{
+					final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+					if (sk != null)
+					{
 						player.addSkill(sk, false);
+					}
+					else
+					{
+						_log.warning("Trying to add a null skill for Territory Ward Id: " + wardId + ", skill Id: " + s.getSkillId() + " level: " + s.getSkillLevel() + "!");
+					}
+				}
+			}
+		}
 	}
 	
 	public void removeResidentialSkills(L2PcInstance player)
@@ -1557,13 +1587,29 @@ public class Castle
 		if (_residentialSkills != null && !_residentialSkills.isEmpty())
 		{
 			for (L2Skill sk : _residentialSkills)
+			{
 				player.removeSkill(sk, false, true);
+			}
 		}
 		if (TerritoryWarManager.getInstance().getTerritory(getCastleId()) != null)
+		{
 			for(int wardId : TerritoryWarManager.getInstance().getTerritory(getCastleId()).getOwnedWardIds())
-				if (ResidentialSkillTable.getInstance().getSkills(wardId) != null)
-					for (L2Skill sk : ResidentialSkillTable.getInstance().getSkills(wardId))
+			{
+				final FastList<L2SkillLearn> territorySkills = SkillTreesData.getInstance().getAvailableResidentialSkills(wardId);
+				for (L2SkillLearn s : territorySkills)
+				{
+					final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+					if (sk != null)
+					{
 						player.removeSkill(sk, false, true);
+					}
+					else
+					{
+						_log.warning("Trying to remove a null skill for Territory Ward Id: " + wardId + ", skill Id: " + s.getSkillId() + " level: " + s.getSkillLevel() + "!");
+					}
+				}
+			}
+		}
 	}
 	
 	/**

+ 16 - 3
L2J_Server_BETA/java/com/l2jserver/gameserver/model/entity/Fort.java

@@ -38,7 +38,8 @@ import com.l2jserver.gameserver.ThreadPoolManager;
 import com.l2jserver.gameserver.datatables.ClanTable;
 import com.l2jserver.gameserver.datatables.DoorTable;
 import com.l2jserver.gameserver.datatables.NpcTable;
-import com.l2jserver.gameserver.datatables.ResidentialSkillTable;
+import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.datatables.SpawnTable;
 import com.l2jserver.gameserver.datatables.StaticObjects;
 import com.l2jserver.gameserver.instancemanager.FortManager;
@@ -46,6 +47,7 @@ import com.l2jserver.gameserver.instancemanager.ZoneManager;
 import com.l2jserver.gameserver.model.L2Clan;
 import com.l2jserver.gameserver.model.L2Object;
 import com.l2jserver.gameserver.model.L2Skill;
+import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.model.L2Spawn;
 import com.l2jserver.gameserver.model.L2World;
 import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
@@ -59,7 +61,6 @@ import com.l2jserver.gameserver.network.serverpackets.PledgeShowInfoUpdate;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
 
-
 public class Fort
 {
 	protected static final Logger _log = Logger.getLogger(Fort.class.getName());
@@ -251,7 +252,19 @@ public class Fort
 		loadDoor();
 		loadFlagPoles();
 		_function = new FastMap<Integer, FortFunction>();
-		_residentialSkills = ResidentialSkillTable.getInstance().getSkills(fortId);
+		FastList<L2SkillLearn> residentialSkills = SkillTreesData.getInstance().getAvailableResidentialSkills(fortId);
+		for (L2SkillLearn s : residentialSkills)
+		{
+			L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+			if (sk != null)
+			{
+				_residentialSkills.add(sk);
+			}
+			else
+			{
+				_log.warning("Fort Id: " + fortId + " has a null residential skill Id: " + s.getSkillId() + " level: " + s.getSkillLevel() + "!");
+			}
+		}
 		if (getOwnerClan() != null)
 		{
 			setVisibleFlag(true);

+ 6 - 6
L2J_Server_BETA/java/com/l2jserver/gameserver/network/SystemMessageId.java

@@ -13000,9 +13000,9 @@ public final class SystemMessageId
 	
 	/**
 	 * ID: 2211<br>
-	 * Message: You have not acquired a good deed skill so you cannot acquire new skills.
+	 * Message: You must learn the Onyx Beast skill before you can acquire further skills.
 	 */
-	public static final SystemMessageId NOT_ACQUIRED_DEED_SKILL_CANNOT_ACQUIRE_SKILLS;
+	public static final SystemMessageId YOU_MUST_LEARN_ONYX_BEAST_SKILL;
 	
 	/**
 	 * ID: 2212<br>
@@ -13048,9 +13048,9 @@ public final class SystemMessageId
 	
 	/**
 	 * ID: 2219<br>
-	 * Message: This lower clan skill has already been acquired.
+	 * Message: This squad skill has already been acquired.
 	 */
-	public static final SystemMessageId LOWER_CLAN_SKILL_ALREADY_ACQUIRED;
+	public static final SystemMessageId SQUAD_SKILL_ALREADY_ACQUIRED;
 	
 	/**
 	 * ID: 2220<br>
@@ -16888,7 +16888,7 @@ public final class SystemMessageId
 		YOU_DONT_MEET_SKILL_LEVEL_REQUIREMENTS = new SystemMessageId(2208);
 		AREA_WHERE_RADAR_CANNOT_BE_USED = new SystemMessageId(2209);
 		RETURN_TO_UNENCHANTED_CONDITION = new SystemMessageId(2210);
-		NOT_ACQUIRED_DEED_SKILL_CANNOT_ACQUIRE_SKILLS = new SystemMessageId(2211);
+		YOU_MUST_LEARN_ONYX_BEAST_SKILL = new SystemMessageId(2211);
 		NOT_COMPLETED_QUEST_FOR_SKILL_ACQUISITION = new SystemMessageId(2212);
 		CANT_BOARD_SHIP_POLYMORPHED = new SystemMessageId(2213);
 		CONFIRM_CHARACTER_CREATION = new SystemMessageId(2214);
@@ -16896,7 +16896,7 @@ public final class SystemMessageId
 		PLEASE_UPDATE_CPU_DRIVER = new SystemMessageId(2216);
 		BALLISTA_DESTROYED_CLAN_REPU_INCREASED = new SystemMessageId(2217);
 		MAIN_CLASS_SKILL_ONLY = new SystemMessageId(2218);
-		LOWER_CLAN_SKILL_ALREADY_ACQUIRED = new SystemMessageId(2219);
+		SQUAD_SKILL_ALREADY_ACQUIRED = new SystemMessageId(2219);
 		PREVIOUS_LEVEL_SKILL_NOT_LEARNED = new SystemMessageId(2220);
 		ACTIVATE_SELECTED_FUNTIONS_CONFIRM = new SystemMessageId(2221);
 		SCOUT_COSTS_150000_ADENA = new SystemMessageId(2222);

+ 9 - 15
L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/CharacterCreate.java

@@ -25,7 +25,7 @@ import com.l2jserver.Config;
 import com.l2jserver.gameserver.datatables.CharNameTable;
 import com.l2jserver.gameserver.datatables.CharTemplateTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.idfactory.IdFactory;
 import com.l2jserver.gameserver.instancemanager.QuestManager;
 import com.l2jserver.gameserver.model.L2ItemInstance;
@@ -45,7 +45,6 @@ import com.l2jserver.gameserver.templates.chars.L2PcTemplate;
 import com.l2jserver.gameserver.templates.chars.L2PcTemplate.PcTemplateItem;
 import com.l2jserver.gameserver.util.Util;
 
-
 @SuppressWarnings("unused")
 public final class CharacterCreate extends L2GameClientPacket
 {
@@ -282,21 +281,21 @@ public final class CharacterCreate extends L2GameClientPacket
 			}
 		}
 		
-		for (L2SkillLearn skill : SkillTreeTable.getInstance().getAvailableSkills(newChar, newChar.getClassId()))
+		for (L2SkillLearn skill : SkillTreesData.getInstance().getAvailableSkills(newChar, newChar.getClassId(), false, true))
 		{
-			newChar.addSkill(SkillTable.getInstance().getInfo(skill.getId(), skill.getLevel()), true);
-			if (skill.getId() == 1001 || skill.getId() == 1177)
+			newChar.addSkill(SkillTable.getInstance().getInfo(skill.getSkillId(), skill.getSkillLevel()), true);
+			if (skill.getSkillId() == 1001 || skill.getSkillId() == 1177)
 			{
-				shortcut = new L2ShortCut(1, 0, 2, skill.getId(), skill.getLevel(), 1);
+				shortcut = new L2ShortCut(1, 0, 2, skill.getSkillId(), skill.getSkillLevel(), 1);
 				newChar.registerShortCut(shortcut);
 			}
-			if (skill.getId() == 1216)
+			if (skill.getSkillId() == 1216)
 			{
-				shortcut = new L2ShortCut(10, 0, 2, skill.getId(), skill.getLevel(), 1);
+				shortcut = new L2ShortCut(10, 0, 2, skill.getSkillId(), skill.getSkillLevel(), 1);
 				newChar.registerShortCut(shortcut);
 			}
 			if (Config.DEBUG)
-				_log.fine("Adding starter skill:" + skill.getId() + " / " + skill.getLevel());
+				_log.fine("Adding starter skill:" + skill.getSkillId() + " / " + skill.getSkillLevel());
 		}
 		
 		if (!Config.DISABLE_TUTORIAL)
@@ -323,14 +322,9 @@ public final class CharacterCreate extends L2GameClientPacket
 			q.newQuestState(player).setState(State.STARTED);
 	}
 	
-	/*
-	 * (non-Javadoc)
-	 * 
-	 * @see com.l2jserver.gameserver.clientpackets.ClientBasePacket#getType()
-	 */
 	@Override
 	public String getType()
 	{
 		return _C__0B_CHARACTERCREATE;
 	}
-}
+}

+ 354 - 353
L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestAcquireSkill.java

@@ -17,26 +17,26 @@ package com.l2jserver.gameserver.network.clientpackets;
 import java.util.logging.Logger;
 
 import com.l2jserver.Config;
-import com.l2jserver.gameserver.datatables.SkillSpellbookTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree;
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree.SubUnitSkill;
-import com.l2jserver.gameserver.model.L2PledgeSkillLearn;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
+import com.l2jserver.gameserver.instancemanager.QuestManager;
+import com.l2jserver.gameserver.model.L2Clan;
+import com.l2jserver.gameserver.model.L2ItemInstance;
 import com.l2jserver.gameserver.model.L2ShortCut;
 import com.l2jserver.gameserver.model.L2Skill;
 import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.model.L2SquadTrainer;
-import com.l2jserver.gameserver.model.L2TransformSkillLearn;
 import com.l2jserver.gameserver.model.actor.L2Npc;
 import com.l2jserver.gameserver.model.actor.instance.L2FishermanInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2TrainerHealersInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2TransformManagerInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2VillageMasterInstance;
-import com.l2jserver.gameserver.model.quest.Quest;
+import com.l2jserver.gameserver.model.quest.QuestState;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.AcquireSkillDone;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
 import com.l2jserver.gameserver.network.serverpackets.ExStorageMaxCount;
 import com.l2jserver.gameserver.network.serverpackets.PledgeSkillList;
 import com.l2jserver.gameserver.network.serverpackets.ShortCutRegister;
@@ -45,20 +45,18 @@ import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.util.Util;
 
 /**
- * This class ...
- *
- * @version $Revision: 1.7.2.1.2.4 $ $Date: 2005/03/27 15:29:30 $
+ * @author Zoey76
  */
-public class RequestAcquireSkill extends L2GameClientPacket
+public final class RequestAcquireSkill extends L2GameClientPacket
 {
-	private static final String _C__6C_REQUESTAQUIRESKILL = "[C] 7C RequestAcquireSkill";
+	private static final String _C__7C_REQUESTACQUIRESKILL = "[C] 7C RequestAcquireSkill";
 	
 	private static Logger _log = Logger.getLogger(RequestAcquireSkill.class.getName());
 	
 	private int _id;
 	private int _level;
 	private int _skillType;
-	private int subType;
+	private int _subType;
 	
 	@Override
 	protected void readImpl()
@@ -66,449 +64,447 @@ public class RequestAcquireSkill extends L2GameClientPacket
 		_id = readD();
 		_level = readD();
 		_skillType = readD();
-		if (_skillType == 3)
-			subType = readD();
+		if (_skillType == SkillType.SubPledge.ordinal())
+		{
+			_subType = readD();
+		}
 	}
 	
 	@Override
 	protected void runImpl()
 	{
-		final L2PcInstance player = getClient().getActiveChar();
-		if (player == null)
+		final L2PcInstance activeChar = getClient().getActiveChar();
+		if (activeChar == null)
+		{
 			return;
+		}
 		
-		if (_level < 1 || _level > 1000 || _id < 1 || _id > 32000)
+		if ((_level < 1) || (_level > 1000) || (_id < 1) || (_id > 32000))
 		{
-			Util.handleIllegalPlayerAction(player, "Wrong Packet Data in Aquired Skill", Config.DEFAULT_PUNISH);
-			_log.warning("Recived Wrong Packet Data in Aquired Skill - id: " + _id + " level: " + _level + " for "+player);
+			Util.handleIllegalPlayerAction(activeChar, "Wrong Packet Data in Aquired Skill", Config.DEFAULT_PUNISH);
+			_log.warning("Recived Wrong Packet Data in Aquired Skill - id: " + _id + " level: " + _level + " for " + activeChar);
 			return;
 		}
 		
-		final L2Npc trainer = player.getLastFolkNPC();
+		final L2Npc trainer = activeChar.getLastFolkNPC();
+		
 		if (!(trainer instanceof L2NpcInstance))
+		{
 			return;
+		}
 		
-		if (!trainer.canInteract(player) && !player.isGM())
+		if (!trainer.canInteract(activeChar) && !activeChar.isGM())
+		{
 			return;
+		}
 		
-		if (!Config.ALT_GAME_SKILL_LEARN)
-			player.setSkillLearningClassId(player.getClassId());
+		final SkillType skillType = SkillType.values()[_skillType];
 		
-		/**
-		 *  If current skill lvl + 1 is not equal to the skill lvl you wanna learn (eg: You have Aggression lvl 3 and the packet sends info that
-		 *  you want to learn Aggression lvl 5, thus skipping lvl 4.) or the packet sends the same level or lower (eg: Aggression lvl 3 and the
-		 *  packet sends info that you want to learn Aggression level 3).
-		 */
-		if (Math.max(player.getSkillLevel(_id), 0) + 1 != _level && !(_skillType == 3 || _skillType == 4))
+		if ((activeChar.getSkillLevel(_id) >= _level) && (skillType != SkillType.SubPledge))
+		{
+			//Already knows the skill with this level
 			return;
+		}
 		
 		final L2Skill skill = SkillTable.getInstance().getInfo(_id, _level);
 		
-		int counts = 0;
-		int requiredSp = 10000000;
+		if (skill == null)
+		{
+			return;
+		}
 		
-		switch (_skillType)
+		//Hack check. Doesn't apply to all Skill Types
+		if (((skillType != SkillType.Transfer) && ((_level > 1) && (activeChar.getKnownSkill(_id) == null))) || ((activeChar.getKnownSkill(_id) != null) && (activeChar.getKnownSkill(_id).getLevel() != (_level - 1))))
 		{
-			case 0:
+			//The previous level skill has not been learned.
+			activeChar.sendPacket(SystemMessageId.PREVIOUS_LEVEL_SKILL_NOT_LEARNED);
+			Util.handleIllegalPlayerAction(activeChar, "Player " + activeChar.getName() + " is requesting skill Id: " + _id + " level " + _level + " without knowing it's previous level!", 0);
+			return;
+		}
+		
+		switch (skillType)
+		{
+			case ClassTransform:
 			{
-				if (trainer instanceof L2TransformManagerInstance) // transform skills
+				//If players is learning transformations:
+				if (trainer instanceof L2TransformManagerInstance)
 				{
-					int costId = 0;
-					
-					// Skill Learn bug Fix
-					L2TransformSkillLearn[] skillst = SkillTreeTable.getInstance().getAvailableTransformSkills(player);
-					for (L2TransformSkillLearn s : skillst)
+					//Hack check.
+					if (L2TransformManagerInstance.canTransform(activeChar))
 					{
-						L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
-						if (sk == null || sk != skill)
-							continue;
-						
-						counts++;
-						costId = s.getItemId();
-						requiredSp = s.getSpCost();
+						activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_COMPLETED_QUEST_FOR_SKILL_ACQUISITION));
+						Util.handleIllegalPlayerAction(activeChar, "Player " + activeChar.getName() + " is requesting skill Id: " + _id + " level " + _level + " without required quests!", 0);
+						return;
 					}
 					
-					if (counts == 0)
+					final L2SkillLearn s = SkillTreesData.getInstance().getTransformSkill(_id, _level);
+					
+					//Required skills:
+					if ((s.getPreReqSkillIdLvl() != null) && (activeChar.getKnownSkill(s.getPreReqSkillIdLvl()[0]) == null))
 					{
-						player.sendMessage("You are trying to learn skill that u can't..");
-						Util.handleIllegalPlayerAction(player, "Player " + player.getName() + " tried to learn skill that he can't!!!", Config.DEFAULT_PUNISH);
+						activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_MUST_LEARN_ONYX_BEAST_SKILL));
 						return;
 					}
 					
-					if (player.getSp() >= requiredSp)
+					if (checkPlayerSkill(activeChar, trainer, s))
 					{
-						if (!player.destroyItemByItemId("Consume", costId, 1, trainer, false))
-						{
-							// Haven't spellbook
-							player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
-							showSkillList(trainer, player);
-							return;
-						}
-						
-						SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
-						sm.addItemName(costId);
-						player.sendPacket(sm);
-					}
-					else
-					{
-						player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_SP_TO_LEARN_SKILL));
-						showSkillList(trainer, player);
-						return;
+						giveSkill(activeChar, trainer, 0, skill);
 					}
 					break;
 				}
-				
-				// normal skills
-				L2SkillLearn[] skills = SkillTreeTable.getInstance().getAvailableSkills(player, player.getSkillLearningClassId());
-				for (L2SkillLearn s : skills)
-				{
-					if (!s.isLearnedByNPC())
-						continue;
-					
-					L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
-					if (sk == null || sk != skill)
-						continue;
-					
-					counts++;
-					requiredSp = SkillTreeTable.getInstance().getSkillCost(player,skill);
-				}
-				
-				if (counts == 0 && !Config.ALT_GAME_SKILL_LEARN)
-				{
-					player.sendMessage("You are trying to learn skill that u can't..");
-					Util.handleIllegalPlayerAction(player, "Player " + player.getName() + " tried to learn skill that he can't!!!", Config.DEFAULT_PUNISH);
-					return;
-				}
-				
-				if (player.getSp() >= requiredSp)
+				else
 				{
-					int spbId = -1;
-					
-					// divine inspiration require book for each level
-					if (Config.DIVINE_SP_BOOK_NEEDED && skill.getId() == L2Skill.SKILL_DIVINE_INSPIRATION)
-						spbId = SkillSpellbookTable.getInstance().getBookForSkill(skill, _level);
-					else if (Config.SP_BOOK_NEEDED && skill.getLevel() == 1)
-						spbId = SkillSpellbookTable.getInstance().getBookForSkill(skill);
-					
-					// spellbook required
-					if (spbId > -1)
+					final L2SkillLearn s = SkillTreesData.getInstance().getClassSkill(_id, _level, activeChar.getClassId());
+					if (s != null)
 					{
-						if (!player.destroyItemByItemId("Consume", spbId, 1, trainer, false))
+						int levelUpSp = s.getLevelUpSp();
+						
+						if (activeChar.getSp() >= levelUpSp)
 						{
-							// Haven't spellbook
-							player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
-							showSkillList(trainer, player);
+							if (checkPlayerSkill(activeChar, trainer, s))
+							{
+								giveSkill(activeChar, trainer, levelUpSp, skill);
+							}
+						}
+						else
+						{
+							activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_SP_TO_LEARN_SKILL));
+							showSkillList(trainer, activeChar);
 							return;
 						}
-						
-						SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
-						sm.addItemName(spbId);
-						player.sendPacket(sm);
 					}
 				}
-				else
-				{
-					player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_SP_TO_LEARN_SKILL));
-					showSkillList(trainer, player);
-					return;
-				}
 				break;
 			}
-			case 1:
+			case Fishing:
 			{
-				int costId = 0;
-				int costCount = 0;
-				
-				// Skill Learn bug Fix
-				L2SkillLearn[] skillsc = SkillTreeTable.getInstance().getAvailableSkills(player);
-				for (L2SkillLearn s : skillsc)
+				final L2SkillLearn s = SkillTreesData.getInstance().getFishingSkill(_id, _level);
+				if (checkPlayerSkill(activeChar, trainer, s))
 				{
-					if (!s.isLearnedByNPC())
-						continue;
-					
-					L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
-					if (sk == null || sk != skill)
-						continue;
-					
-					counts++;
-					costId = s.getIdCost();
-					costCount = s.getCostCount();
-					requiredSp = s.getSpCost();
-				}
-				
-				if (counts == 0)
-				{
-					//player.sendMessage("You are trying to learn skill that u can't..");
-					Util.handleIllegalPlayerAction(player, "Player " + player.getName() + " tried to learn skill that he can't!!!", Config.DEFAULT_PUNISH);
-					return;
-				}
-				
-				if (player.getSp() >= requiredSp)
-				{
-					if (!player.destroyItemByItemId("Consume", costId, costCount, trainer, false))
-					{
-						// Haven't spellbook
-						player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
-						showSkillList(trainer, player);
-						return;
-					}
-					
-					SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
-					sm.addItemName(costId);
-					sm.addItemNumber(costCount);
-					player.sendPacket(sm);
-				}
-				else
-				{
-					player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_SP_TO_LEARN_SKILL));
-					showSkillList(trainer, player);
-					return;
+					giveSkill(activeChar, trainer, 0, skill);
 				}
 				break;
 			}
-			case 2:
+			case Pledge:
 			{
-				int itemId = 0;
-				int itemCount = 0;
-				int repCost = 100000000;
-				
-				// Skill Learn bug Fix
-				L2PledgeSkillLearn[] skills = SkillTreeTable.getInstance().getAvailablePledgeSkills(player);
-				for (L2PledgeSkillLearn s : skills)
-				{
-					L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
-					if (sk == null || sk != skill)
-						continue;
-					
-					counts++;
-					itemId = s.getItemId();
-					itemCount = s.getItemCount();
-					repCost = s.getRepCost();
-					break;
-				}
-				
-				if (counts == 0)
+				if (!activeChar.isClanLeader())
 				{
-					//player.sendMessage("You are trying to learn skill that u can't..");
-					Util.handleIllegalPlayerAction(player, "Player " + player + " tried to learn clan skill that he can't!!!", Config.DEFAULT_PUNISH);
 					return;
 				}
 				
-				if (player.getClan().getReputationScore() >= repCost)
+				final L2Clan clan = activeChar.getClan();
+				
+				int itemId = -1;
+				int itemCount = -1;
+				int repCost = 100000000;
+				
+				final L2SkillLearn s = SkillTreesData.getInstance().getPledgeSkill(_id, _level);
+				if (s != null)
 				{
-					if (Config.LIFE_CRYSTAL_NEEDED)
+					repCost = s.getLevelUpSp();
+					
+					if (clan.getReputationScore() >= repCost)
 					{
-						if (!player.destroyItemByItemId("Consume", itemId, itemCount, trainer, false))
+						if (Config.LIFE_CRYSTAL_NEEDED && (s.getItemsIdCount() != null))
 						{
-							// Haven't spellbook
-							player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
-							L2VillageMasterInstance.showPledgeSkillList(player);
-							return;
+							for (int[] itemIdCount : s.getItemsIdCount())
+							{
+								itemId = itemIdCount[0];
+								itemCount = itemIdCount[1];
+								
+								if ((itemId > 0) && (itemCount > 0))
+								{
+									if (!activeChar.destroyItemByItemId("Consume", itemId, itemCount, trainer, false))
+									{
+										//Doesn't have required item.
+										activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
+										L2VillageMasterInstance.showPledgeSkillList(activeChar);
+										return;
+									}
+									
+									final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
+									sm.addItemName(itemId);
+									sm.addItemNumber(itemCount);
+									activeChar.sendPacket(sm);
+								}
+							}
 						}
 						
-						SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
-						sm.addItemName(itemId);
-						sm.addItemNumber(itemCount);
-						player.sendPacket(sm);
+						clan.takeReputationScore(repCost, true);
+						
+						final SystemMessage cr = SystemMessage.getSystemMessage(SystemMessageId.S1_DEDUCTED_FROM_CLAN_REP);
+						cr.addNumber(repCost);
+						activeChar.sendPacket(cr);
+						
+						clan.addNewSkill(skill);
+						
+						clan.broadcastToOnlineMembers(new PledgeSkillList(clan));
+						
+						activeChar.sendPacket(new AcquireSkillDone());
+						
+						L2VillageMasterInstance.showPledgeSkillList(activeChar);
 					}
+					else
+					{
+						activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ACQUIRE_SKILL_FAILED_BAD_CLAN_REP_SCORE));
+						L2VillageMasterInstance.showPledgeSkillList(activeChar);
+					}
+					return;
 				}
-				else
+			}
+			case SubPledge:
+			{
+				if (!activeChar.isClanLeader())
 				{
-					player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ACQUIRE_SKILL_FAILED_BAD_CLAN_REP_SCORE));
-					L2VillageMasterInstance.showPledgeSkillList(player);
 					return;
 				}
 				
-				player.getClan().takeReputationScore(repCost, true);
-				player.getClan().addNewSkill(skill);
-				
-				if (Config.DEBUG)
-					_log.fine("Learned pledge skill " + _id + " for " + requiredSp + " SP.");
-				
-				SystemMessage cr = SystemMessage.getSystemMessage(SystemMessageId.S1_DEDUCTED_FROM_CLAN_REP);
-				cr.addNumber(repCost);
-				player.sendPacket(cr);
+				final L2Clan clan = activeChar.getClan();
 				
-				player.sendPacket(new AcquireSkillDone());
-				
-				player.getClan().broadcastToOnlineMembers(new PledgeSkillList(player.getClan()));
-				
-				L2VillageMasterInstance.showPledgeSkillList(player); //Maybe we should add a check here...
-				return;
-			}
-			case 3:
-			{
-				if (!player.isClanLeader())
-					return;
-				if (player.getClan().getHasFort() == 0 && player.getClan().getHasCastle() == 0)
+				if ((clan.getHasFort() == 0) && (clan.getHasCastle() == 0))
+				{
 					return;
+				}
+				
 				if (trainer instanceof L2SquadTrainer)
 				{
-					int id = 0;
-					int count = 0;
+					int itemId = -1;
+					int itemCount = -1;
 					int rep = 100000000;
-					boolean found = false;
-					for (SubUnitSkill sus: SubPledgeSkillTree.getInstance().getAvailableSkills(player.getClan()))
+					
+					final L2SkillLearn s = SkillTreesData.getInstance().getSubPledgeSkill(_id, _level);
+					if (s != null)
 					{
-						if (sus.getSkill() == skill)
+						//Hack check. Check if SubPledge can accept the new skill:
+						if (!clan.isLearnableSubPledgeSkill(skill, _subType))
+						{
+							activeChar.sendPacket(SystemMessageId.SQUAD_SKILL_ALREADY_ACQUIRED);
+							Util.handleIllegalPlayerAction(activeChar, "Player " + activeChar.getName() + " is requesting skill Id: " + _id + " level " + _level + " without knowing it's previous level!", 0);
+							return;
+						}
+						
+						rep = s.getLevelUpSp();
+						
+						if (clan.getReputationScore() < rep)
 						{
-							id = sus.getItemId();
-							count = sus.getCount();
-							rep = sus.getReputation();
-							found = true;
-							break;
+							activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ACQUIRE_SKILL_FAILED_BAD_CLAN_REP_SCORE));
+							return;
 						}
-					}
-					
-					// skill not available for clan !?
-					if (!found)
-						return;
-					
-					// check if subunit can accept skill
-					if (!player.getClan().isLearnableSubSkill(skill, subType))
-						return;
-					
-					if (player.getClan().getReputationScore() < rep)
-					{
-						player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ACQUIRE_SKILL_FAILED_BAD_CLAN_REP_SCORE));
-						return;
-					}
-					
-					if (!player.destroyItemByItemId("SubSkills", id, count, trainer, false))
-					{
-						player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
+						
+						for (int[] itemIdCount : s.getItemsIdCount())
+						{
+							itemId = itemIdCount[0];
+							itemCount = itemIdCount[1];
+							
+							if (!activeChar.destroyItemByItemId("SubSkills", itemId, itemCount, trainer, false))
+							{
+								activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
+								return;
+							}
+							else
+							{
+								final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
+								sm.addItemName(itemId);
+								sm.addItemNumber(itemCount);
+								activeChar.sendPacket(sm);
+							}
+						}
+						
+						if (rep > 0)
+						{
+							clan.takeReputationScore(rep, true);
+							final SystemMessage cr = SystemMessage.getSystemMessage(SystemMessageId.S1_DEDUCTED_FROM_CLAN_REP);
+							cr.addNumber(rep);
+							activeChar.sendPacket(cr);
+						}
+						
+						clan.addNewSkill(skill, _subType);
+						
+						clan.broadcastToOnlineMembers(new PledgeSkillList(clan));
+						
+						activeChar.sendPacket(new AcquireSkillDone());
+						
+						((L2SquadTrainer) trainer).showSubUnitSkillList(activeChar);
 						return;
 					}
-					else
-					{
-						SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
-						sm.addItemName(id);
-						sm.addItemNumber(count);
-						player.sendPacket(sm);
-					}
-					
-					player.getClan().takeReputationScore(rep, true);
-					SystemMessage cr = SystemMessage.getSystemMessage(SystemMessageId.S1_DEDUCTED_FROM_CLAN_REP);
-					cr.addNumber(rep);
-					player.sendPacket(cr);
-					
-					if (subType > -2)
-						player.getClan().addNewSkill(skill, subType);
-					
-					player.getClan().broadcastToOnlineMembers(new PledgeSkillList(player.getClan()));
-					
-					((L2SquadTrainer) trainer).showSubUnitSkillList(player);
 				}
 				break;
 			}
-			case 4:
-			case 5:
+			case Transfer:
 			{
-				requiredSp = 0;
-				Quest[] qlst = trainer.getTemplate().getEventQuests(Quest.QuestEventType.ON_SKILL_LEARN);
-				if ((qlst != null) && qlst.length == 1)
+				final L2SkillLearn s = SkillTreesData.getInstance().getTransferSkill(_id, _level, activeChar.getClassId());
+				if (checkPlayerSkill(activeChar, trainer, s))
 				{
-					if (!qlst[0].notifyAcquireSkill(trainer, player, skill))
-					{
-						qlst[0].notifyAcquireSkillList(trainer, player);
-						return;
-					}
+					giveSkill(activeChar, trainer, 0, skill);
 				}
-				else
-					return;
 				break;
 			}
-			case 6:
+			case SubClass:
 			{
-				int costId = 0;
-				int costCount = 0;
-				
-				// Skill Learn bug Fix
-				L2SkillLearn[] skillsc = SkillTreeTable.getInstance().getAvailableSpecialSkills(player);
-				for (L2SkillLearn s : skillsc)
+				//Hack check.
+				if (activeChar.isSubClassActive())
 				{
-					L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
-					if (sk == null || sk != skill)
-						continue;
-					
-					counts++;
-					costId = s.getIdCost();
-					costCount = s.getCostCount();
-					requiredSp = s.getSpCost();
+					activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SKILL_NOT_FOR_SUBCLASS));
+					Util.handleIllegalPlayerAction(activeChar, "Player " + activeChar.getName() + " is requesting skill Id: " + _id + " level " + _level + " while Sub-Class is active!", 0);
+					return;
 				}
 				
-				if (counts == 0)
+				final L2SkillLearn s = SkillTreesData.getInstance().getSubClassSkill(_id, _level);
+				
+				QuestState st = activeChar.getQuestState("SubClassSkills");
+				if (st == null)
 				{
-					player.sendMessage("You are trying to learn skill that u can't..");
-					Util.handleIllegalPlayerAction(player, "Player " + player.getName() + " tried to learn skill that he can't!!!", Config.DEFAULT_PUNISH);
-					return;
+					st = QuestManager.getInstance().getQuest("SubClassSkills").newQuestState(activeChar);
 				}
 				
-				if (player.getSp() >= requiredSp)
+				for (String varName : L2TransformManagerInstance._questVarNames)
 				{
-					if (!player.destroyItemByItemId("Consume", costId, costCount, trainer, false))
+					for (int i = 1; i <= Config.MAX_SUBCLASS; i++)
 					{
-						// Haven't spellbook
-						player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
-						showSkillList(trainer, player);
-						return;
+						String qvar = st.getGlobalQuestVar(varName + i);
+						if (!qvar.isEmpty() && !qvar.endsWith(";") && !qvar.equals("0"))
+						{
+							int itemObjId = Integer.parseInt(qvar);
+							L2ItemInstance Item = activeChar.getInventory().getItemByObjectId(itemObjId);
+							if ((Item != null) && Util.contains(L2TransformManagerInstance._itemsIds, Item.getItemId()))
+							{
+								if (checkPlayerSkill(activeChar, trainer, s))
+								{
+									giveSkill(activeChar, trainer, 0, skill);
+									//Logging the given skill.
+									st.saveGlobalQuestVar(varName + i, skill.getId() + ";");
+								}
+								return;
+							}
+						}
 					}
-					
-					SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
-					sm.addItemName(costId);
-					sm.addItemNumber(costCount);
-					player.sendPacket(sm);
 				}
-				else
+				break;
+			}
+			case Collect:
+			{
+				final L2SkillLearn s = SkillTreesData.getInstance().getCollectSkill(_id, _level);
+				if (checkPlayerSkill(activeChar, trainer, s))
 				{
-					player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_SP_TO_LEARN_SKILL));
-					showSkillList(trainer, player);
-					return;
+					giveSkill(activeChar, trainer, 0, skill);
 				}
 				break;
 			}
 			default:
 			{
-				_log.warning("Recived Wrong Packet Data in Aquired Skill - unk1:" + _skillType);
-				return;
+				_log.warning("Recived Wrong Packet Data in Aquired Skill, unknown skill type:" + _skillType);
 			}
 		}
-		
-		if (Config.DEBUG)
-			_log.fine("Learned skill " + _id + " for " + requiredSp + " SP.");
-		
-		if (_skillType != 3 && _skillType != 2)
+	}
+	
+	/**
+	 * Perform a simple check for current player and skill.<br>
+	 * Used for skills that require items, not Sp.
+	 * @param player the skill learning player.
+	 * @param trainer the skills teaching Npc.
+	 * @param s the skill to be learn.
+	 */
+	private boolean checkPlayerSkill(L2PcInstance player, L2Npc trainer, L2SkillLearn s)
+	{
+		if ((s.getSkillId() == _id) && (s.getSkillLevel() == _level))
 		{
-			player.setSp(player.getSp() - requiredSp);
-			
-			StatusUpdate su = new StatusUpdate(player);
-			su.addAttribute(StatusUpdate.SP, player.getSp());
-			player.sendPacket(su);
+			//Hack check.
+			if (s.getGetLevel() > player.getLevel())
+			{
+				player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_DONT_MEET_SKILL_LEVEL_REQUIREMENTS));
+				Util.handleIllegalPlayerAction(player, "Player " + player.getName() + ", level " + player.getLevel() + " is requesting skill Id: " + _id + " level " + _level + " without having minimum required level, " + s.getGetLevel() + "!", 0);
+				return false;
+			}
 			
-			SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.LEARNED_SKILL_S1);
-			sm.addSkillName(skill);
-			player.sendPacket(sm);
+			int itemId = -1;
+			int itemCount = -1;
 			
-			player.sendPacket(new AcquireSkillDone());
+			if (!Config.DIVINE_SP_BOOK_NEEDED && (_id == L2Skill.SKILL_DIVINE_INSPIRATION))
+			{
+				return true;
+			}
 			
-			player.addSkill(skill, true);
-			player.sendSkillList();
+			if (s.getItemsIdCount() != null)
+			{
+				for (int[] itemIdCount : s.getItemsIdCount())
+				{
+					itemId = itemIdCount[0];
+					itemCount = itemIdCount[1];
+					
+					if (!player.destroyItemByItemId("Consume", itemId, itemCount, trainer, false))
+					{
+						//Player doesn't have required item.
+						player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MISSING_TO_LEARN_SKILL));
+						showSkillList(trainer, player);
+						return false;
+					}
+					else
+					{
+						final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
+						sm.addItemName(itemId);
+						sm.addItemNumber(itemCount);
+						player.sendPacket(sm);
+					}
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+	
+	/**
+	 * Takes the needed SP if the skill require it, add the skill to the player and makes proper updates.
+	 * @param player
+	 * @param trainer
+	 * @param levelUpSp
+	 * @param skill
+	 */
+	private void giveSkill(L2PcInstance player, L2Npc trainer, int levelUpSp, L2Skill skill)
+	{
+		//Remove Sp from player if it's needed.
+		if (levelUpSp > 0)
+		{
+			player.setSp(player.getSp() - levelUpSp);
 			
-			updateShortCuts(player);
-			showSkillList(trainer, player);
+			final StatusUpdate su = new StatusUpdate(player);
+			su.addAttribute(StatusUpdate.SP, player.getSp());
+			player.sendPacket(su);
+		}
+		
+		//Send message.
+		final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.LEARNED_SKILL_S1);
+		sm.addSkillName(skill);
+		player.sendPacket(sm);
+		
+		player.sendPacket(new AcquireSkillDone());
+		
+		player.addSkill(skill, true);
+		player.sendSkillList();
+		
+		updateShortCuts(player);
+		showSkillList(trainer, player);
+		
+		//If skill is expand type then sends packet:
+		if ((_id >= 1368) && (_id <= 1372))
+		{
+			player.sendPacket(new ExStorageMaxCount(player));
 		}
 	}
 	
+	/**
+	 * Updates the shortcut bars with the new acquired skill.
+	 * @param player
+	 */
 	private void updateShortCuts(L2PcInstance player)
 	{
-		// update all the shortcuts to this skill
+		//Update all the shortcuts for this skill
 		if (_level > 1)
 		{
-			L2ShortCut[] allShortCuts = player.getAllShortCuts();
+			final L2ShortCut[] allShortCuts = player.getAllShortCuts();
 			
 			for (L2ShortCut sc : allShortCuts)
 			{
-				if (sc.getId() == _id && sc.getType() == L2ShortCut.TYPE_SKILL)
+				if ((sc.getId() == _id) && (sc.getType() == L2ShortCut.TYPE_SKILL))
 				{
 					L2ShortCut newsc = new L2ShortCut(sc.getSlot(), sc.getPage(), sc.getType(), sc.getId(), _level, 1);
 					player.sendPacket(new ShortCutRegister(newsc));
@@ -518,33 +514,38 @@ public class RequestAcquireSkill extends L2GameClientPacket
 		}
 	}
 	
+	/**
+	 * Wrapper for returning the skill list to the player after it's done with current skill.
+	 * @param trainer the Npc which the {@code player} is interacting.
+	 * @param player the active character.
+	 */
 	private void showSkillList(L2Npc trainer, L2PcInstance player)
 	{
-		if (_skillType == 4)
+		if ((trainer instanceof L2TrainerHealersInstance) && (_skillType == SkillType.Transfer.ordinal()))
 		{
-			Quest[] qlst = trainer.getTemplate().getEventQuests(Quest.QuestEventType.ON_SKILL_LEARN);
-			qlst[0].notifyAcquireSkillList(trainer, player);
+			L2TrainerHealersInstance.showTransferSkillList(player);
 		}
 		else if (trainer instanceof L2FishermanInstance)
+		{
 			L2FishermanInstance.showFishSkillList(player);
-		else if (trainer instanceof L2TransformManagerInstance)
+		}
+		else if ((trainer instanceof L2TransformManagerInstance) && (_skillType == SkillType.ClassTransform.ordinal()))
+		{
 			L2TransformManagerInstance.showTransformSkillList(player);
+		}
+		else if ((trainer instanceof L2TransformManagerInstance) && (_skillType == SkillType.SubClass.ordinal()))
+		{
+			L2TransformManagerInstance.showSubClassSkillList(player);
+		}
 		else
-			L2NpcInstance.showSkillList(player, trainer, player.getSkillLearningClassId());
-		
-		// if skill is expand sendpacket :)
-		if (_id >= 1368 && _id <= 1372)
-			player.sendPacket(new ExStorageMaxCount(player));
+		{
+			L2NpcInstance.showSkillList(player, trainer, player.getClassId());
+		}
 	}
 	
-	/*
-	 * (non-Javadoc)
-	 * 
-	 * @see com.l2jserver.gameserver.clientpackets.ClientBasePacket#getType()
-	 */
 	@Override
 	public String getType()
 	{
-		return _C__6C_REQUESTAQUIRESKILL;
+		return _C__7C_REQUESTACQUIRESKILL;
 	}
 }

+ 203 - 139
L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestAcquireSkillInfo.java

@@ -17,32 +17,25 @@ package com.l2jserver.gameserver.network.clientpackets;
 import java.util.logging.Logger;
 
 import com.l2jserver.Config;
-import com.l2jserver.gameserver.datatables.SkillSpellbookTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree;
-import com.l2jserver.gameserver.datatables.SubPledgeSkillTree.SubUnitSkill;
-import com.l2jserver.gameserver.model.L2PledgeSkillLearn;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.model.L2Skill;
 import com.l2jserver.gameserver.model.L2SkillLearn;
 import com.l2jserver.gameserver.model.L2SquadTrainer;
-import com.l2jserver.gameserver.model.L2TransformSkillLearn;
 import com.l2jserver.gameserver.model.actor.L2Npc;
 import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2TransformManagerInstance;
-import com.l2jserver.gameserver.model.quest.Quest;
 import com.l2jserver.gameserver.network.serverpackets.AcquireSkillInfo;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
 
 /**
- * This class ...
- *
- * @version $Revision: 1.5.2.1.2.5 $ $Date: 2005/04/06 16:13:48 $
+ * @author Zoey76
  */
-public class RequestAcquireSkillInfo extends L2GameClientPacket
+public final class RequestAcquireSkillInfo extends L2GameClientPacket
 {
-	private static final String _C__6B_REQUESTAQUIRESKILLINFO = "[C] 6B RequestAcquireSkillInfo";
-	private static Logger _log = Logger.getLogger(RequestAcquireSkillInfo.class.getName());
+	private static final String _C__6B_REQUESTACQUIRESKILLINFO = "[C] 6B RequestAcquireSkillInfo";
+	private static final Logger _log = Logger.getLogger(RequestAcquireSkillInfo.class.getName());
 	
 	private int _id;
 	private int _level;
@@ -59,211 +52,282 @@ public class RequestAcquireSkillInfo extends L2GameClientPacket
 	@Override
 	protected void runImpl()
 	{
-		if (_id <= 0 || _level <= 0) // minimal sanity check
+		if ((_id <= 0) || (_level <= 0))
+		{
 			return;
+		}
 		
 		final L2PcInstance activeChar = getClient().getActiveChar();
 		
 		if (activeChar == null)
+		{
 			return;
+		}
 		
 		final L2Npc trainer = activeChar.getLastFolkNPC();
+		
 		if (!(trainer instanceof L2NpcInstance))
+		{
 			return;
+		}
 		
 		if (!trainer.canInteract(activeChar) && !activeChar.isGM())
+		{
 			return;
+		}
 		
 		final L2Skill skill = SkillTable.getInstance().getInfo(_id, _level);
 		
-		boolean canteach = false;
-		
 		if (skill == null)
 		{
-			if (Config.DEBUG)
-				_log.warning("skill id " + _id + " level " + _level + " is undefined. aquireSkillInfo failed.");
+			_log.warning(RequestAcquireSkillInfo.class.getSimpleName() + ": Skill Id: " + _id + " level: " + _level + " is undefined. " + RequestAcquireSkillInfo.class.getName() + " failed.");
 			return;
 		}
 		
-		switch (_skillType)
+		final SkillType skillType = SkillType.values()[_skillType];
+		
+		//Doesn't apply to all Skill Types
+		if (((skillType != SkillType.Transfer) && ((_level > 1) && (activeChar.getKnownSkill(_id) == null))) || ((activeChar.getKnownSkill(_id) != null) && (activeChar.getKnownSkill(_id).getLevel() != (_level - 1))))
 		{
-			case 0:
+			_log.warning(RequestAcquireSkillInfo.class.getSimpleName() + ": Player " + activeChar.getName() + " is requesting info for skill Id: " + _id + " level " + _level + " without knowing it's previous level!");
+		}
+		
+		switch (skillType)
+		{
+			case ClassTransform:
 			{
 				if (trainer instanceof L2TransformManagerInstance)
 				{
-					int itemId = 0;
-					L2TransformSkillLearn[] skillst = SkillTreeTable.getInstance().getAvailableTransformSkills(activeChar);
+					final L2SkillLearn s = SkillTreesData.getInstance().getTransformSkill(_id, _level);
 					
-					for (L2TransformSkillLearn s : skillst)
+					if (s != null)
 					{
-						if (s.getId() == _id && s.getLevel() == _level)
+						int itemId = -1;
+						int itemCount = -1;
+						
+						final AcquireSkillInfo asi = new AcquireSkillInfo(_id, _level, 0, SkillType.ClassTransform);
+						if (s.getItemsIdCount() != null)
 						{
-							canteach = true;
-							itemId = s.getItemId();
-							break;
+							for (int[] itemIdCount : s.getItemsIdCount())
+							{
+								itemId = itemIdCount[0];
+								itemCount = itemIdCount[1];
+								
+								if ((itemId > 0) && (itemCount > 0))
+								{
+									asi.addRequirement(99, itemId, itemCount, 50);
+								}
+							}
 						}
+						sendPacket(asi);
 					}
-					
-					if (!canteach)
-						return; // cheater
-						
-					int requiredSp = 0;
-					AcquireSkillInfo asi = new AcquireSkillInfo(skill.getId(), skill.getLevel(), requiredSp, 0);
-					
-					// all transformations require scrolls
-					asi.addRequirement(99, itemId, 1, 50);
-					sendPacket(asi);
 					return;
 				}
-				
-				if (!trainer.getTemplate().canTeach(activeChar.getSkillLearningClassId()))
-					return; // cheater
-					
-				L2SkillLearn[] skills = SkillTreeTable.getInstance().getAvailableSkills(activeChar, activeChar.getSkillLearningClassId());
-				
-				for (L2SkillLearn s : skills)
+				else if (trainer.getTemplate().canTeach(activeChar.getClassId()))
 				{
-					if (s.getId() == _id && s.getLevel() == _level)
+					final L2SkillLearn s = SkillTreesData.getInstance().getClassSkill(_id, _level, activeChar.getClassId());
+					if (s != null)
 					{
-						canteach = true;
-						break;
+						int itemId = -1;
+						int itemCount = -1;
+						final int levelUpSp = s.getLevelUpSp();
+						
+						final AcquireSkillInfo asi = new AcquireSkillInfo(_id, _level, levelUpSp, SkillType.ClassTransform);
+						
+						if (s.getItemsIdCount() != null)
+						{
+							for (int[] itemIdCount : s.getItemsIdCount())
+							{
+								if (!Config.DIVINE_SP_BOOK_NEEDED && (_id == L2Skill.SKILL_DIVINE_INSPIRATION))
+								{
+									continue;
+								}
+								
+								itemId = itemIdCount[0];
+								itemCount = itemIdCount[1];
+								
+								if ((itemId > 0) && (itemCount > 0))
+								{
+									asi.addRequirement(99, itemId, itemCount, 50);
+								}
+							}
+						}
+						sendPacket(asi);
 					}
 				}
-				
-				if (!canteach)
-					return; // cheater
-					
-				int requiredSp = SkillTreeTable.getInstance().getSkillCost(activeChar, skill);
-				AcquireSkillInfo asi = new AcquireSkillInfo(skill.getId(), skill.getLevel(), requiredSp, 0);
-				
-				int spbId = -1;
-				if (Config.DIVINE_SP_BOOK_NEEDED && skill.getId() == L2Skill.SKILL_DIVINE_INSPIRATION)
-					spbId = SkillSpellbookTable.getInstance().getBookForSkill(skill, _level);
-				else if (Config.SP_BOOK_NEEDED && skill.getLevel() == 1)
-					spbId = SkillSpellbookTable.getInstance().getBookForSkill(skill);
-				
-				if (spbId > -1)
-					asi.addRequirement(99, spbId, 1, 50);
-				
-				sendPacket(asi);
 				break;
 			}
-			case 2:
+			case Fishing:
 			{
-				int requiredRep = 0;
-				int itemId = 0;
-				int itemCount = 0;
-				L2PledgeSkillLearn[] skills = SkillTreeTable.getInstance().getAvailablePledgeSkills(activeChar);
-				
-				for (L2PledgeSkillLearn s : skills)
+				final L2SkillLearn s = SkillTreesData.getInstance().getFishingSkill(_id, _level);
+				if (s != null)
 				{
-					if (s.getId() == _id && s.getLevel() == _level)
+					int itemId = -1;
+					int itemCount = -1;
+					
+					final AcquireSkillInfo asi = new AcquireSkillInfo(_id, _level, 0, SkillType.Fishing);
+					for (int[] itemIdCount : s.getItemsIdCount())
 					{
-						canteach = true;
-						requiredRep = s.getRepCost();
-						itemId = s.getItemId();
-						itemCount = s.getItemCount();
-						break;
+						itemId = itemIdCount[0];
+						itemCount = itemIdCount[1];
+						
+						if ((itemId > 0) && (itemCount > 0))
+						{
+							asi.addRequirement(4, itemId, itemCount, 0);
+						}
 					}
+					sendPacket(asi);
+				}
+				break;
+			}
+			case Pledge:
+			{
+				if (!activeChar.isClanLeader())
+				{
+					return;
 				}
 				
-				if (!canteach)
-					return; // cheater
+				final L2SkillLearn s = SkillTreesData.getInstance().getPledgeSkill(_id, _level);
+				if (s != null)
+				{
+					final int requiredRep = s.getLevelUpSp();
+					int itemId = -1;
+					int itemCount = -1;
 					
-				AcquireSkillInfo asi = new AcquireSkillInfo(skill.getId(), skill.getLevel(), requiredRep, 2);
-				
-				if (Config.LIFE_CRYSTAL_NEEDED)
-					asi.addRequirement(1, itemId, itemCount, 0);
-				
-				sendPacket(asi);
+					final AcquireSkillInfo asi = new AcquireSkillInfo(_id, _level, requiredRep, SkillType.Pledge);
+					
+					if (Config.LIFE_CRYSTAL_NEEDED)
+					{
+						for (int[] itemIdCount : s.getItemsIdCount())
+						{
+							itemId = itemIdCount[0];
+							itemCount = itemIdCount[1];
+							
+							if ((itemId > 0) && (itemCount > 0))
+							{
+								asi.addRequirement(1, itemId, itemCount, 0);
+							}
+						}
+					}
+					sendPacket(asi);
+				}
 				break;
 			}
-			case 3:
+			case SubPledge:
 			{
+				if (!activeChar.isClanLeader())
+				{
+					return;
+				}
+				
 				if (trainer instanceof L2SquadTrainer)
 				{
-					SubUnitSkill sus = SubPledgeSkillTree.getInstance().getSkill(SkillTable.getSkillHashCode(skill));
-					AcquireSkillInfo asi = new AcquireSkillInfo(skill.getId(), skill.getLevel(), sus.getReputation(), 3);
-					asi.addRequirement(0, sus.getItemId(), sus.getCount(), 0);
-					sendPacket(asi);
+					final L2SkillLearn s = SkillTreesData.getInstance().getSubPledgeSkill(_id, _level);
+					if (s != null)
+					{
+						int itemId = -1;
+						int itemCount = -1;
+						
+						final AcquireSkillInfo asi = new AcquireSkillInfo(_id, _level, s.getLevelUpSp(), SkillType.SubPledge);
+						
+						for (int[] itemIdCount : s.getItemsIdCount())
+						{
+							itemId = itemIdCount[0];
+							itemCount = itemIdCount[1];
+							
+							if ((itemId > 0) && (itemCount > 0))
+							{
+								asi.addRequirement(0, itemId, itemCount, 0);
+							}
+						}
+						sendPacket(asi);
+					}
 				}
 				break;
 			}
-			case 4:
-			case 5:
+			case SubClass:
 			{
-				Quest[] qlst = trainer.getTemplate().getEventQuests(Quest.QuestEventType.ON_SKILL_LEARN);
-				if ((qlst != null) && qlst.length == 1)
+				final L2SkillLearn s = SkillTreesData.getInstance().getSubClassSkill(_id, _level);
+				
+				if (s != null)
 				{
-					if (!qlst[0].notifyAcquireSkillInfo(trainer, activeChar, skill))
+					int itemId = -1;
+					int itemCount = -1;
+					
+					final AcquireSkillInfo asi = new AcquireSkillInfo(_id, _level, 0, SkillType.SubClass);
+					
+					for (int[] itemIdCount : s.getItemsIdCount())
 					{
-						qlst[0].notifyAcquireSkillList(trainer, activeChar);
-						return;
+						itemId = itemIdCount[0];
+						itemCount = itemIdCount[1];
+						
+						if ((itemId > 0) && (itemCount > 0))
+						{
+							asi.addRequirement(99, itemId, itemCount, 50);
+						}
 					}
+					sendPacket(asi);
 				}
-				else
-					return;
 				break;
 			}
-			case 6:
+			case Collect:
 			{
-				int costid = 0;
-				int costcount = 0;
-				L2SkillLearn[] skillsc = SkillTreeTable.getInstance().getAvailableSpecialSkills(activeChar);
-				for (L2SkillLearn s : skillsc)
+				final L2SkillLearn s = SkillTreesData.getInstance().getCollectSkill(_id, _level);
+				if (s != null)
 				{
-					L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
+					int itemId = -1;
+					int itemCount = -1;
 					
-					if (sk == null || sk != skill)
-						continue;
+					final AcquireSkillInfo asi = new AcquireSkillInfo(_id, _level, 0, SkillType.Collect);
 					
-					canteach = true;
-					costid = s.getIdCost();
-					costcount = s.getCostCount();
+					for (int[] itemIdCount : s.getItemsIdCount())
+					{
+						itemId = itemIdCount[0];
+						itemCount = itemIdCount[1];
+						
+						if ((itemId > 0) && (itemCount > 0))
+						{
+							asi.addRequirement(6, itemId, itemCount, 0);
+						}
+					}
+					sendPacket(asi);
 				}
-				
-				AcquireSkillInfo asi = new AcquireSkillInfo(skill.getId(), skill.getLevel(), 0, 6);
-				asi.addRequirement(5, costid, costcount, 0);
-				sendPacket(asi);
 				break;
 			}
-			default: // Common Skills
+			case Transfer:
 			{
-				int costid = 0;
-				int costcount = 0;
-				int spcost = 0;
-				
-				L2SkillLearn[] skillsc = SkillTreeTable.getInstance().getAvailableSkills(activeChar);
-				
-				for (L2SkillLearn s : skillsc)
+				final L2SkillLearn s = SkillTreesData.getInstance().getTransferSkill(_id, _level, activeChar.getClassId());
+				if (s != null)
 				{
-					L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
+					int itemId = -1;
+					int itemCount = -1;
 					
-					if (sk == null || sk != skill)
-						continue;
+					final AcquireSkillInfo asi = new AcquireSkillInfo(_id, _level, 0, SkillType.Transfer);
 					
-					canteach = true;
-					costid = s.getIdCost();
-					costcount = s.getCostCount();
-					spcost = s.getSpCost();
+					for (int[] itemIdCount : s.getItemsIdCount())
+					{
+						itemId = itemIdCount[0];
+						itemCount = itemIdCount[1];
+						
+						if ((itemId > 0) && (itemCount > 0))
+						{
+							asi.addRequirement(4, itemId, itemCount, 0);
+						}
+					}
+					sendPacket(asi);
+				}
+				else
+				{
+					_log.warning(RequestAcquireSkillInfo.class.getSimpleName() + ": Null L2SkillLearn for id: " + _id + " and level " + _level + " in Transfer Skill Tree for skill learning class " + activeChar.getClassId() + "!");
 				}
-				
-				AcquireSkillInfo asi = new AcquireSkillInfo(skill.getId(), skill.getLevel(), spcost, 1);
-				asi.addRequirement(4, costid, costcount, 0);
-				sendPacket(asi);
 				break;
 			}
 		}
 	}
 	
-	/*
-	 * (non-Javadoc)
-	 *
-	 * @see com.l2jserver.gameserver.clientpackets.ClientBasePacket#getType()
-	 */
 	@Override
 	public String getType()
 	{
-		return _C__6B_REQUESTAQUIRESKILLINFO;
+		return _C__6B_REQUESTACQUIRESKILLINFO;
 	}
 }

+ 6 - 10
L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestSkillList.java

@@ -17,33 +17,29 @@ package com.l2jserver.gameserver.network.clientpackets;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 
 /**
- * This class ...
- *
- * @version $Revision: 1.3.4.2 $ $Date: 2005/03/27 15:29:30 $
+ * @version 1.4
  */
 public final class RequestSkillList extends L2GameClientPacket
 {
 	private static final String _C__3F_REQUESTSKILLLIST = "[C] 3F RequestSkillList";
-	//private static Logger _log = Logger.getLogger(RequestSkillList.class.getName());
 	
 	@Override
 	protected void readImpl()
 	{
-		// this is just a trigger packet. it has no content
+		//Trigger skill.
 	}
 	
 	@Override
 	protected void runImpl()
 	{
-		L2PcInstance cha = getClient().getActiveChar();
+		final L2PcInstance cha = getClient().getActiveChar();
 		
 		if (cha != null)
+		{
 			cha.sendSkillList();
+		}
 	}
 	
-	/* (non-Javadoc)
-	 * @see com.l2jserver.gameserver.clientpackets.ClientBasePacket#getType()
-	 */
 	@Override
 	public String getType()
 	{
@@ -55,4 +51,4 @@ public final class RequestSkillList extends L2GameClientPacket
 	{
 		return false;
 	}
-}
+}

+ 4 - 7
L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/AcquireSkillDone.java

@@ -15,15 +15,15 @@
 package com.l2jserver.gameserver.network.serverpackets;
 
 /**
- *
  * @author  Kerberos
  */
-
 public class AcquireSkillDone extends L2GameServerPacket
 {
+	private static final String _S__94_ACQUIRESKILLDONE = "[S] 94 AcquireSkillDone";
+	
 	public AcquireSkillDone()
 	{
-		
+		//
 	}
 	
 	@Override
@@ -32,12 +32,9 @@ public class AcquireSkillDone extends L2GameServerPacket
 		writeC(0x94);
 	}
 	
-	/* (non-Javadoc)
-	 * @see com.l2jserver.gameserver.serverpackets.ServerBasePacket#getType()
-	 */
 	@Override
 	public String getType()
 	{
-		return "[S] 94 AcquireSkillDone";
+		return _S__94_ACQUIRESKILLDONE;
 	}
 }

+ 16 - 17
L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/AcquireSkillInfo.java

@@ -14,30 +14,33 @@
  */
 package com.l2jserver.gameserver.network.serverpackets;
 
-import java.util.List;
+import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType;
 
 import javolution.util.FastList;
 
 /**
+ * Sample:
  * <code>
- * sample
- *
  * a4
  * 4d000000 01000000 98030000 			Attack Aura, level 1, sp cost
  * 01000000 							number of requirements
  * 05000000 47040000 0100000 000000000	   1 x spellbook advanced ATTACK                                                 .
  * </code>
- *
+ * <br>
  * format   dddd d (ddQd)
  *
- * @version $Revision: 1.3.2.1.2.4 $ $Date: 2005/03/27 15:29:39 $
+ * @version  1.5
  */
 public class AcquireSkillInfo extends L2GameServerPacket
 {
-	private static final String _S__A4_AQUIRESKILLINFO = "[S] 91 AcquireSkillInfo";
-	private List<Req> _reqs;
-	private int _id, _level, _spCost, _mode;
+	private static final String _S__91_ACQUIRESKILLINFO = "[S] 91 AcquireSkillInfo";
+	private FastList<Req> _reqs;
+	private int _id, _level, _spCost;
+	private SkillType _type;
 	
+	/**
+	 * Private class containing learning skill requisites.
+	 */
 	private static class Req
 	{
 		public int itemId;
@@ -54,13 +57,13 @@ public class AcquireSkillInfo extends L2GameServerPacket
 		}
 	}
 	
-	public AcquireSkillInfo(int id, int level, int spCost, int mode)
+	public AcquireSkillInfo(int id, int level, int spCost, SkillType type)
 	{
 		_reqs = new FastList<Req>();
 		_id = id;
 		_level = level;
 		_spCost = spCost;
-		_mode = mode;
+		_type = type;
 	}
 	
 	public void addRequirement(int type, int id, int count, int unk)
@@ -75,7 +78,7 @@ public class AcquireSkillInfo extends L2GameServerPacket
 		writeD(_id);
 		writeD(_level);
 		writeD(_spCost);
-		writeD(_mode); //c4
+		writeD(_type.ordinal());
 		
 		writeD(_reqs.size());
 		
@@ -88,13 +91,9 @@ public class AcquireSkillInfo extends L2GameServerPacket
 		}
 	}
 	
-	/* (non-Javadoc)
-	 * @see com.l2jserver.gameserver.serverpackets.ServerBasePacket#getType()
-	 */
 	@Override
 	public String getType()
 	{
-		return _S__A4_AQUIRESKILLINFO;
+		return _S__91_ACQUIRESKILLINFO;
 	}
-	
-}
+}

+ 31 - 41
L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/AcquireSkillList.java

@@ -14,51 +14,37 @@
  */
 package com.l2jserver.gameserver.network.serverpackets;
 
-import java.util.List;
-
 import javolution.util.FastList;
 
 /**
- * sample
- *
- * a3
- * 05000000
- * 03000000 03000000 06000000 3c000000 00000000 	power strike
- * 10000000 02000000 06000000 3c000000 00000000 	mortal blow
- * 38000000 04000000 06000000 36010000 00000000 	power shot
- * 4d000000 01000000 01000000 98030000 01000000 	ATTACK aura  920sp
- * 8e000000 03000000 03000000 cc010000 00000000     Armor Mastery
- *
- * format   d (ddddd)
- * skillid, level, maxlevel?,
- *
- * C4 format changes:
- * 0000: [8a] [00 00 00 00] [35 00 00 00] 92 00 00 00 01 00 00    .....5..........
- *            ^^^^^^^^^^^^^
- * 0010: 00 2d 00 00 00 04 01 00 00 00 00 00 00 a4 00 00    .-..............
- * 0020: 00 01 00 00 00 03 00 00 00 e4 0c 00 00 00 00 00    ................
- * 0030: 00 d4 00 00 00 01 00 00 00 06 00 00 00 08 52 00    ..............R.
- * @version $Revision: 1.3.2.1.2.5 $ $Date: 2005/03/27 15:29:57 $
+ * TODO: Gather samples from newer chronicles.
+ * @version 1.4
  */
 public final class AcquireSkillList extends L2GameServerPacket
 {
-	//private static Logger _log = Logger.getLogger(AquireSkillList.class.getName());
+	private static final String _S__90_AQUIRESKILLLIST = "[S] 90 AquireSkillList";
+	
+	/**
+	 * Enumerate containing learning skill types.
+	 */
 	public enum SkillType
 	{
-		Usual, // 0
-		Fishing, // 1
-		Clan, // 2
-		SubUnit, //3
-		unk4,
-		unk5,
-		Special // 6
+		//TODO: Transform is 0 or 4?
+		ClassTransform, //0
+		Fishing, //1
+		Pledge, //2
+		SubPledge, //3
+		Transfer, //4
+		SubClass, //5
+		Collect, //6
 	}
 	
-	private static final String _S__A3_AQUIRESKILLLIST = "[S] 90 AquireSkillList";
-	
-	private List<Skill> _skills;
+	private FastList<Skill> _skills;
 	private SkillType _skillType;
 	
+	/**
+	 * Private class containing learning skill information.
+	 */
 	private static class Skill
 	{
 		public int id;
@@ -85,13 +71,18 @@ public final class AcquireSkillList extends L2GameServerPacket
 	public void addSkill(int id, int nextLevel, int maxLevel, int spCost, int requirements)
 	{
 		if (_skills == null)
+		{
 			_skills = new FastList<Skill>();
+		}
 		_skills.add(new Skill(id, nextLevel, maxLevel, spCost, requirements));
 	}
 	
 	@Override
-	protected final void writeImpl()
+	protected void writeImpl()
 	{
+		if (_skills == null)
+			return;
+		
 		writeC(0x90);
 		writeD(_skillType.ordinal());
 		writeD(_skills.size());
@@ -103,17 +94,16 @@ public final class AcquireSkillList extends L2GameServerPacket
 			writeD(temp.maxLevel);
 			writeD(temp.spCost);
 			writeD(temp.requirements);
-			if (_skillType == SkillType.SubUnit)
-				writeD(0); //?
+			if (_skillType == SkillType.SubPledge)
+			{
+				writeD(0); //TODO: ?
+			}
 		}
 	}
 	
-	/* (non-Javadoc)
-	 * @see com.l2jserver.gameserver.serverpackets.ServerBasePacket#getType()
-	 */
 	@Override
 	public String getType()
 	{
-		return _S__A3_AQUIRESKILLLIST;
+		return _S__90_AQUIRESKILLLIST;
 	}
-}
+}

+ 17 - 23
L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/PledgeSkillList.java

@@ -19,27 +19,27 @@ import com.l2jserver.gameserver.model.L2Skill;
 
 /**
  * Format: (ch) dd[dd][ddd]
- *
  * @author  -Wooden-
  */
 public class PledgeSkillList extends L2GameServerPacket
 {
-	private static final String _S__FE_39_PLEDGESKILLLIST = "[S] FE:3a PledgeSkillList";
+	private static final String _S__FE_39_PLEDGESKILLLIST = "[S] FE:3A PledgeSkillList";
 	private L2Skill[] _skills;
 	private SubPledgeSkill[] _subSkills;
 	
 	public static class SubPledgeSkill
 	{
+		int _subType;
+		int _skillId;
+		int _skillLvl;
+		
 		public SubPledgeSkill(int subType, int skillId, int skillLvl)
 		{
 			super();
-			this.subType = subType;
-			this.skillId = skillId;
-			this.skillLvl = skillLvl;
+			_subType = subType;
+			_skillId = skillId;
+			_skillLvl = skillLvl;
 		}
-		int subType;
-		int skillId;
-		int skillLvl;
 	}
 	
 	public PledgeSkillList(L2Clan clan)
@@ -48,35 +48,29 @@ public class PledgeSkillList extends L2GameServerPacket
 		_subSkills = clan.getAllSubSkills();
 	}
 	
-	/**
-	 * @see com.l2jserver.util.network.BaseSendablePacket.ServerBasePacket#writeImpl()
-	 */
 	@Override
 	protected void writeImpl()
 	{
-		writeC(0xfe);
-		writeH(0x3a);
+		writeC(0xfE);
+		writeH(0x3A);
 		writeD(_skills.length);
-		writeD(_subSkills.length); // squad skill lenght
-		for(L2Skill sk : _skills)
+		writeD(_subSkills.length); //Squad skill length
+		for (L2Skill sk : _skills)
 		{
 			writeD(sk.getId());
 			writeD(sk.getLevel());
 		}
-		for(SubPledgeSkill sk : _subSkills)
+		for (SubPledgeSkill sk : _subSkills)
 		{
-			writeD(sk.subType); // clan Sub-unit types
-			writeD(sk.skillId);
-			writeD(sk.skillLvl);
+			writeD(sk._subType); //Clan Sub-unit types
+			writeD(sk._skillId);
+			writeD(sk._skillLvl);
 		}
 	}
 	
-	/**
-	 * @see com.l2jserver.gameserver.BasePacket#getType()
-	 */
 	@Override
 	public String getType()
 	{
 		return _S__FE_39_PLEDGESKILLLIST;
 	}
-}
+}

+ 2 - 2
L2J_Server_BETA/java/com/l2jserver/gameserver/script/EngineInterface.java

@@ -25,7 +25,7 @@ import com.l2jserver.gameserver.datatables.LevelUpData;
 import com.l2jserver.gameserver.datatables.MapRegionTable;
 import com.l2jserver.gameserver.datatables.NpcTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.datatables.SkillTreeTable;
+import com.l2jserver.gameserver.datatables.SkillTreesData;
 import com.l2jserver.gameserver.datatables.SpawnTable;
 import com.l2jserver.gameserver.datatables.TeleportLocationTable;
 import com.l2jserver.gameserver.idfactory.IdFactory;
@@ -46,7 +46,7 @@ public interface EngineInterface
 	
 	public RecipeController recipeController = RecipeController.getInstance();
 	
-	public SkillTreeTable skillTreeTable = SkillTreeTable.getInstance();
+	public SkillTreesData skillTreeTable = SkillTreesData.getInstance();
 	public CharTemplateTable charTemplates = CharTemplateTable.getInstance();
 	public ClanTable clanTable = ClanTable.getInstance();
 	

+ 2 - 6
L2J_Server_BETA/java/com/l2jserver/gameserver/skills/conditions/ConditionPlayerGrade.java

@@ -19,13 +19,10 @@ import java.util.logging.Logger;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.skills.Env;
 
-
 /**
  * The Class ConditionPlayerGrade.
- *
  * @author Gigiikun
  */
-
 public final class ConditionPlayerGrade extends Condition
 {
 	protected static final Logger _log = Logger.getLogger(ConditionPlayerGrade.class.getName());
@@ -43,7 +40,6 @@ public final class ConditionPlayerGrade extends Condition
 	
 	/**
 	 * Instantiates a new condition player grade.
-	 *
 	 * @param value the value
 	 */
 	public ConditionPlayerGrade(int value)
@@ -51,7 +47,7 @@ public final class ConditionPlayerGrade extends Condition
 		_value = value;
 	}
 	
-	/* (non-Javadoc)
+	/**
 	 * @see com.l2jserver.gameserver.skills.conditions.Condition#testImpl(com.l2jserver.gameserver.skills.Env)
 	 */
 	@Override
@@ -59,7 +55,7 @@ public final class ConditionPlayerGrade extends Condition
 	{
 		if (env.player instanceof L2PcInstance)
 		{
-			byte expIndex = (byte)((L2PcInstance)env.player).getExpertiseIndex();
+			final byte expIndex = (byte) ((L2PcInstance) env.player).getExpertiseLevel();
 			
 			return _value == expIndex;
 		}

+ 1 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/templates/skills/L2EffectType.java

@@ -57,6 +57,7 @@ public enum L2EffectType
 	PETRIFICATION,
 	BLUFF,
 	CHARM_OF_LUCK,
+	LUCKY,
 	INVINCIBLE,
 	TRANSFORMATION,
 	DISARM,

+ 0 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/templates/skills/L2SkillType.java

@@ -169,7 +169,6 @@ public enum L2SkillType
 	CHARGEDAM(L2SkillChargeDmg.class),
 	MHOT,
 	DETECT_WEAKNESS,
-	LUCK,
 	RECALL(L2SkillTeleport.class),
 	TELEPORT(L2SkillTeleport.class),
 	SUMMON_FRIEND,

+ 9 - 5
L2J_Server_BETA/java/config/Character.properties

@@ -85,9 +85,14 @@ EnableModifySkillReuse = False
 # Format: skillid,newDelayTime;skillid,newDelayTime2 (See skillDuration for examples)
 SkillReuseList = 
 
+# If it's true all class skills will be delivered upon level up and login.
 # Default: False
 AutoLearnSkills = False
 
+# If it's true skills from forgotten scrolls will be delivered upon level up and login, require AutoLearnSkills.
+# Default: False
+AutoLearnForgottenScrollSkills = False
+
 # Default: False
 AutoLootHerbs = False
 
@@ -183,10 +188,6 @@ AlternateClassMaster = False
 # Default: True
 LifeCrystalNeeded = True
 
-# Require spell book needed to learn skills.
-# Default: False
-SpBookNeeded = False
-
 # Require book needed to enchant skills.
 # Default: True
 EnchantSkillSpBookNeeded = True
@@ -211,6 +212,9 @@ AltSubClassWithoutQuests = False
 # Default: False
 AltSubclassEverywhere = False
 
+# Allow player to learn transformations without quest.
+# Default: False
+AltTransfomarionWithoutQuest = False
 
 # ---------------------------------------------------------------------------
 # Summons configuration
@@ -716,4 +720,4 @@ StoreCharUiSettings = False
 # Character name restriction
 # Disallow characters to have a name which contains the words.
 # Split them with ",". Example: announcements,announce...
-ForbiddenNames = annou,ammou,amnou,anmou,anou,amou
+ForbiddenNames = annou,ammou,amnou,anmou,anou,amou